@olierjs/fugue-cli 0.1.0 → 0.1.3

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.
Files changed (43) hide show
  1. package/README.md +54 -2
  2. package/THIRD_PARTY_NOTICES +52 -1
  3. package/dist/darwin-arm64/fugue-cli +0 -0
  4. package/dist/darwin-x64/fugue-cli +0 -0
  5. package/dist/linux-arm64/fugue-cli +0 -0
  6. package/dist/linux-x64/fugue-cli +0 -0
  7. package/dist/win32-arm64/fugue-cli.exe +0 -0
  8. package/dist/win32-x64/fugue-cli.exe +0 -0
  9. package/package.json +2 -1
  10. package/skills/fugue-show-me/SKILL.md +44 -0
  11. package/skills/fugue-show-me/assets/diagram.css +77 -0
  12. package/skills/fugue-show-me/references/pele.md +68 -0
  13. package/skills/fugue-show-me/scripts/render-html.mjs +116 -0
  14. package/skills/show-me/SKILL.md +128 -0
  15. package/skills/show-me/agents/openai.yaml +2 -0
  16. package/skills/statechart/SKILL.md +36 -0
  17. package/skills/statechart/scripts/bug-states.mjs +81 -0
  18. package/skills/statechart/scripts/chart-edges.mjs +68 -0
  19. package/skills/statechart/scripts/chart-parts.mjs +21 -0
  20. package/skills/statechart/scripts/chart-to-tla.mjs +205 -0
  21. package/skills/statechart/scripts/compare-charts.mjs +74 -0
  22. package/skills/statechart/scripts/lib.mjs +970 -0
  23. package/skills/statechart/scripts/map-view.mjs +92 -0
  24. package/skills/statechart/scripts/property-loop.mjs +60 -0
  25. package/skills/statechart/scripts/read-check.mjs +79 -0
  26. package/skills/statechart/scripts/regions-chart.mjs +307 -0
  27. package/skills/statechart/scripts/render.mjs +531 -0
  28. package/skills/statechart/scripts/rule-book.mjs +84 -0
  29. package/skills/statechart/scripts/second-replay.mjs +122 -0
  30. package/skills/statechart/scripts/snapshot.mjs +74 -0
  31. package/skills/statechart/scripts/spec-text.mjs +126 -0
  32. package/skills/statechart/scripts/statechart-help.mjs +350 -0
  33. package/skills/statechart/scripts/statechart.mjs +903 -0
  34. package/skills/statechart/scripts/test/band.mmd +15 -0
  35. package/skills/statechart/scripts/test/regions.mmd +62 -0
  36. package/skills/statechart/scripts/test/statechart.test.mjs +6854 -0
  37. package/skills/statechart/scripts/test/sync.mmd +19 -0
  38. package/skills/statechart/scripts/test/upload.mmd +16 -0
  39. package/skills/statechart/scripts/tlc-to-chart-args.mjs +52 -0
  40. package/skills/statechart/scripts/tlc-to-chart.mjs +802 -0
  41. package/skills/statechart/scripts/tlc-tools.mjs +69 -0
  42. package/skills/statechart/scripts/ways-in.mjs +69 -0
  43. package/upstream-skills.json +18 -0
package/README.md CHANGED
@@ -13,7 +13,9 @@ fugue-cli guide
13
13
 
14
14
  后续更新执行同一条安装命令。包内提供 macOS、Linux、Windows 的 x64 和 arm64 二进制。
15
15
 
16
- 安装脚本自动将包内 Skill 同步到 `~/.agents/skills`,包括薄入口 `fugue-stack` 和完整的 `plan-with-remote`。无需手动复制目录。替换同名旧 Skill 或清理本工具管理的过时 Skill 前,会先保存备份;其他 Skill 保持原样。备份放在 `~/.local/share/fugue-cli/backups`,位于技能发现目录之外。
16
+ 使用 nvm 管理 Node 时,先在当前终端执行 `nvm use 22.19.0` 或选择更高版本,再安装和运行。启动 Agent 的环境也需要能通过 `PATH` 找到 `fugue-cli`。
17
+
18
+ 安装脚本自动将包内 Skill 同步到 `~/.agents/skills`,包括薄入口 `fugue-stack`、远端规划 `plan-with-remote`、原版 `show-me`、原版 `statechart` 和可视化解释适配 `fugue-show-me`。无需手动复制目录。替换同名旧 Skill 或清理本工具管理的过时 Skill 前,会先保存备份;其他 Skill 保持原样。备份放在 `~/.local/share/fugue-cli/backups`,位于技能发现目录之外。
17
19
 
18
20
  自动同步通过 npm 的 `postinstall` 执行,需要允许安装脚本运行。清理发生在安装或升级时,针对新版已移除的受管理 Skill;`npm uninstall` 没有对应的自动清理钩子。
19
21
 
@@ -27,24 +29,74 @@ fugue-cli --help
27
29
  fugue-cli --version
28
30
  ```
29
31
 
32
+ 在目标项目中启动支持 Skill 的 Agent,描述要完成的工作,例如:
33
+
34
+ ```text
35
+ $fugue-stack 根据这份已确认需求实现功能,完成后审查差异并验证。
36
+ $fugue-stack 审查当前代码变更,列出有实际影响的问题。
37
+ $fugue-show-me 用最小的结构图解释这次改动。
38
+ $fugue-show-me 检查取消与重试交错时的状态变化,画出可能的缺陷路径。
39
+ ```
40
+
41
+ `fugue-cli guide` 输出工作指南,编码、审查和解释由当前 Agent 按指南执行。若安装后暂时看不到新 Skill,可重新启动 Agent 后再选择入口。
42
+
30
43
  需求理解、规划和需求或方案解释继续由 `plan-with-remote` 及远端规划服务负责。指南提供规划技能的绝对路径;该技能的客户端需要 Git、已配置的规划服务,以及已在服务中登记的目标仓库,配置说明见 [configuration.md](skills/plan-with-remote/references/configuration.md)。
31
44
 
32
45
  编码、代码审查与验证、面向人的实现解释由当前 Agent 按指南完成。读取指南本身不会发起规划、修改项目或调用模型。远端规划返回的正式文档继续按 Markdown 原文保存,批准版本由规划客户端和服务端管理。
33
46
 
47
+ ## 上游技能
48
+
49
+ `show-me` 完整目录原样引入自 [humanlayer/skills](https://github.com/humanlayer/skills/tree/653b6411c1f70c275a18e37673b042ff99f67ceb/plugins/show-me/skills/show-me),保留原始说明和 Agent 调用配置。来源、固定提交和目录记录在 [upstream-skills.json](upstream-skills.json),MIT 许可保留在 [THIRD_PARTY_NOTICES](THIRD_PARTY_NOTICES)。
50
+
51
+ `statechart` 完整目录原样引入自 [cursor/plugins 的 pstack](https://github.com/cursor/plugins/tree/99b609a72323b70204eab407c622f436e858e112/pstack/skills/statechart),包括原始说明、脚本和测试。同样在来源记录中固定提交,并保留 MIT 许可。
52
+
53
+ 需要可视化解释时,总览路由到 [fugue-show-me](skills/fugue-show-me/SKILL.md):由 `show-me` 选择通用表达形式,需要分析状态变化、检查行为规则或展示缺陷路径时配合 `statechart`。适配维护在独立目录中;更新上游时整体替换原版目录并更新来源记录。
54
+
55
+ `fugue-show-me` 根据这些方法组织全局视图和必要的交互、状态细节,再通过本层的 [Pele 输出脚本](skills/fugue-show-me/scripts/render-html.mjs) 将 Mermaid 统一展示为单文件 HTML。图在正文直接可见,采用 Flexoki 浅色与深色主题,按信息角色分色,支持自适应和缩放。首次生成页面会将 `pele@1.12.0` 安装到独立用户缓存,生成的页面可离线打开;该展示链路不导入或修改上游的内部渲染模块。
56
+
57
+ ## 状态图依赖
58
+
59
+ `statechart` 使用 Node.js 和 npm;模型检查还需要 [mise](https://mise.jdx.dev/getting-started.html)。macOS 可先安装:
60
+
61
+ ```sh
62
+ brew install mise
63
+ node ~/.agents/skills/statechart/scripts/statechart.mjs --help
64
+ ```
65
+
66
+ 首次模型检查由原版脚本通过 mise 安装 Java Temurin 25 和 TLC 1.7.4,需要联网下载约 305 MB。若使用已有环境,可按上游帮助设置 `JAVA` 和 `TLA2TOOLS`。首次 PNG 出图会将 `pele@1.8.0` 和 `@resvg/resvg-js@2.6.2` 安装到用户缓存目录;仅渲染现成 Mermaid 图时不运行 TLC。
67
+
68
+ 该版 TLC 无法解析模型字符串中的中文。模型使用 ASCII 标识符和字符串值,中文标签放在映射文件中,从模型生成中文状态图。
69
+
70
+ 输入和产物放在目标仓库之外的临时目录,按上游要求交付 PNG、Mermaid 源码、缺陷说明和再生成命令。模型检查结果与真实代码复现结果分别说明。
71
+
34
72
  ## 开发与发布
35
73
 
36
74
  开发与打包需要 Go >=1.23 和 Node >=22.19.0。
37
75
 
76
+ 验证尚未发布到 npm 的仓库版本时,在仓库根目录打包,再安装生成的本地包:
77
+
78
+ ```sh
79
+ npm pack
80
+ npm install -g ./olierjs-fugue-cli-0.1.3.tgz
81
+ fugue-cli --version
82
+ fugue-cli guide
83
+ ```
84
+
85
+ 本地包安装同样会运行 Skill 自动同步。`@latest` 使用 npm 上的已发布版本,本地包使用本次构建的仓库版本。
86
+
38
87
  ```sh
39
88
  go test ./...
40
89
  node --test tests/*.test.mjs
90
+ node --test skills/statechart/scripts/test/statechart.test.mjs
41
91
  npm pack
42
92
  ```
43
93
 
94
+ 上游测试包含 Linux 的 `LD_PRELOAD` 故障注入,并对临时路径和英文 Java 诊断文本作精确断言;macOS 上需单独核对这些环境差异。
95
+
44
96
  `npm pack` 的 `prepack` 自动构建各平台二进制,并将版本写入命令。发布目标固定为 npmjs,包名为 `@olierjs/fugue-cli`。发布验收后的包:
45
97
 
46
98
  ```sh
47
- npm publish ./olierjs-fugue-cli-0.1.0.tgz --access public
99
+ npm publish ./olierjs-fugue-cli-0.1.3.tgz --access public
48
100
  ```
49
101
 
50
102
  Go 命令入口位于 [cmd/fugue-cli](cmd/fugue-cli/main.go),指南源码位于 [guide.md](internal/cli/guide.md),入口技能位于 [SKILL.md](skills/fugue-stack/SKILL.md)。
@@ -1,4 +1,4 @@
1
- Third-party dependencies included in the compiled fugue-cli binaries.
1
+ Third-party components distributed with fugue-cli.
2
2
 
3
3
 
4
4
  github.com/spf13/cobra v1.10.2
@@ -414,3 +414,54 @@ github.com/inconshreveable/mousetrap v1.1.0
414
414
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
415
415
  See the License for the specific language governing permissions and
416
416
  limitations under the License.
417
+
418
+ humanlayer/skills: show-me (653b6411c1f70c275a18e37673b042ff99f67ceb)
419
+ https://github.com/humanlayer/skills/tree/653b6411c1f70c275a18e37673b042ff99f67ceb/plugins/show-me/skills/show-me
420
+
421
+ MIT License
422
+
423
+ Copyright (c) 2026 HumanLayer
424
+
425
+ Permission is hereby granted, free of charge, to any person obtaining a copy
426
+ of this software and associated documentation files (the "Software"), to deal
427
+ in the Software without restriction, including without limitation the rights
428
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
429
+ copies of the Software, and to permit persons to whom the Software is
430
+ furnished to do so, subject to the following conditions:
431
+
432
+ The above copyright notice and this permission notice shall be included in all
433
+ copies or substantial portions of the Software.
434
+
435
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
436
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
437
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
438
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
439
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
440
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
441
+ SOFTWARE.
442
+
443
+
444
+ cursor/plugins: pstack statechart (99b609a72323b70204eab407c622f436e858e112)
445
+ https://github.com/cursor/plugins/tree/99b609a72323b70204eab407c622f436e858e112/pstack/skills/statechart
446
+
447
+ MIT License
448
+
449
+ Copyright (c) 2026 Lauren Tan
450
+
451
+ Permission is hereby granted, free of charge, to any person obtaining a copy
452
+ of this software and associated documentation files (the "Software"), to deal
453
+ in the Software without restriction, including without limitation the rights
454
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
455
+ copies of the Software, and to permit persons to whom the Software is
456
+ furnished to do so, subject to the following conditions:
457
+
458
+ The above copyright notice and this permission notice shall be included in all
459
+ copies or substantial portions of the Software.
460
+
461
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
462
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
463
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
464
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
465
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
466
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
467
+ SOFTWARE.
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olierjs/fugue-cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.3",
4
4
  "description": "Fugue 需求交付工作指南与 Agent 技能",
5
5
  "type": "module",
6
6
  "bin": { "fugue-cli": "bin/fugue-cli.mjs" },
@@ -10,6 +10,7 @@
10
10
  "scripts/postinstall.mjs",
11
11
  "scripts/skills.mjs",
12
12
  "skills/",
13
+ "upstream-skills.json",
13
14
  "THIRD_PARTY_NOTICES"
14
15
  ],
15
16
  "engines": { "node": ">=22.19.0" },
@@ -0,0 +1,44 @@
1
+ ---
2
+ name: fugue-show-me
3
+ description: 组合 show-me 与 statechart 的分析视角,用 Pele 将 Mermaid 展示为可读的 HTML 图解;解释实现、代码变更、运行结果、状态变化或缺陷路径时使用。
4
+ ---
5
+
6
+ # 可视化解释
7
+
8
+ 上游 skill 保持独立。本层负责按问题选择分析方法、组织面向人的视图,再用 Pele 统一展示;不修改上游,也不调用它们的内部渲染模块。
9
+
10
+ ## 获取信息与组织视图
11
+
12
+ 先阅读并遵循[上游 show-me](../show-me/SKILL.md),由它指导表达形式的选择。技能提供分析方法,事实来自当前源码、差异、文档和实际运行结果。
13
+
14
+ | 问题需要的视角 | 方法与信息 | 适合的图 |
15
+ | --- | --- | --- |
16
+ | 系统全貌、职责边界、数据流 | 按 `show-me` 从材料中提取组件、输入输出与关系 | 流程图、分组结构图 |
17
+ | 谁先做什么、谁调用谁 | 按 `show-me` 跟随真实调用或事件顺序 | 时序图 |
18
+ | 状态、并发、重试、取消、恢复、缺陷路径 | 阅读并遵循[上游 statechart](../statechart/SKILL.md),获取模型状态、转换、规则、检查结果和复现证据 | 上游生成的状态图与缺陷路径图 |
19
+
20
+ 按问题选用必要的方法;普通结构解释不强制建模。任务中其他已选技能的输出也可作为材料,使用前核对其依据和适用范围。
21
+
22
+ 模型负责组织信息:统一不同材料的对象名称、版本和粒度;区分已确认行为、模型假设与待确认关系。涉及多个组件或分支时先给出边界和主路径的总览,再按问题展开关键交互或状态细节。一张图回答一个问题,避免把全部实现细节堆进总览;说明使用用户的语言,标签优先写读者关心的行为。
23
+
24
+ 图中按职责或状态区分颜色,沿用 [Pele 展示说明](references/pele.md)中的颜色角色;由模型明确标注输入、处理、判断、外部交互、完成、失败和跳过,保留对应文字标签。不要按节点顺序轮流染色,也不让脚本从节点名称猜业务含义。浅色和深色使用同一套颜色含义,原模型的规则结果和缺陷路径标记优先。
25
+
26
+ 使用 `statechart` 时保留上游的建模、模型检查、复现及交付要求。原版 TLC 使用 ASCII 模型标识符和字符串值,中文状态与步骤标签写在映射文件中。将它生成的 `.mmd` 直接交给展示层,保留状态、边、步骤编号和缺陷标记;另画的简洁总览不能替代经检查的模型图,Pele 的渲染成功也不证明行为正确。
27
+
28
+ ## 用 Pele 交付图示
29
+
30
+ 选择 Mermaid 表达并需要 HTML 时,用本层的 [Pele 输出脚本](scripts/render-html.mjs) 渲染。输入可以同时包含模型编写的总览、时序图和 `statechart` 生成的状态图,顺序就是页面阅读顺序。用易懂的文件名作为各图标题。
31
+
32
+ ```bash
33
+ node <本 skill 目录>/scripts/render-html.mjs --title "发布流程说明" --out /绝对路径/说明.html /绝对路径/全局视图.mmd /绝对路径/状态流转.mmd
34
+ ```
35
+
36
+ 脚本通过 Pele 的公开 API 生成内嵌 SVG,并将 Pele 的浏览器包一并嵌入 HTML,用它提供自适应、中文字体测量和缩放。采用官方站点的 Flexoki 浅色与深色配色,页面内可切换,`--theme light|dark` 指定初始主题。首次安装固定版本依赖,后续复用本层的独立缓存;页面打开时无需联网或启动服务。集成到已有 HTML、调整主题或定位兼容问题时,阅读 [Pele 展示说明](references/pele.md)。
37
+
38
+ 核心图放在对应说明旁、默认可见,简短说明补充结论、范围与必要证据。源码和模型检查材料可放附录,下载入口仅作补充。在支持 Mermaid 的对话或文档中可直接使用会渲染的 `mermaid` 代码块;表达形式更适合树、伪代码或表格时,继续遵循 `show-me`,无需强制转成图。
39
+
40
+ ## 交付检查
41
+
42
+ 打开最终交付的 HTML 或图片,检查实际图示;不能用“文件已生成”、HTML 源码或下载链接存在代替检查。HTML 检查桌面和窄屏,确认核心图已显示、中文标签及连线可读、大图可查看完整内容;本地静态页面还需确认离线可显示。渲染失败就修复后重新检查,不把下载源码当成完成。无法预览时明确说明未验证的范围。
43
+
44
+ 将上游的 `Bash(open ...)` 对应为当前宿主可用的终端或文件预览工具,按实际操作系统打开 HTML 或图片。交付本地文件时提供绝对路径链接;仅在实际打开成功后说明已打开。
@@ -0,0 +1,77 @@
1
+ /* Flexoki light/dark palettes from https://pele.run/theming and the official site. */
2
+ :root {
3
+ color-scheme: light;
4
+ --bg: #fffcf0; --surface: #f2f0e5; --surface-alt: #e6e4d9;
5
+ --text: #100f0f; --muted: #6f6e69; --border: #cecdc3; --accent: #205ea6;
6
+ --page-muted: #575653; --link: #205ea6;
7
+ --role-input: #24837b; --role-process: #205ea6; --role-decision: #ad8301;
8
+ --role-external: #5e409d; --role-success: #66800b; --role-failure: #af3029; --role-skipped: #6f6e69;
9
+ --role-strength: 18%;
10
+ --pele-series-1: #af3029; --pele-series-2: #bc5215; --pele-series-3: #ad8301; --pele-series-4: #66800b;
11
+ --pele-series-5: #24837b; --pele-series-6: #205ea6; --pele-series-7: #5e409d; --pele-series-8: #a02f6f;
12
+ }
13
+ html[data-theme="dark"] {
14
+ color-scheme: dark;
15
+ --bg: #100f0f; --surface: #1c1b1a; --surface-alt: #282726;
16
+ --text: #cecdc3; --muted: #878580; --border: #403e3c; --accent: #4385be;
17
+ --page-muted: #878580; --link: #66a0c8;
18
+ --role-input: #3aa99f; --role-process: #4385be; --role-decision: #d0a215;
19
+ --role-external: #8b7ec8; --role-success: #879a39; --role-failure: #d14d41; --role-skipped: #878580;
20
+ --role-strength: 24%;
21
+ --pele-series-1: #d14d41; --pele-series-2: #da702c; --pele-series-3: #d0a215; --pele-series-4: #879a39;
22
+ --pele-series-5: #3aa99f; --pele-series-6: #4385be; --pele-series-7: #8b7ec8; --pele-series-8: #ce5d97;
23
+ }
24
+ * { box-sizing: border-box; }
25
+ body { margin: 0; background: var(--surface); color: var(--text); font: 16px/1.65 var(--font); }
26
+ main { max-width: 1200px; margin: auto; padding: 40px 28px; }
27
+ .page-heading { display: flex; justify-content: space-between; align-items: start; gap: 20px; }
28
+ h1 { font-size: clamp(26px,4vw,38px); line-height: 1.3; margin: 0 0 12px; }
29
+ h2 { font-size: 20px; margin: 0 0 16px; }
30
+ header p { color: var(--page-muted); font-size: 14px; }
31
+ .theme-controls { display: flex; flex-shrink: 0; padding: 3px; gap: 3px; border: 1px solid var(--border); border-radius: 9px; background: var(--bg); }
32
+ .theme-controls button { cursor: pointer; font: inherit; font-size: 13px; min-height: 36px; padding: 5px 14px; border: 0; border-radius: 6px; background: transparent; color: var(--muted); }
33
+ .theme-controls button[aria-pressed="true"] { background: var(--surface-alt); color: var(--text); font-weight: 600; }
34
+ nav { display: flex; flex-wrap: wrap; gap: 10px 20px; margin: 20px 0; }
35
+ a { color: var(--link); text-decoration: underline; text-underline-offset: 4px; overflow-wrap: anywhere; }
36
+ .legend { display: flex; flex-wrap: wrap; gap: 10px 22px; padding: 0; margin: 22px 0 32px; list-style: none; font-size: 13px; color: var(--page-muted); }
37
+ .legend li { display: inline-flex; align-items: center; gap: 8px; }
38
+ .legend i { width: 12px; height: 12px; border-radius: 3px; background: var(--role-color); }
39
+ section { min-width: 0; margin: 24px 0; padding: 24px; background: var(--bg); border: 1px solid var(--border); border-radius: 12px; }
40
+ .diagram {
41
+ width: 100%; min-width: 0;
42
+ --pele-bg: var(--bg); --pele-fg: var(--text); --pele-fg-on-light: #100f0f; --pele-fg-on-dark: #fffcf0;
43
+ --pele-muted: var(--muted); --pele-line: var(--muted); --pele-surface: var(--surface);
44
+ --pele-surface-alt: var(--surface-alt); --pele-border: var(--border); --pele-accent: var(--accent);
45
+ --pele-critical: var(--role-failure); --pele-error: var(--role-failure); --pele-font: var(--font);
46
+ }
47
+ .diagram > svg { display: block; max-width: 100%; height: auto; margin-inline: auto; }
48
+ .fugueInput { --role-color: var(--role-input); }
49
+ .fugueProcess { --role-color: var(--role-process); }
50
+ .fugueDecision { --role-color: var(--role-decision); }
51
+ .fugueExternal { --role-color: var(--role-external); }
52
+ .fugueSuccess { --role-color: var(--role-success); }
53
+ .fugueFailure { --role-color: var(--role-failure); }
54
+ .fugueSkipped { --role-color: var(--role-skipped); }
55
+
56
+ /* Neutral model states follow the theme; rule outcomes keep their source colors. */
57
+ .diagram .pele-state:is(.works,.worksPath) > :is(rect,path,circle,ellipse,polygon) { fill: var(--surface) !important; }
58
+ .diagram .pele-state:is(.works,.worksPath) .pele-label { fill: var(--text) !important; }
59
+
60
+ /* Roles are explicit Mermaid classes, never guessed from labels or source order. */
61
+ .diagram .pele-node:is(.fugueInput,.fugueProcess,.fugueDecision,.fugueExternal,.fugueSuccess,.fugueFailure,.fugueSkipped):not([class*="wrong" i]):not([class*="fixed" i]):not([class*="mixed" i]):not([class*="after" i]):not([class*="worse" i]) > :is(rect,path,circle,ellipse,polygon) {
62
+ fill: color-mix(in srgb, var(--role-color) var(--role-strength), var(--bg)) !important;
63
+ stroke: var(--path-border, var(--role-color)) !important;
64
+ }
65
+ .diagram .pele-node:is(.fugueInput,.fugueProcess,.fugueDecision,.fugueExternal,.fugueSuccess,.fugueFailure,.fugueSkipped):not([class*="wrong" i]):not([class*="fixed" i]):not([class*="mixed" i]):not([class*="after" i]):not([class*="worse" i]) .pele-label { fill: var(--text) !important; }
66
+
67
+ /* Blue path borders remain blue, with their original width and step numbers. */
68
+ .diagram .pele-state[class*="Path"] { --path-border: var(--role-process); }
69
+ .diagram .pele-state[class*="Path"] > :is(rect,path,circle,ellipse,polygon) { stroke: var(--role-process) !important; }
70
+ a:focus-visible,button:focus-visible,.diagram:focus-visible { outline: 2px solid var(--accent); outline-offset: 4px; }
71
+ noscript { color: var(--page-muted); }
72
+ @media(max-width:640px) {
73
+ main { padding: 24px 14px; }
74
+ .page-heading { flex-direction: column; gap: 8px; }
75
+ section { padding: 16px 10px; }
76
+ h2 { font-size: 18px; }
77
+ }
@@ -0,0 +1,68 @@
1
+ # Pele 展示层
2
+
3
+ Pele 只负责把 Mermaid 画成 SVG。模型决定要解释什么、采用哪个视角和保留哪些关系;源码、运行结果与上游检查结果提供事实依据。展示层不重新判断这些事实。
4
+
5
+ ## 生成单文件 HTML
6
+
7
+ ```bash
8
+ node <本 skill 目录>/scripts/render-html.mjs \
9
+ --title "取消与恢复的实现" \
10
+ --out /绝对路径/说明.html \
11
+ /绝对路径/全局视图.mmd \
12
+ /绝对路径/调用顺序.mmd \
13
+ /绝对路径/状态与缺陷.mmd
14
+ ```
15
+
16
+ 仅传入本次解释需要的图,不要求凑齐示例中的三种视角。输入与产物放在目标源码仓库之外,除非用户明确要求保存到仓库。
17
+
18
+ 脚本使用 `pele@1.12.0`,首次通过 npm 安装到用户缓存目录的 `fugue-show-me/pele-1.12.0`,遵循 `XDG_CACHE_HOME`,默认在 `~/.cache` 下。缓存独立于 `statechart`;不读取上游缓存、导入上游私有模块或依赖上游 PNG 渲染实现。
19
+
20
+ 所有输入先调用 `render()` 检查并生成 SVG,成功后才写 HTML;错误会使命令失败,不生成仅剩下载入口的页面。HTML 同时内嵌 SVG、Mermaid 源码和 Pele 的浏览器包。浏览器通过 `mount()` 使用实际字体和容器宽度布局,缩放、拖动、复位及键盘操作复用 Pele 自带能力。
21
+
22
+ 打开后查看每张图,而不是仅检查命令成功。浏览器发生绘图错误时,Pele 会在图旁显示错误;这仍是不完整的交付,需要修复并重新检查。
23
+
24
+ ## 主题与颜色含义
25
+
26
+ 使用 Pele 官方站点的 Flexoki 浅色与深色配色:浅色为暖白 `#fffcf0`,深色为墨黑 `#100f0f`。页面内提供切换按钮;命令加 `--theme dark` 可指定初始深色,默认浅色。颜色通过同一份 CSS 变量切换,不重新布局图,也不改变缩放位置。
27
+
28
+ | 信息角色 | Mermaid 类名 | 色系 |
29
+ | --- | --- | --- |
30
+ | 输入、上下文与事实 | `fugueInput` | 青色 |
31
+ | 程序处理步骤 | `fugueProcess` | 蓝色 |
32
+ | 判断、确认与等待 | `fugueDecision` | 黄色 |
33
+ | 外部系统交互 | `fugueExternal` | 紫色 |
34
+ | 完成与目标达成 | `fugueSuccess` | 绿色 |
35
+ | 失败与阻断 | `fugueFailure` | 红色 |
36
+ | 跳过与未执行 | `fugueSkipped` | 灰色 |
37
+
38
+ 流程图用标准 Mermaid 类标注,类的主题样式由本层 HTML 提供:
39
+
40
+ ```mermaid
41
+ flowchart LR
42
+ input[任务事实]:::fugueInput --> check{来源是否完整}:::fugueDecision
43
+ check -->|完整| run[执行处理]:::fugueProcess
44
+ check -->|缺失| stop[失败并保留事实]:::fugueFailure
45
+ run --> done[交付结果]:::fugueSuccess
46
+ ```
47
+
48
+ 状态图在展示副本中增加 `class s0 fugueInput` 这样的颜色角色即可,保留原 `class`、状态、边、编号和规则标记。已标记的错误或修复状态保持原有结果配色;缺陷路径继续使用蓝框及原有线宽。不得为了颜色修改模型或把失败状态改成通过。
49
+
50
+ 时序图用标准 `box rgba(...) 分组名` 区分参与方,透明填色能与浅、深背景叠合;分组名写明职责。需要突出某段交互时可使用 `rect rgba(...)`。采用上表的对应色系,不按参与方顺序随机分色。
51
+
52
+ 页面图例只列出图中实际使用的颜色角色。颜色增强信息分组,文字标签继续承载含义。两种主题均需检查实际节点填色、标签和连线,测量文字与其真实背景的对比度。
53
+
54
+ 配色来源:[官方站点 Flexoki 主题定义](https://github.com/obsidianmd/pele/blob/bdbad5df4dea13b9a14a83a92aed62c4769e49f5/website/src/styles/pele-theme.scss)。
55
+
56
+ ## 集成到已有 HTML
57
+
58
+ 可以调整生成页面的布局、说明和 CSS,保留每张图的容器、源码数据与初始化脚本。也可按以下原则自行组装:
59
+
60
+ - 使用 Pele 的公开 `render()` / `mount()` API;浏览器单文件使用 `pele/global` 导出的包并内嵌它,不依赖 CDN。
61
+ - 把 SVG 放在相应说明旁。多张图各用独立的 `idPrefix`,避免箭头、渐变、标题和描述的 ID 冲突。
62
+ - 图示 CSS 位于 `assets/diagram.css`,生成时内嵌到 HTML。用 `--pele-bg`、`--pele-fg`、`--pele-muted`、`--pele-line`、`--pele-surface`、`--pele-surface-alt`、`--pele-border` 等 CSS 变量匹配宿主页面。源图的 `classDef` 可能表达错误、修复和缺陷路径,不为统一配色覆盖这些语义。
63
+ - 用 `mount()` 读取实际中文字体并随容器宽度重排;需要缩放时使用它的 `zoom` 选项,不自行实现图布局或缩放系统。仍然过密的图应先拆分视角,而不是继续缩小文字。
64
+ - 内嵌源码、标题和说明时正确转义 HTML 与脚本结束标签。图中禁用外部图片以保持离线显示,保留 Pele 的许可文本。
65
+
66
+ Pele 使用自己的布局与主题;Mermaid 的 `themeVariables` 不生效,任意 HTML 标签也不受支持。遇到不支持的语法,按原含义调整或拆图后重新验证;状态与缺陷路径的含义必须保持,不静默改用另一套渲染器。
67
+
68
+ 官方依据:[API](https://pele.run/api)、[主题](https://pele.run/theming)、[兼容性](https://pele.run/compatibility)、[安全与嵌入](https://pele.run/security)。
@@ -0,0 +1,116 @@
1
+ import { spawnSync } from 'node:child_process';
2
+ import { existsSync } from 'node:fs';
3
+ import { mkdir, mkdtemp, readFile, rename, rm, writeFile } from 'node:fs/promises';
4
+ import { createRequire } from 'node:module';
5
+ import { homedir } from 'node:os';
6
+ import { basename, dirname, extname, join, resolve } from 'node:path';
7
+ import { pathToFileURL } from 'node:url';
8
+ import { parseArgs } from 'node:util';
9
+
10
+ const version = '1.12.0';
11
+ const fontFamily = "-apple-system, BlinkMacSystemFont, 'Segoe UI', 'PingFang SC', 'Microsoft YaHei', sans-serif";
12
+ const html = text => text.replace(/[&<>"']/g, char => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' })[char]);
13
+ const json = value => JSON.stringify(value).replace(/</g, '\\u003c');
14
+ const roles = [
15
+ ['fugueInput', '输入事实'], ['fugueProcess', '处理步骤'], ['fugueDecision', '判断与等待'],
16
+ ['fugueExternal', '外部交互'], ['fugueSuccess', '完成'], ['fugueFailure', '失败与阻断'], ['fugueSkipped', '跳过'],
17
+ ];
18
+
19
+ async function loadPele() {
20
+ const cache = join(process.env.XDG_CACHE_HOME || join(homedir(), '.cache'), 'fugue-show-me', `pele-${version}`);
21
+ if (!existsSync(join(cache, 'node_modules', 'pele', 'package.json'))) {
22
+ await mkdir(dirname(cache), { recursive: true });
23
+ const staging = await mkdtemp(`${cache}-`);
24
+ try {
25
+ await writeFile(join(staging, 'package.json'), '{"private":true}\n');
26
+ console.error(`首次安装 pele@${version},缓存位置:${cache}`);
27
+ const result = spawnSync('npm', ['install', '--no-audit', '--no-fund', '--ignore-scripts', '--save-exact', `pele@${version}`], {
28
+ cwd: staging,
29
+ encoding: 'utf8',
30
+ env: { ...process.env, PATH: `${dirname(process.execPath)}${process.platform === 'win32' ? ';' : ':'}${process.env.PATH || ''}` },
31
+ shell: process.platform === 'win32',
32
+ });
33
+ if (result.status !== 0) throw new Error(result.error?.message || result.stderr || 'Pele 安装失败');
34
+ try { await rename(staging, cache); }
35
+ catch (error) { if (!existsSync(join(cache, 'node_modules', 'pele', 'package.json'))) throw error; }
36
+ } finally {
37
+ await rm(staging, { recursive: true, force: true });
38
+ }
39
+ }
40
+ const require = createRequire(join(cache, 'package.json'));
41
+ const { render } = await import(pathToFileURL(require.resolve('pele')).href);
42
+ const bundle = await readFile(require.resolve('pele/global'), 'utf8');
43
+ const license = await readFile(join(dirname(require.resolve('pele/package.json')), 'LICENSE'), 'utf8');
44
+ return { render, bundle, license };
45
+ }
46
+
47
+ async function main() {
48
+ const { values, positionals } = parseArgs({ options: {
49
+ out: { type: 'string', short: 'o' }, title: { type: 'string' }, theme: { type: 'string', default: 'light' }, help: { type: 'boolean', short: 'h' },
50
+ }, allowPositionals: true });
51
+ const usage = '用法:node render-html.mjs --out 说明.html [--title 标题] [--theme light|dark] 总览.mmd [细节.mmd ...]';
52
+ if (values.help) { console.log(usage); return; }
53
+ if (!values.out || !positionals.length) throw new Error(usage);
54
+ if (!['light', 'dark'].includes(values.theme)) throw new Error('主题必须是 light 或 dark');
55
+ const sources = await Promise.all(positionals.map(async file => ({
56
+ title: basename(file, extname(file)), source: await readFile(file, 'utf8'),
57
+ })));
58
+ const { render, bundle, license } = await loadPele();
59
+ const diagrams = sources.map((diagram, index) => {
60
+ const id = `diagram-${index + 1}`;
61
+ try {
62
+ const { svg } = render(diagram.source, { idPrefix: id, fontFamily, images: false });
63
+ return { ...diagram, id, svg };
64
+ } catch (error) { throw new Error(`${positionals[index]}:${error.message}`, { cause: error }); }
65
+ });
66
+ const title = values.title || '图解说明';
67
+ const css = await readFile(new URL('../assets/diagram.css', import.meta.url), 'utf8');
68
+ const nodeClasses = diagrams.flatMap(({ svg }) => [...svg.matchAll(/<g\b[^>]*\bclass="([^"]*)"/g)].map(match => match[1].split(/\s+/)))
69
+ .filter(classes => classes.includes('pele-node') && !classes.some(name => /wrong|fixed|mixed|after|worse/i.test(name)));
70
+ const usedRoles = roles.filter(([name]) => nodeClasses.some(classes => classes.includes(name)));
71
+ const legend = usedRoles.length ? `<ul class="legend" aria-label="颜色含义">${usedRoles.map(([name, label]) => `<li class="${name}"><i aria-hidden="true"></i>${label}</li>`).join('')}</ul>` : '';
72
+ const sections = diagrams.map(({ title, id, svg }) => `<section aria-labelledby="${id}-heading">
73
+ <h2 id="${id}-heading">${html(title)}</h2>
74
+ <div class="diagram" id="${id}" role="region" aria-label="${html(title)}">${svg}</div>
75
+ </section>`).join('\n');
76
+ const page = `<!doctype html>
77
+ <html lang="zh-CN" data-theme="${values.theme}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
78
+ <title>${html(title)}</title><style>
79
+ :root { --font: ${fontFamily}; }
80
+ ${css}
81
+ </style></head><body><main>
82
+ <header><div class="page-heading"><h1>${html(title)}</h1><div class="theme-controls" role="group" aria-label="页面主题"><button type="button" data-theme-option="light" aria-pressed="${values.theme === 'light'}">浅色</button><button type="button" data-theme-option="dark" aria-pressed="${values.theme === 'dark'}">深色</button></div></div><p>先看全局,再查看需要的细节。图内可缩放、拖动和复位;聚焦图后也可使用 +、−、0 和方向键。</p></header>
83
+ <nav aria-label="图示目录">${diagrams.map(({ id, title }) => `<a href="#${id}-heading">${html(title)}</a>`).join('')}</nav>
84
+ ${legend}
85
+ ${sections}
86
+ <noscript>当前展示静态图;启用 JavaScript 后可使用自适应布局和缩放。</noscript>
87
+ </main>
88
+ <script id="diagram-sources" type="application/json">${json(diagrams.map(({ id, source }) => ({ id, source })))}</script>
89
+ <script>/* Pele ${version}\n${license.replace(/\*\//g, '* /')}\n*/\n${bundle.replace(/<\/script/gi, '<\\/script')}</script>
90
+ <script>
91
+ for (const button of document.querySelectorAll('[data-theme-option]')) {
92
+ button.addEventListener('click', () => {
93
+ document.documentElement.dataset.theme = button.dataset.themeOption;
94
+ for (const option of document.querySelectorAll('[data-theme-option]')) {
95
+ option.setAttribute('aria-pressed', String(option === button));
96
+ }
97
+ });
98
+ }
99
+ for (const { id, source } of JSON.parse(document.getElementById('diagram-sources').textContent)) {
100
+ const container = document.getElementById(id);
101
+ pele.mount(container, source, {
102
+ idPrefix: id,
103
+ images: false,
104
+ zoom: { always: true, labels: { zoomIn: '放大', zoomOut: '缩小', reset: '显示全图' } },
105
+ onRender() { container.dataset.rendered = 'true'; delete container.dataset.renderError; },
106
+ onError(error) { container.dataset.renderError = String(error); },
107
+ });
108
+ }
109
+ </script></body></html>`;
110
+ const output = resolve(values.out);
111
+ await mkdir(dirname(output), { recursive: true });
112
+ await writeFile(output, page);
113
+ console.log(`已生成 ${diagrams.length} 张图:${output}`);
114
+ }
115
+
116
+ main().catch(error => { console.error(error.message); process.exitCode = 1; });
@@ -0,0 +1,128 @@
1
+ ---
2
+ name: show-me
3
+ description: Help the user understand the current topic visually with concise diagrams, code-shape sketches, and focused HTML artifacts.
4
+ disable-model-invocation: true
5
+ ---
6
+
7
+ Help the user understand the current topic of conversation visually. Skip the preamble and keep prose brief. Pick the smallest view that makes the key point clear.
8
+
9
+ - Show logic or an algorithm as pseudocode:
10
+
11
+ ```text
12
+ on(save)
13
+ if content is unchanged
14
+ return cached result
15
+ write new content
16
+ return fresh result
17
+ ```
18
+
19
+ - Show runtime control flow as a call tree:
20
+
21
+ ```text
22
+ submitForm
23
+ createSession
24
+ persistPrompt
25
+ launchAgent
26
+ navigateToSession
27
+ ```
28
+
29
+ - Show UI structure as a component tree, including state and module boundaries that matter:
30
+
31
+ ```tsx
32
+ <SessionPage> (apps/example/src/routes/session.tsx)
33
+ useSessionEvents()
34
+ <SessionToolbar>
35
+ <RunSkillButton> (packages/ui)
36
+ ```
37
+
38
+ - Show file responsibility or a broad refactor as a shallow file tree:
39
+
40
+ ```text
41
+ src/
42
+ ├── commands/ # parses user actions
43
+ ├── sessions/ # owns session state
44
+ └── transport/ # sends API requests
45
+ ```
46
+
47
+ - Show component interaction, control flow, or data flow with Mermaid:
48
+
49
+ ```mermaid
50
+ sequenceDiagram
51
+ participant User
52
+ participant UI
53
+ participant Daemon
54
+ User->>UI: choose command
55
+ UI->>Daemon: send expanded prompt
56
+ Daemon-->>UI: stream result
57
+ ```
58
+
59
+ - Use `diff` when the point is what changes and the surrounding shape already exists. Match the diff shape to the topic.
60
+
61
+ For a component change:
62
+
63
+ ```diff
64
+ <SessionPage>
65
+ useSessionEvents()
66
+ <SessionToolbar>
67
+ + <RunSkillButton />
68
+ <SessionTimeline>
69
+ + <SkillResultCard />
70
+ ```
71
+
72
+ For a file-layout change:
73
+
74
+ ```diff
75
+ src/
76
+ ├── commands/
77
+ +│ └── show-me.ts # expands the slash command
78
+ ├── sessions/
79
+ -└── transport.ts
80
+ +└── transport/
81
+ + ├── client.ts
82
+ + └── stream.ts
83
+ ```
84
+
85
+ For a call-tree or call-stack change:
86
+
87
+ ```diff
88
+ submitForm
89
+ createSession
90
+ persistPrompt
91
+ + expandSkillMention
92
+ launchAgent
93
+ - navigateToSession
94
+ + navigateToSession
95
+ + subscribeToEvents
96
+ ```
97
+
98
+ For a state or control-flow change:
99
+
100
+ ```diff
101
+ on(save)
102
+ - write content
103
+ + if content is unchanged
104
+ + return cached result
105
+ + write new content
106
+ + invalidate cache
107
+ ```
108
+
109
+ - Show the whole block when most of it is new, when omitted context would hide ownership or order, or when the user needs a copyable target shape:
110
+
111
+ ```ts
112
+ function expandSkill(command: string): string {
113
+ const skillName = command.slice(1)
114
+ return `use the ${skillName} skill`
115
+ }
116
+ ```
117
+
118
+ - For a visual UI, layout, state comparison, or concept too dense for Mermaid, write one focused HTML file — a diagram, an infographic, or a short slide deck, whichever fits the point. Match the product's colors, type, spacing, and components; use real labels and data; support desktop and mobile. Then open it for the user:
119
+
120
+ ```
121
+ Bash(open path/to/show-me-{description}.html)
122
+ ```
123
+
124
+ ### guidance
125
+
126
+ Place each visual next to the short text it supports. Keep only the calls, files, props, states, and boundaries needed to answer the user's current question or the options to resolve the current discussion point.
127
+
128
+ You may use one of these, you may use several, it is unlikely you will use all of them. Use your judgement and don't overwhelm the user.
@@ -0,0 +1,2 @@
1
+ policy:
2
+ allow_implicit_invocation: false