@chenguangyao/devflow-kit 0.1.43 → 0.1.44

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,3 @@
1
+ 'use strict';
2
+
3
+ module.exports = require('../src/client/workflow-adapter-client.js');
@@ -219,29 +219,110 @@ devflow flow picker --slug=<slug> --json
219
219
  - `quality`:专项检查,例如 frontend-quality / security-hardening / dependency-upgrade / ci-fix。
220
220
  - `delivery`:质量闸与交付,例如 review / verify / deliver / archive。
221
221
 
222
- 每个 item 会标记 `selected`、`recommended`、`locked`、`required`、`protected`、`installed`、`source` 和可执行的 `command`。系统推荐 recipe 中的 step 会自动 `selected=true`;open risk signal 对应但尚未选中的专项 skill 会显示 `recommended=true` 和原因,例如 `open frontend_change risk signal`。`review`、`verify`、`deliver` 这类 protected gate 会显示 `locked=true`,UI 可以渲染为不可取消。
222
+ 每个 item 会标记 `selected`、`recommended`、`locked`、`required`、`protected`、`installed`、`source`、`after`、`before` 和可执行的 `command`。系统推荐 recipe 中的 step 会自动 `selected=true`;open risk signal 对应但尚未选中的专项 skill 会显示 `recommended=true` 和原因,例如 `open frontend_change risk signal`。`review`、`verify`、`deliver` 这类 protected gate 会显示 `locked=true`,UI 可以渲染为不可取消。候选专项 skill 会带上 `after` / `before` placement,UI 直接提交修改后的 picker surface 时不需要自己推断插入位置。
223
223
 
224
- 这份 picker 协议的 schema 真源是 `schemas/workflow-picker.schema.json`。UI 不应依赖 CLI 人类输出解析,而应使用 `flow picker --json` 中的 `groups[].items[]` 渲染线性选项卡。典型 picker 输出样例固定在 `test/fixtures/workflow-surfaces/picker-*.json`,覆盖默认推荐和 risk signal 推荐专项 skill 两种场景。
224
+ 这份 picker 协议的 schema 真源是 `schemas/workflow-picker.schema.json`。`apply-selection` 的输入 schema 真源是 `schemas/workflow-selection.schema.json`,它同时支持最小 `{ "items": [...] }` 和前端修改后的完整 `workflow_picker_surface`。UI 不应依赖 CLI 人类输出解析,而应使用 `flow picker --json` 中的 `groups[].items[]` 渲染线性选项卡。典型 picker 输出样例固定在 `test/fixtures/workflow-surfaces/picker-*.json`,覆盖默认推荐和 risk signal 推荐专项 skill 两种场景。
225
225
 
226
- 静态 UI 原型固定在 `docs/workflow-ui-prototype.html`,由 `npm run prototype:workflow` 从 `test/fixtures/workflow-surfaces/*.json` 生成。它展示线性 skill 选项卡、推荐勾选态、locked gate workflow 确认卡,不需要 dev server,直接打开 HTML 即可检查交互形态。
226
+ 静态 UI 原型固定在 `docs/workflow-ui-prototype.html`,由 `npm run prototype:workflow` 从 `test/fixtures/workflow-surfaces/*.json` 生成。它展示线性 skill 选项卡、推荐勾选态、locked gate、`workflow_selection_result_surface` 成功应用面板、workflow 确认卡和风险确认卡,不需要 dev server,直接打开 HTML 即可检查交互形态。
227
227
 
228
- 如果要用真实 change state 生成实时原型,可以运行 `npm run prototype:workflow -- --slug=<slug> --cwd=<repo>`。脚本会调用 `devflow flow picker --slug=<slug> --json` 和 `devflow flow card --slug=<slug> --json`,默认写到系统临时目录的 `devflow-workflow-ui-<slug>.html`,避免覆盖仓库内的静态 fixture 原型;需要指定位置时加 `--out=<file>`。
228
+ 如果要用真实 change state 生成实时原型,可以运行 `npm run prototype:workflow -- --slug=<slug> --cwd=<repo>`。脚本会调用 `devflow flow picker --slug=<slug> --json` 和 `devflow flow card --slug=<slug> --json`,默认写到系统临时目录的 `devflow-workflow-ui-<slug>.html`,避免覆盖仓库内的静态 fixture 原型;需要指定位置时加 `--out=<file>`。如果要预览用户勾选后的链路,可以追加 `--selection='{"items":[...]}'` 或 `--selection-file=<file>`,脚本会先调用 `devflow flow apply-selection --slug=<slug> --selection=<json> --json`,或从 picker surface 的 `actions.applySelectionFile` 读取文件版 action,把 `<file>` 替换为真实选择文件路径后执行,再渲染 `workflow_selection_result_surface` 或风险确认卡。
229
+
230
+ 真实本地宿主可以直接运行:
231
+
232
+ ```bash
233
+ devflow flow ui --slug=<slug> --port=8787
234
+ ```
235
+
236
+ 它会启动一个零依赖 HTTP UI,默认监听 `127.0.0.1`,浏览器页面通过 `/api/status`、`/api/picker`、`/api/adapter/catalog`、`/api/adapter?host=codex`、`/api/diff`、`/api/explain`、`/api/apply-selection`、`/api/card`、`/api/confirm` 和 `/api/checkpoint/resolve` 调用现有 CLI JSON surface。这个 host 不读取或改写 devflow 内部文件格式,所有状态变化仍通过 `devflow status --json`、`flow picker --json`、`flow adapter --json`、`flow apply-selection --json`、`flow confirm` 和 `checkpoint resolve` 完成;`diff` / `explain` 只读展示相对默认 recipe 的变化、风险信号、verify 要求和 override 来源。页面会把 `status --json` 的 `primaryPanel` / `availableActions` 渲染成主操作区:`confirm-workflow` 会调用 `/api/confirm`,`checkpoint-decision` 会调用 `/api/checkpoint/resolve`,`open-surface` 只打开已有 picker/card/checkpoint surface;其他命令只展示,不自动执行任意 shell。`apply-selection` 返回 `workflow_policy_confirmation_surface` 时,页面会直接渲染“接受风险 / 保留当前流程”确认卡;接受风险会调用 `/api/checkpoint/resolve`,然后刷新当前 workflow。脚本集成可以使用 `devflow flow ui --slug=<slug> --port=0 --json --once` 获取一次性的 URL / endpoint surface,用于校验宿主命令是否可启动;这个 surface 会带 `schemas.adapter`、`schemas.adapterCatalog`、`schemas.selection`、`schemas.error` 和 `schemas.commandResult`,并且 `actions.adapter` 指向 `/api/adapter`,`actions.adapterCatalog` 指向 `/api/adapter/catalog`。Codex/Cursor 扩展可以加 `?host=codex` 或 `?host=cursor` 获取对应聊天卡 surface;其他宿主可以先读 `/api/adapter/catalog` 发现支持的 profile/renderMode,再用 `/api/adapter?host=windsurf&profile=cursor&render-mode=webview_or_browser` 复用 WebView 能力;也可以加 `/api/adapter?host=<host>&validate=1` 让本地 host 在转发 `flow adapter` 时启用契约校验。
237
+
238
+ 宿主 adapter 可以用下面的命令获取机器可读接入说明:
239
+
240
+ ```bash
241
+ devflow flow adapter-catalog --json
242
+ devflow flow adapter-catalog --json --validate-contract
243
+ devflow flow adapter --slug=<slug> --host=codex --json
244
+ devflow flow adapter --slug=<slug> --host=cursor --json
245
+ devflow flow adapter --slug=<slug> --host=cli --json
246
+ devflow flow adapter --slug=<slug> --host=windsurf --profile=cursor --json
247
+ devflow flow adapter --slug=<slug> --host=jetbrains --render-mode=browser --json
248
+ devflow flow adapter --slug=<slug> --host=codex --json --validate-contract
249
+ ```
250
+
251
+ `codex` adapter 的 `renderMode` 是 `in_app_browser`:Codex App 聊天侧可以直接展示 `chatCard.markdown`,交互卡片在 in-app browser 中打开 `flow ui` URL。`cursor` adapter 的 `renderMode` 是 `webview_or_browser`:Cursor 扩展可以先在对话框展示同一份 `chatCard.markdown`,再把本地 URL 嵌入 WebView;没有扩展时保持 `devflow flow ui` 进程运行,在系统浏览器打开 URL。`cli` adapter 的 `renderMode` 是 `terminal_markdown`:纯终端、普通聊天窗口或不支持 WebView 的宿主可以只打印 `chatCard.markdown`,再把编辑后的 `workflow_chat_selection` POST 到 `chatSelectionApi`。CLI 非 JSON 输出也会打印这张 Markdown 卡片,方便纯终端或普通聊天窗口复制展示。
252
+
253
+ 其他工具不需要进入 devflow 内置白名单:`host` 只是宿主名称,允许 `windsurf`、`jetbrains`、`zed` 等安全标识;`profile` 才是能力模板。未指定 profile 时,自定义 host 默认使用 `browser` profile;如果宿主支持 WebView,可用 `--profile=cursor` 复用 WebView 能力;如果宿主只能展示终端/Markdown,可用 `--profile=cli` 或 `--render-mode=terminal_markdown`。`renderMode` 可以显式覆盖 profile 的默认值,但状态变更仍只能走 adapter surface 暴露的受控 action。`devflow flow adapter-catalog --json` 会返回 `workflow_adapter_catalog`,列出 `profiles`、`renderModes`、`hostAliases`、示例命令和 safety 模型;第三方工具应该优先用这个 catalog 做运行时发现,而不是硬编码 devflow 当前支持列表。加 `--validate-contract` 或请求 `/api/adapter/catalog?validate=1` 会用 `schemas/workflow-adapter-catalog.schema.json` 做运行时契约校验。
254
+
255
+ 如果当前 change 已经有 workflow draft,`chatCard.markdown` 会内联一份线性 checkbox 选择卡:按核心流程、专项检查、质量闸与交付分组列出所有 step;推荐项默认勾选,`review` / `verify` / `deliver` 等 locked gate 会标记为锁定。宿主如果支持原生交互,可以读取 `chatCard.selection.groups[].items[]` 渲染自己的 checkbox UI;用户提交后,可以把编辑后的 `workflow_chat_selection` 原样传给 `chatSelectionApi`(本地 UI host 下是 `/api/adapter/apply-selection`),或等价的 `applySelectionApi` / `devflow flow apply-selection --json`,它已经被 `workflow_selection.schema.json` 接受。聊天层不需要也不应该自己判断风险,`apply-selection` 会在取消 locked gate、降低验证覆盖或关闭风险相关检查时返回 workflow policy confirmation card。
256
+
257
+ 本地 UI host 会在 `/api/apply-selection` 和 `/api/adapter/apply-selection` 边界先做轻量 selection 校验:只接受最小 `{ "items": [...] }`、`workflow_picker_surface` 或 `workflow_chat_selection`,并要求每个 item 至少包含非空 `step` 和布尔 `selected`。结构错误会直接返回 `workflow_ui_error`,`code: "invalid-workflow-selection"`,不会继续调用 CLI;无效 JSON body 会返回 `code: "invalid-json-body"`。策略风险仍交给 `devflow flow apply-selection` 的 policy engine 判断。
258
+
259
+ 所有本地 UI host 的 JSON endpoint 都会优先透传 CLI 返回的合法 JSON surface;如果 CLI 命令失败但没有 JSON 输出,会返回 `workflow_ui_error`,`code: "devflow-command-failed"`,并带上 `command`、`exitCode`、`stdout`、`stderr`。如果 CLI 退出成功但没有返回 JSON,会返回 `code: "devflow-json-missing"`。宿主可以只根据 `type` 和 `code` 渲染错误卡,不需要解析人类文本。
260
+
261
+ 这份错误 surface 的 schema 真源是 `schemas/workflow-ui-error.schema.json`。每个 `workflow_ui_error` 都会带 `schema: "https://devflow.dev/schemas/workflow-ui-error.schema.json"`,宿主可以用它校验 `code`、`message`、`details` 和可选命令输出字段,再决定展示内联错误、确认卡错误态,或退回纯文本 fallback。
262
+
263
+ 两者都使用同一套 `/api/*` endpoint,不需要为不同 IDE 复制业务逻辑。adapter surface 会带上 `protocolVersion: workflow-adapter-surface/v1`、`features`、`host`、`profile`、`renderMode` 和 `capabilities`:宿主先看协议版本,再按 `features` 以及 `supportsChatCard`、`supportsChatSelection`、`supportsEmbeddedUi`、`supportsApplySelection`、`supportsCheckpointResolve`、`supportsConfirm` 等能力决定是渲染原生 checkbox、嵌入本地 UI,还是只展示 Markdown fallback。当前 feature 包括 `catalog-discovery`、`chat-card`、`chat-selection`、`embedded-ui`、`read-only-diff`、`read-only-explain`、`checkpoint-resolve` 和 `controlled-confirm`;后续新增能力会优先加 feature 标识,而不是立刻更换协议版本。`capabilities.errorSchema` 和 `capabilities.commandResultSchema` 会分别指向 `workflow_ui_error` 与 `workflow_ui_command_result` 的 schema,方便宿主统一处理 `/api/*` 成功命令结果和错误卡。adapter surface 的 `actions` 会显式列出 `statusApi`、`pickerApi`、`diffApi`、`explainApi`、`applySelectionApi`、`chatSelectionApi`、`cardApi`、`checkpointResolveApi` 和 `confirmApi`;其中 `diffApi` / `explainApi` 是只读说明,`applySelectionApi` / `chatSelectionApi` / `checkpointResolveApi` / `confirmApi` 是受控状态变更入口。`chatCard` 只承载可渲染的展示内容,不直接修改 workflow;Codex/Cursor 或其他宿主如果要做原生按钮,应该把按钮映射到这些受控 action,而不是在聊天层执行任意 shell。
264
+
265
+ 这份宿主接入协议的 schema 真源是 `schemas/workflow-adapter-surface.schema.json`。插件侧可以用它校验 `workflow_adapter_surface`、`workflow_chat_card` 和 `workflow_chat_selection` 三层结构;`capabilities.schema` / `capabilities.selectionSchema` 会显式指向 adapter surface 和用户选择输入的 schema。用户编辑后的 `workflow_chat_selection` 则继续由 `schemas/workflow-selection.schema.json` 约束并提交给 `chatSelectionApi`。
266
+
267
+ CLI 侧也提供轻量运行时校验:加 `--validate-contract` 时,`flow adapter` 会在输出前用内置 schema 子集校验 adapter surface;成功时仍只输出原 JSON,失败时阻断并报告具体字段路径,例如 `capabilities.supportsConfirm`。
268
+
269
+ adapter surface 还有一组 snapshot fixture:`test/fixtures/workflow-adapter-surfaces/{codex,cursor,cli,browser}.json`。它们固定了 `protocolVersion`、`renderMode`、`capabilities`、`actions`、`chatCard.selection` 和 host-specific instructions;修改 adapter 协议或宿主行为时,应同步更新这些 fixture,并确认是有意变更。
270
+
271
+ 为了让宿主接入更具体,仓库提供了可复用 SDK 和最小 client 示例。npm 包的稳定入口是 `@chenguangyao/devflow-kit/client/workflow-adapter`,仓库内源码入口是 `src/client/workflow-adapter-client.js`;它提供 `ensureCompatibleCatalog`、`recommendAdapterProfile`、`ensureCompatibleSurface`、`buildSelectionPayload`、`submitSelection`、`renderCatalogExample`、`renderHostExample` 和 `renderSurfaceMarkdown`。脚本 `scripts/workflow-adapter-client-example.js` 只是这个 SDK 的 CLI 包装。
272
+
273
+ ```bash
274
+ devflow flow adapter-catalog --json --validate-contract > /tmp/devflow-adapter-catalog.json
275
+ node scripts/workflow-adapter-client-example.js --catalog-file=/tmp/devflow-adapter-catalog.json --host=windsurf
276
+ devflow flow adapter --slug=<slug> --host=codex --json --validate-contract > /tmp/devflow-adapter.json
277
+ node scripts/workflow-adapter-client-example.js --surface-file=/tmp/devflow-adapter.json --host=codex
278
+ node scripts/workflow-adapter-client-example.js --surface-file=/tmp/devflow-adapter.json --host=cursor
279
+ node scripts/workflow-adapter-client-example.js --surface-file=/tmp/devflow-adapter.json --host=cli
280
+ node scripts/workflow-adapter-client-example.js --surface-file=/tmp/devflow-adapter.json --dry-run --steps=requirement,design,review,verify,deliver
281
+ node scripts/workflow-adapter-client-example.js --surface-file=/tmp/devflow-adapter.json --submit --steps=requirement,design,review,verify,deliver
282
+ ```
283
+
284
+ 这个示例只做宿主侧该做的事:读取 `workflow_adapter_catalog` 推荐 host 对应的 `profile` / `renderMode` 和 adapter 命令;检查 adapter surface 的 `protocolVersion` 和 `capabilities`;展示 `chatCard.markdown`;基于 `chatCard.selection` 生成 `workflow_chat_selection` payload,并把提交目标固定到 `actions.chatSelectionApi`。默认只打印说明;`--dry-run` 只输出将要 POST 的 URL 和 payload,不发网络请求;`--submit` 才会 POST 到本地 UI host。它不会直接改 `state.json`,也不会绕过 policy engine;真正的状态变更仍由 `/api/adapter/apply-selection` / `devflow flow apply-selection` 完成。提交失败时,示例会把 `workflow_ui_error.code` 和 `message` 放进错误信息,同时保留完整 `response`,方便 Codex/Cursor 扩展渲染错误卡。
285
+
286
+ 宿主模板放在 `examples/adapter-hosts/`:`terminal-host.js` 展示纯终端/Markdown 接法,`webview-host.html` 展示 WebView 如何先读 catalog 再嵌入本地 UI,`generic-agent-host.md` 给未知 IDE/Agent 工具一份接入检查表。三个模板都遵守同一条边界:不执行卡片里的任意 shell,所有 mutation 都通过 devflow-controlled action。
287
+
288
+ adapter 端到端 smoke 固定在 `test/workflow-ui-host.test.js`:它通过 `/api/adapter?host=codex&validate=1` 获取并校验 surface,再用 `scripts/workflow-adapter-client-example.js` 把用户选择提交到 `chatSelectionApi`,最后断言本地 UI host 转发到 `devflow flow apply-selection`。这条测试覆盖 Codex / Cursor / CLI 宿主最关键的闭环:推荐 skill 展示、用户勾选、受控提交、policy engine 接管。高风险路径也有独立 smoke:第一次提交返回 `workflow_policy_confirmation_surface`,宿主调用 `/api/checkpoint/resolve` 接受风险后,再重试同一份 chat selection,确认风险确认不会绕过 policy engine,也不会让宿主直接改 state。
289
+
290
+ 浏览器级 smoke 可以通过下面的 opt-in 脚本执行:
291
+
292
+ ```bash
293
+ npm run test:workflow-ui-browser
294
+ ```
295
+
296
+ 这个脚本只使用本地已安装的 Playwright,不会自动下载依赖。没有 Playwright 时输出 `[skip]` 并退出成功;有 Playwright 时会启动临时 `flow ui` host,真实打开浏览器页面,点击“应用选择”和“确认 workflow”,验证主要 UI 交互链路。
229
297
 
230
298
  picker 只负责展示候选和推荐,不直接保存用户选择。用户确认推荐流程时优先运行 `devflow flow card --slug=<slug> --json` 再执行 `devflow flow confirm --slug=<slug>`;脚本仍可直接运行 `devflow flow check --slug=<slug> --json` 做底层预检。如果用户在选项卡里勾选额外专项 skill 或取消 optional step,UI 可以把选择结果交给:
231
299
 
232
300
  ```bash
233
301
  devflow flow apply-selection --slug=<slug> --selection='{"items":[...]}' --json
302
+ # 或
303
+ devflow flow apply-selection --slug=<slug> --selection-file=selection.json --json
234
304
  ```
235
305
 
306
+ `--selection` / `--selection-file` 既可以提供最小 `{ "items": [...] }`,也可以提供前端修改后的完整 `workflow_picker_surface`。完整 surface 会从 `groups[].items[]` 展平成选择项,因此 UI 可以直接把用户勾选后的 picker JSON 回传给 CLI。脚本或 IDE 集成更适合用 `--selection-file`,避免在 shell 参数里转义大段 JSON。
307
+
308
+ 宿主集成时不要自己拼 `apply-selection` 命令。推荐流程是:
309
+
310
+ 1. 调用 `devflow flow picker --slug=<slug> --json`。
311
+ 2. 用 `groups[].items[]` 渲染线性选项卡,把推荐项自动勾上,把 `locked=true` 的 gate 渲染为不可取消。
312
+ 3. 用户调整后,把修改后的 picker surface 或最小 `{ "items": [...] }` 写入临时 JSON 文件。
313
+ 4. 读取 `actions.applySelectionFile`,将命令中的 `<file>` 替换为临时文件路径,用参数数组执行该 action,而不是通过 shell 拼接字符串。
314
+ 5. 根据返回的 `workflow_selection_result_surface` 或 `workflow_policy_confirmation_surface` 渲染结果面板或风险确认卡。
315
+
236
316
  picker 的 `actions` 会返回 UI 可直接使用的入口:
237
317
 
238
318
  - `card`:打开统一 workflow 确认面,命令为 `devflow flow card --slug=<slug> --json`。
239
319
  - `check`:执行底层预检,适合脚本或调试入口。
240
320
  - `applySelection`:提交选项卡勾选结果。
321
+ - `applySelectionFile`:提交保存在文件中的选项卡勾选结果,推荐给 IDE / Web UI 使用。
241
322
  - `confirm`:确认当前 workflow 快照。
242
323
  - `suggest`:基于自然语言意图生成编排建议。
243
324
 
244
- `apply-selection` 会把安全选择转换成 `flow add-step` / `flow disable-step` 等 draft override,并复用 CLI 的 policy engine。添加检查型 step、禁用无风险 optional step 会直接写入 draft;取消 `review` / `verify` / `deliver` 这类 locked gate,或禁用风险相关专项检查时,会生成 `workflow_policy` checkpoint 并保持 workflow 不变。成功后返回 `nextAction: "devflow flow card --slug=<slug> --json"`,调用方再进入统一确认面。
325
+ `apply-selection` 会把安全选择转换成 `flow add-step` / `flow disable-step` 等 draft override,并复用 CLI 的 policy engine。添加检查型 step、禁用无风险 optional step 会直接写入 draft;取消 `review` / `verify` / `deliver` 这类 locked gate,或禁用风险相关专项检查时,会生成 `workflow_policy` checkpoint 并保持 workflow 不变。成功响应的 schema 真源是 `schemas/workflow-selection-result.schema.json`,返回 `type: "workflow_selection_result_surface"`、`diff`、`actions` 和 `nextAction: "devflow flow card --slug=<slug> --json"`;它只表示安全选择已经写入 draft,不代表 workflow 已 confirm。调用方可以先展示相对 base recipe 的差异,再进入统一确认面。
245
326
 
246
327
  如果 `apply-selection --json` 触发了风险确认,它不会输出人类文本,而是返回 `workflow_policy_confirmation_surface`,供 UI 直接渲染“保留当前流程 / 接受风险”确认卡:
247
328
 
@@ -294,6 +375,7 @@ devflow flow set-verify --slug=<slug> --reports=unit,integration,smoke,self-test
294
375
  devflow flow diff --slug=<slug>
295
376
  devflow flow picker --slug=<slug> --json
296
377
  devflow flow apply-selection --slug=<slug> --selection='{"items":[...]}' --json
378
+ devflow flow apply-selection --slug=<slug> --selection-file=selection.json --json
297
379
  devflow flow explain --slug=<slug>
298
380
  devflow flow card --slug=<slug> --json
299
381
  devflow flow check --slug=<slug> --json
@@ -502,6 +584,7 @@ devflow flow card --slug=<slug> --json
502
584
  "actions": {
503
585
  "picker": "devflow flow picker --slug=<slug> --json",
504
586
  "applySelection": "devflow flow apply-selection --slug=<slug> --selection='<json>' --json",
587
+ "applySelectionFile": "devflow flow apply-selection --slug=<slug> --selection-file=<file> --json",
505
588
  "check": "devflow flow check --slug=<slug> --json",
506
589
  "confirm": "devflow flow confirm --slug=<slug>"
507
590
  },
@@ -556,6 +639,8 @@ devflow ingest "<url-or-file>" --project-root=<repo>
556
639
  devflow checkpoint resolve --id=<problem-card-id> --decision=accept
557
640
  devflow flow picker --slug=<slug> --json
558
641
  devflow flow apply-selection --slug=<slug> --selection='<json>' --json
642
+ # IDE / Web UI 推荐读取 picker.actions.applySelectionFile 后提交选择文件
643
+ devflow flow apply-selection --slug=<slug> --selection-file=selection.json --json
559
644
  devflow flow explain --slug=<slug>
560
645
  devflow flow card --slug=<slug> --json
561
646
  devflow flow confirm --slug=<slug>
@@ -584,6 +669,8 @@ IDE / Web UI 可以调用 `devflow status --slug=<slug> --json` 作为当前 cha
584
669
  - `blockingReason`:如果当前需要用户确认或存在硬阻塞,给出结构化原因;没有阻塞时为 `null`。
585
670
  - `availableActions`:可直接渲染的按钮列表,包含 `id`、`label`、`command`、`primary`、`danger`、`requiresReason`。
586
671
 
672
+ `workflow.actions` 会带上 `picker`、`applySelection`、`applySelectionFile`、`card`、`check`、`confirm`,方便 IDE / Web UI 从 status 页面直接打开选项卡、提交选择文件或进入确认卡,不需要自己拼 `flow apply-selection` 命令。
673
+
587
674
  这份 JSON contract 的 schema 真源是 `schemas/status-surface.schema.json`。UI 集成或插件适配时,应以该 schema 作为字段兼容边界;新增字段可以向后兼容追加,但 `primaryPanel` / `blockingReason` / `availableActions` 的核心字段不应随意改名。
588
675
  典型输出样例固定在 `test/fixtures/status-surfaces/*.json`,覆盖 workflow draft、pending checkpoint、review blocked、verify missing reports 和 confirmed current step。修改 `status --json` UI 字段时,应同步更新这些 golden snapshots。
589
676
 
@@ -613,8 +700,12 @@ IDE / Web UI 可以调用 `devflow status --slug=<slug> --json` 作为当前 cha
613
700
  "currentStep": "requirement",
614
701
  "check": { "ok": false },
615
702
  "actions": {
703
+ "picker": "devflow flow picker --slug=<slug> --json",
704
+ "applySelection": "devflow flow apply-selection --slug=<slug> --selection='<json>' --json",
705
+ "applySelectionFile": "devflow flow apply-selection --slug=<slug> --selection-file=<file> --json",
616
706
  "card": "devflow flow card --slug=<slug> --json",
617
- "picker": "devflow flow picker --slug=<slug> --json"
707
+ "check": "devflow flow check --slug=<slug> --json",
708
+ "confirm": "devflow flow confirm --slug=<slug>"
618
709
  }
619
710
  },
620
711
  "pendingCheckpoint": { "type": "workflow_policy" },
@@ -29,11 +29,17 @@
29
29
  small { color: var(--muted); font-size: 12px; line-height: 1.35; }
30
30
  li p { color: var(--warn); font-size: 12px; margin: 0; }
31
31
  .command-row, .actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; align-items: center; }
32
+ .result { border-color: #95c9bf; background: #f5fbf9; }
33
+ .result-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px; }
34
+ .result-grid div { display: grid; gap: 5px; border: 1px solid var(--line); border-radius: 8px; background: white; padding: 12px; min-height: 72px; }
35
+ .result-grid strong { overflow-wrap: anywhere; }
36
+ .policy { border-color: #e3b16f; background: #fffaf3; }
37
+ .risk-box { display: grid; gap: 4px; margin-top: 12px; border: 1px solid #e3b16f; border-radius: 8px; background: white; padding: 12px; }
32
38
  .timeline { display: grid; grid-template-columns: repeat(8, minmax(96px, 1fr)); gap: 8px; overflow-x: auto; padding-bottom: 4px; }
33
39
  .timeline li { min-width: 96px; border: 1px solid var(--line); border-radius: 8px; padding: 10px; background: var(--panel); display: grid; gap: 5px; }
34
40
  .timeline span { display: grid; place-items: center; width: 22px; height: 22px; border-radius: 999px; background: var(--accent); color: white; font-size: 12px; font-weight: 700; }
35
41
  button { border: 0; border-radius: 6px; padding: 8px 12px; background: var(--accent); color: white; font-weight: 700; }
36
- @media (max-width: 860px) { .toolbar { align-items: start; flex-direction: column; } .groups { grid-template-columns: 1fr; } .timeline { grid-template-columns: repeat(4, minmax(96px, 1fr)); } }
42
+ @media (max-width: 860px) { .toolbar { align-items: start; flex-direction: column; } .groups, .result-grid { grid-template-columns: 1fr; } .timeline { grid-template-columns: repeat(4, minmax(96px, 1fr)); } }
37
43
  </style>
38
44
  </head>
39
45
  <body>
@@ -216,6 +222,32 @@
216
222
  <code>devflow flow card --slug=snapshot-picker-risk --json</code>
217
223
  </div>
218
224
  </section>
225
+ <section class="surface result" data-surface="workflow-selection-result" aria-label="Workflow selection result">
226
+ <div class="surface-head"><h2>选择已应用</h2><span>workflow_selection_result_surface / ok</span></div>
227
+ <div class="result-grid">
228
+ <div>
229
+ <small>新增</small>
230
+ <strong>frontend-quality</strong>
231
+ </div>
232
+ <div>
233
+ <small>禁用</small>
234
+ <strong>archive</strong>
235
+ </div>
236
+ <div>
237
+ <small>移动</small>
238
+ <strong>-</strong>
239
+ </div>
240
+ <div>
241
+ <small>验证报告</small>
242
+ <strong>-</strong>
243
+ </div>
244
+ </div>
245
+ <div class="actions">
246
+ <button type="button">continue</button>
247
+ <code>devflow flow card --slug=snapshot-selection-result --json</code>
248
+ <code>devflow flow picker --slug=snapshot-selection-result --json</code>
249
+ </div>
250
+ </section>
219
251
  <section class="surface confirm" data-surface="workflow-card" aria-label="Workflow confirmation card">
220
252
  <div class="surface-head"><h2>确认卡</h2><span>ok / draft</span></div>
221
253
  <ol class="timeline">
@@ -266,6 +298,19 @@
266
298
  <code>devflow flow picker --slug=snapshot-card-safe --json</code>
267
299
  </div>
268
300
  </section>
301
+ <section class="surface policy" data-surface="workflow-policy-card" aria-label="Workflow policy confirmation card">
302
+ <div class="surface-head"><h2>确认 workflow 风险</h2><span>workflow_policy_confirm / pending</span></div>
303
+ <p>这个 workflow 调整会提高风险,是否接受本次 change 的风险变化?</p>
304
+ <div class="risk-box">
305
+ <strong>禁用 required workflow step: verify</strong>
306
+ <small>workflow_policy-1234abcd · workflow</small>
307
+ </div>
308
+ <div class="actions">
309
+ <button type="button">accept-risk</button>
310
+ <code>devflow checkpoint resolve --id=workflow_policy-1234abcd --decision=accept-risk</code>
311
+ <code>devflow status --slug=snapshot-policy-risk</code>
312
+ </div>
313
+ </section>
269
314
  </main>
270
315
  </body>
271
316
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chenguangyao/devflow-kit",
3
- "version": "0.1.43",
3
+ "version": "0.1.44",
4
4
  "description": "Independent dev workflow kit fusing arb-workflow-kit, superpowers and OpenSpec ideas. Wiki-first entry, change-folder model, worktree-swarm apply, 3-round review, multi-IDE adapter, pluggable providers.",
5
5
  "bin": {
6
6
  "devflow": "bin/devflow.js",
@@ -9,6 +9,7 @@
9
9
  "main": "src/cli/index.js",
10
10
  "files": [
11
11
  "bin/",
12
+ "client/",
12
13
  "src/",
13
14
  "scripts/",
14
15
  "schemas/",
@@ -21,6 +22,7 @@
21
22
  "scripts": {
22
23
  "start": "node bin/devflow.js",
23
24
  "test": "node --test \"test/**/*.test.js\"",
25
+ "test:workflow-ui-browser": "DEVFLOW_RUN_BROWSER_E2E=1 node scripts/workflow-ui-browser-smoke.js",
24
26
  "test:watch": "node --test --watch \"test/**/*.test.js\"",
25
27
  "pack:dry": "npm pack --dry-run",
26
28
  "lint": "echo 'lint not configured yet'",
@@ -45,8 +47,5 @@
45
47
  ],
46
48
  "author": "chenguangyao",
47
49
  "license": "MIT",
48
- "type": "commonjs",
49
- "dependencies": {
50
- "@chenguangyao/devflow-kit": "^0.1.42"
51
- }
50
+ "type": "commonjs"
52
51
  }
@@ -112,7 +112,12 @@
112
112
  },
113
113
  "additionalProperties": true
114
114
  },
115
- "workflow": { "type": ["object", "null"] },
115
+ "workflow": {
116
+ "oneOf": [
117
+ { "type": "null" },
118
+ { "$ref": "#/definitions/workflow" }
119
+ ]
120
+ },
116
121
  "pendingCheckpoint": { "type": ["object", "null"] },
117
122
  "checkpointConfirmationCard": { "type": ["object", "null"] },
118
123
  "primaryPanel": {
@@ -183,6 +188,33 @@
183
188
  },
184
189
  "additionalProperties": true,
185
190
  "definitions": {
191
+ "workflow": {
192
+ "type": "object",
193
+ "required": ["status", "baseRecipe", "currentStep", "nextStep", "overrides", "check", "actions"],
194
+ "properties": {
195
+ "status": { "type": ["string", "null"] },
196
+ "baseRecipe": { "type": ["object", "null"] },
197
+ "currentStep": { "type": ["string", "null"] },
198
+ "nextStep": { "type": ["string", "null"] },
199
+ "overrides": { "type": "object" },
200
+ "check": { "type": ["object", "null"] },
201
+ "actions": { "$ref": "#/definitions/workflowActions" }
202
+ },
203
+ "additionalProperties": true
204
+ },
205
+ "workflowActions": {
206
+ "type": "object",
207
+ "required": ["picker", "applySelection", "applySelectionFile", "card", "check", "confirm"],
208
+ "properties": {
209
+ "picker": { "type": "string" },
210
+ "applySelection": { "type": "string" },
211
+ "applySelectionFile": { "type": "string" },
212
+ "card": { "type": "string" },
213
+ "check": { "type": "string" },
214
+ "confirm": { "type": "string" }
215
+ },
216
+ "additionalProperties": true
217
+ },
186
218
  "artifact": {
187
219
  "type": "object",
188
220
  "required": ["name", "path", "exists"],
@@ -0,0 +1,104 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://devflow.dev/schemas/workflow-adapter-catalog.schema.json",
4
+ "title": "devflow workflow adapter catalog",
5
+ "description": "Machine-readable catalog for workflow adapter host profiles and render modes.",
6
+ "type": "object",
7
+ "required": [
8
+ "type",
9
+ "protocolVersion",
10
+ "schema",
11
+ "defaults",
12
+ "profiles",
13
+ "renderModes",
14
+ "hostAliases",
15
+ "examples",
16
+ "safety"
17
+ ],
18
+ "properties": {
19
+ "type": { "const": "workflow_adapter_catalog" },
20
+ "protocolVersion": { "const": "workflow-adapter-surface/v1" },
21
+ "schema": { "const": "https://devflow.dev/schemas/workflow-adapter-catalog.schema.json" },
22
+ "features": {
23
+ "type": "array",
24
+ "items": { "type": "string" }
25
+ },
26
+ "defaults": {
27
+ "type": "object",
28
+ "required": ["customHostProfile", "customHostRenderMode", "mutationModel"],
29
+ "properties": {
30
+ "customHostProfile": { "enum": ["browser"] },
31
+ "customHostRenderMode": { "enum": ["browser"] },
32
+ "mutationModel": { "const": "devflow-controlled" }
33
+ }
34
+ },
35
+ "profiles": {
36
+ "type": "array",
37
+ "items": { "$ref": "#/definitions/profile" }
38
+ },
39
+ "renderModes": {
40
+ "type": "array",
41
+ "items": { "$ref": "#/definitions/renderMode" }
42
+ },
43
+ "hostAliases": {
44
+ "type": "object",
45
+ "additionalProperties": { "type": "string" }
46
+ },
47
+ "examples": {
48
+ "type": "array",
49
+ "items": { "$ref": "#/definitions/example" }
50
+ },
51
+ "safety": {
52
+ "type": "object",
53
+ "required": ["arbitraryShell", "mutationModel", "readOnlyActions", "mutationActions"],
54
+ "properties": {
55
+ "arbitraryShell": { "const": false },
56
+ "mutationModel": { "const": "devflow-controlled" },
57
+ "readOnlyActions": {
58
+ "type": "array",
59
+ "items": { "type": "string" }
60
+ },
61
+ "mutationActions": {
62
+ "type": "array",
63
+ "items": { "type": "string" }
64
+ }
65
+ }
66
+ }
67
+ },
68
+ "definitions": {
69
+ "profile": {
70
+ "type": "object",
71
+ "required": ["id", "label", "defaultRenderMode", "description", "recommendedHosts"],
72
+ "properties": {
73
+ "id": { "enum": ["codex", "cursor", "browser", "cli"] },
74
+ "label": { "type": "string" },
75
+ "defaultRenderMode": { "enum": ["in_app_browser", "webview_or_browser", "browser", "terminal_markdown"] },
76
+ "description": { "type": "string" },
77
+ "recommendedHosts": {
78
+ "type": "array",
79
+ "items": { "type": "string" }
80
+ }
81
+ }
82
+ },
83
+ "renderMode": {
84
+ "type": "object",
85
+ "required": ["id", "label", "supportsEmbeddedUi", "description"],
86
+ "properties": {
87
+ "id": { "enum": ["in_app_browser", "webview_or_browser", "browser", "terminal_markdown"] },
88
+ "label": { "type": "string" },
89
+ "supportsEmbeddedUi": { "type": "boolean" },
90
+ "description": { "type": "string" }
91
+ }
92
+ },
93
+ "example": {
94
+ "type": "object",
95
+ "required": ["host", "profile", "renderMode", "command"],
96
+ "properties": {
97
+ "host": { "type": "string" },
98
+ "profile": { "enum": ["codex", "cursor", "browser", "cli"] },
99
+ "renderMode": { "enum": ["in_app_browser", "webview_or_browser", "browser", "terminal_markdown"] },
100
+ "command": { "type": "string" }
101
+ }
102
+ }
103
+ }
104
+ }
@@ -0,0 +1,193 @@
1
+ {
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
+ "$id": "https://devflow.dev/schemas/workflow-adapter-surface.schema.json",
4
+ "title": "devflow workflow adapter surface",
5
+ "description": "Machine-readable surface returned by devflow flow adapter --json for Codex/Cursor/browser/CLI and custom host integration.",
6
+ "type": "object",
7
+ "required": [
8
+ "type",
9
+ "protocolVersion",
10
+ "host",
11
+ "profile",
12
+ "slug",
13
+ "renderMode",
14
+ "url",
15
+ "capabilities",
16
+ "actions",
17
+ "chatCard",
18
+ "instructions",
19
+ "fallbackMarkdown"
20
+ ],
21
+ "properties": {
22
+ "type": { "const": "workflow_adapter_surface" },
23
+ "protocolVersion": { "const": "workflow-adapter-surface/v1" },
24
+ "host": { "type": "string" },
25
+ "profile": { "enum": ["codex", "cursor", "browser", "cli"] },
26
+ "slug": { "type": "string" },
27
+ "renderMode": { "enum": ["in_app_browser", "webview_or_browser", "browser", "terminal_markdown"] },
28
+ "url": { "type": "string" },
29
+ "features": {
30
+ "type": "array",
31
+ "items": { "type": "string" }
32
+ },
33
+ "capabilities": { "$ref": "#/definitions/capabilities" },
34
+ "actions": { "$ref": "#/definitions/actionMap" },
35
+ "chatCard": { "$ref": "#/definitions/chatCard" },
36
+ "instructions": {
37
+ "type": "array",
38
+ "items": { "type": "string" }
39
+ },
40
+ "fallbackMarkdown": { "type": "string" }
41
+ },
42
+ "additionalProperties": true,
43
+ "definitions": {
44
+ "capabilities": {
45
+ "type": "object",
46
+ "required": [
47
+ "schema",
48
+ "selectionSchema",
49
+ "errorSchema",
50
+ "commandResultSchema",
51
+ "host",
52
+ "profile",
53
+ "renderMode",
54
+ "supportsChatCard",
55
+ "supportsChatSelection",
56
+ "supportsNativeCheckboxes",
57
+ "supportsEmbeddedUi",
58
+ "supportsReadOnlyDiff",
59
+ "supportsReadOnlyExplain",
60
+ "supportsApplySelection",
61
+ "supportsCheckpointResolve",
62
+ "supportsConfirm",
63
+ "mutationModel"
64
+ ],
65
+ "properties": {
66
+ "schema": { "const": "https://devflow.dev/schemas/workflow-adapter-surface.schema.json" },
67
+ "selectionSchema": { "const": "https://devflow.dev/schemas/workflow-selection.schema.json" },
68
+ "errorSchema": { "const": "https://devflow.dev/schemas/workflow-ui-error.schema.json" },
69
+ "commandResultSchema": { "const": "https://devflow.dev/schemas/workflow-ui-command-result.schema.json" },
70
+ "host": { "type": "string" },
71
+ "profile": { "enum": ["codex", "cursor", "browser", "cli"] },
72
+ "renderMode": { "enum": ["in_app_browser", "webview_or_browser", "browser", "terminal_markdown"] },
73
+ "features": {
74
+ "type": "array",
75
+ "items": { "type": "string" }
76
+ },
77
+ "supportsChatCard": { "type": "boolean" },
78
+ "supportsChatSelection": { "type": "boolean" },
79
+ "supportsNativeCheckboxes": { "type": "boolean" },
80
+ "supportsEmbeddedUi": { "type": "boolean" },
81
+ "supportsReadOnlyDiff": { "type": "boolean" },
82
+ "supportsReadOnlyExplain": { "type": "boolean" },
83
+ "supportsApplySelection": { "type": "boolean" },
84
+ "supportsCheckpointResolve": { "type": "boolean" },
85
+ "supportsConfirm": { "type": "boolean" },
86
+ "mutationModel": { "const": "devflow-controlled" }
87
+ },
88
+ "additionalProperties": true
89
+ },
90
+ "actionMap": {
91
+ "type": "object",
92
+ "required": [
93
+ "startUi",
94
+ "startUiJson",
95
+ "statusApi",
96
+ "pickerApi",
97
+ "diffApi",
98
+ "explainApi",
99
+ "applySelectionApi",
100
+ "chatSelectionApi",
101
+ "cardApi",
102
+ "checkpointResolveApi",
103
+ "confirmApi"
104
+ ],
105
+ "properties": {
106
+ "startUi": { "type": "string" },
107
+ "startUiJson": { "type": "string" },
108
+ "statusApi": { "type": "string" },
109
+ "pickerApi": { "type": "string" },
110
+ "diffApi": { "type": "string" },
111
+ "explainApi": { "type": "string" },
112
+ "applySelectionApi": { "type": "string" },
113
+ "chatSelectionApi": { "type": "string" },
114
+ "cardApi": { "type": "string" },
115
+ "checkpointResolveApi": { "type": "string" },
116
+ "confirmApi": { "type": "string" }
117
+ },
118
+ "additionalProperties": true
119
+ },
120
+ "chatCard": {
121
+ "type": "object",
122
+ "required": [
123
+ "type",
124
+ "format",
125
+ "title",
126
+ "selection",
127
+ "markdown"
128
+ ],
129
+ "properties": {
130
+ "type": { "const": "workflow_chat_card" },
131
+ "format": { "const": "markdown" },
132
+ "title": { "type": "string" },
133
+ "selection": { "$ref": "#/definitions/chatSelection" },
134
+ "markdown": { "type": "string" }
135
+ },
136
+ "additionalProperties": true
137
+ },
138
+ "chatSelection": {
139
+ "type": "object",
140
+ "required": ["type", "layout", "groups"],
141
+ "properties": {
142
+ "type": { "const": "workflow_chat_selection" },
143
+ "layout": { "const": "linear-checkboxes" },
144
+ "groups": {
145
+ "type": "array",
146
+ "items": { "$ref": "#/definitions/chatSelectionGroup" }
147
+ }
148
+ },
149
+ "additionalProperties": true
150
+ },
151
+ "chatSelectionGroup": {
152
+ "type": "object",
153
+ "required": ["id", "label", "items"],
154
+ "properties": {
155
+ "id": { "type": "string" },
156
+ "label": { "type": "string" },
157
+ "items": {
158
+ "type": "array",
159
+ "items": { "$ref": "#/definitions/chatSelectionItem" }
160
+ }
161
+ },
162
+ "additionalProperties": true
163
+ },
164
+ "chatSelectionItem": {
165
+ "type": "object",
166
+ "required": [
167
+ "step",
168
+ "skill",
169
+ "label",
170
+ "selected",
171
+ "recommended",
172
+ "locked",
173
+ "required",
174
+ "installed",
175
+ "reason",
176
+ "command"
177
+ ],
178
+ "properties": {
179
+ "step": { "type": "string" },
180
+ "skill": { "type": "string" },
181
+ "label": { "type": "string" },
182
+ "selected": { "type": "boolean" },
183
+ "recommended": { "type": "boolean" },
184
+ "locked": { "type": "boolean" },
185
+ "required": { "type": "boolean" },
186
+ "installed": { "type": "boolean" },
187
+ "reason": { "type": ["string", "null"] },
188
+ "command": { "type": ["string", "null"] }
189
+ },
190
+ "additionalProperties": true
191
+ }
192
+ }
193
+ }