@cyber-dash-tech/revela 0.1.16 → 0.2.0

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.
package/README.zh-CN.md CHANGED
@@ -2,35 +2,37 @@
2
2
 
3
3
  [English](README.md) | **中文**
4
4
 
5
- [![npm version](https://img.shields.io/npm/v/@cyber-dash-tech/revela)](https://www.npmjs.com/package/@cyber-dash-tech/revela) [![license](https://img.shields.io/npm/l/@cyber-dash-tech/revela)](LICENSE) [![tests](https://img.shields.io/badge/tests-110%20passing-brightgreen)](tests/) [![OpenCode plugin](https://img.shields.io/badge/OpenCode-plugin-blue)](https://opencode.ai) [![Bun](https://img.shields.io/badge/Bun-%E2%89%A51.0-orange)](https://bun.sh)
5
+ [![npm version](https://img.shields.io/npm/v/@cyber-dash-tech/revela)](https://www.npmjs.com/package/@cyber-dash-tech/revela) [![license](https://img.shields.io/npm/l/@cyber-dash-tech/revela)](LICENSE) [![tests](https://img.shields.io/badge/tests-109%20passing-brightgreen)](tests/) [![OpenCode plugin](https://img.shields.io/badge/OpenCode-plugin-blue)](https://opencode.ai) [![Bun](https://img.shields.io/badge/Bun-%E2%89%A51.0-orange)](https://bun.sh)
6
6
 
7
7
  <p align="center">
8
8
  <img src="assets/img/logo.png" alt="Revela" width="800" />
9
9
  </p>
10
10
 
11
11
  Revela 是一个 [OpenCode](https://opencode.ai) 插件,可以把你当前使用的 agent 变成 HTML 幻灯片生成器。
12
- 在当前会话中启用它之后,agent 可以完成调研、结构设计、HTML 写作和自动 QA,并把结果输出到 `decks/*.html`。
12
+ 在当前会话中启用之后,agent 可以完成调研、结构设计、HTML 写作、QA 和导出。
13
13
 
14
- **[在线演示 — AI 权力转移](https://cyber-dash-tech.github.io/revela/assets/html/ai-power-shift.html)** · 一份使用 Revela 生成的 5 页投资简报。
14
+ **[在线演示 — AI 权力转移](https://cyber-dash-tech.github.io/revela/assets/html/ai-power-shift.html)**
15
15
 
16
16
  ---
17
17
 
18
- ## Revela 是什么
18
+ ## 它能做什么
19
19
 
20
- Revela 是一种工作模式,不是一个独立聊天 agent
21
-
22
- - `/revela enable` 会把演示文稿生成专用的 system prompt 注入到当前 agent
20
+ - 通过 `/revela enable` 向当前 agent 注入演示文稿专用 system prompt
23
21
  - prompt 由 3 层组成:核心 skill、当前 domain、当前 design
24
- - agent 可以扫描工作区文件、委托网页调研、生成 HTML 幻灯片,并自动执行布局 QA
25
- - design domain 的切换都在本地完成,并会立即重建 active prompt
22
+ - 支持工作区文档扫描,以及 `.pdf`、`.docx`、`.pptx`、`.xlsx` 的透明文本提取
23
+ - agent 每次写入 `decks/*.html` 时自动执行布局 QA
24
+ - 支持导出成 PDF 和可编辑 PPTX
25
+ - design 和 domain 的切换都在本地完成,不消耗 LLM token
26
+
27
+ Revela 是一种工作模式,不是独立 agent。
26
28
 
27
29
  ---
28
30
 
29
31
  ## 环境要求
30
32
 
31
33
  - [OpenCode](https://opencode.ai)
32
- - Bun 运行时(`bun >= 1.0.0`)
33
- - [Google Chrome](https://www.google.com/chrome/) 或 Chromium,用于布局 QA PDF 导出
34
+ - Bun 运行时 `>= 1.0.0`
35
+ - [Google Chrome](https://www.google.com/chrome/) 或 Chromium,用于 QA、PDF 导出和 PPTX 导出
34
36
  - Git,用于源码安装
35
37
 
36
38
  ---
@@ -48,19 +50,13 @@ Revela 是一种工作模式,不是一个独立聊天 agent。
48
50
  }
49
51
  ```
50
52
 
51
- 重启 OpenCode 后,插件会通过 Bun 自动安装。
53
+ 然后重启 OpenCode
52
54
 
53
- 如果想全局安装,可以把同样的 `plugin` 配置写到 `~/.config/opencode/opencode.json`。
55
+ 如果想全局安装,可以把同样配置写到 `~/.config/opencode/opencode.json`。
54
56
 
55
57
  ### 本地 wrapper 安装
56
58
 
57
- 以下情况建议直接使用本地 wrapper
58
-
59
- - Bun 插件安装不稳定或被网络环境阻塞
60
- - 你在中国大陆网络环境下使用 OpenCode
61
- - 你希望直接运行本地源码仓库
62
-
63
- 源码安装方式:
59
+ 如果 Bun 安装被网络阻塞、不稳定,或者你想直接运行本地源码仓库,建议使用本地 wrapper
64
60
 
65
61
  ```bash
66
62
  git clone https://github.com/cyber-dash-tech/revela
@@ -74,41 +70,44 @@ npm install
74
70
  export { default } from "/absolute/path/to/revela/index.ts";
75
71
  ```
76
72
 
77
- 如果走本地 wrapper 方案,确保 `~/.config/opencode/opencode.json` 里不要同时保留 `@cyber-dash-tech/revela` `plugin` 配置,否则 OpenCode 启动时仍会尝试用 Bun 安装。
73
+ 如果使用本地 wrapper,记得把 `opencode.json` 里的 `@cyber-dash-tech/revela` `plugin` 配置删掉,否则 OpenCode 仍可能尝试用 Bun 安装。
78
74
 
79
75
  ### 中国大陆网络说明
80
76
 
81
- OpenCode 的 npm 插件安装依赖 Bun,而 Bun 可能不会遵循 npm mirror 配置。如果直接安装失败,优先使用上面的本地 wrapper 方案,或者先把包安装到 `~/.config/opencode/`,再手动创建本地插件入口文件。
77
+ OpenCode 的 npm 插件安装依赖 Bun,而 Bun 可能不会遵循 npm mirror 设置。如果直接安装失败,优先使用上面的本地 wrapper 方案。
82
78
 
83
79
  ---
84
80
 
85
81
  ## 快速开始
86
82
 
87
- 启动 OpenCode
83
+ 先在当前会话中启用 Revela
88
84
 
89
- ```bash
90
- opencode
85
+ ```text
86
+ /revela enable
91
87
  ```
92
88
 
93
- 在当前会话中启用 Revela
89
+ 如有需要,先切换 design 或 domain
94
90
 
95
91
  ```text
96
- /revela enable
92
+ /revela designs
93
+ /revela designs summit
94
+ /revela domains deeptech-investment
97
95
  ```
98
96
 
99
- 然后直接给 agent 一个幻灯片任务,例如:
97
+ 然后直接给 agent 一个 deck 任务:
100
98
 
101
99
  ```text
102
- Create a 6-slide HTML deck on humanoid robotics supply chains. Use the summit design, cite the main market drivers, and save the result to decks/humanoid-robotics.html.
100
+ Create a 6-slide HTML deck on humanoid robotics supply chains. Cite the main market drivers, use the active design faithfully, and save the result to decks/humanoid-robotics.html.
103
101
  ```
104
102
 
105
- 如果需要,把生成好的 HTML 导出为 PDF:
103
+ 需要导出时:
106
104
 
107
105
  ```text
108
106
  /revela pdf decks/humanoid-robotics.html
107
+ /revela pptx decks/humanoid-robotics.html
109
108
  ```
110
109
 
111
- 关闭 Revela,让当前 agent 回到普通模式:
110
+ 完成后关闭演示文稿模式:
112
111
 
113
112
  ```text
114
113
  /revela disable
@@ -134,6 +133,7 @@ Create a 6-slide HTML deck on humanoid robotics supply chains. Use the summit de
134
133
  /revela domains-rm <name> 删除已安装 domain
135
134
 
136
135
  /revela pdf <file> 将 HTML deck 导出为同目录 PDF
136
+ /revela pptx <file> 将 HTML deck 导出为同目录可编辑 PPTX
137
137
  ```
138
138
 
139
139
  所有 `/revela` 命令都在本地执行,不消耗 LLM token。
@@ -146,11 +146,12 @@ Create a 6-slide HTML deck on humanoid robotics supply chains. Use the summit de
146
146
 
147
147
  这份 prompt 由 3 层组成:
148
148
 
149
- 1. `skill/SKILL.md`:核心幻灯片生成流程
150
- 2. 当前 active domain:行业结构与术语
151
- 3. 当前 active design:视觉语言、layout、component 和图表规则
149
+ 1. `skill/SKILL.md` - 核心幻灯片生成流程
150
+ 2. 当前 active domain - 行业结构与术语
151
+ 3. 当前 active design - 视觉系统、layout、component 和图表规则
152
152
 
153
- 当前 design 和 domain 会持久化到 `~/.config/revela/config.json`。是否启用 Revela 则是会话级状态,不会跨会话持久化。
153
+ 持久化配置保存在 `~/.config/revela/config.json`。
154
+ 是否启用 Revela 则只在当前会话生效。
154
155
 
155
156
  ---
156
157
 
@@ -159,60 +160,54 @@ Create a 6-slide HTML deck on humanoid robotics supply chains. Use the summit de
159
160
  启用 Revela 后,agent 可以使用:
160
161
 
161
162
  - `revela-workspace-scan` 扫描工作区中的 PDF、Office 文件、CSV、Markdown 和文本文件
162
- - `revela-research` 子代理抓取目标网页,并把结构化结果保存到 `researches/<topic>/`
163
- - `revela-research-save` research axis 写入单个 findings 文件
163
+ - `revela-research` 子代理做定向网页调研
164
+ - `revela-research-save` 把结构化 findings 写入 `researches/<topic>/`
165
+
166
+ 支持提取文本的入口:
164
167
 
165
- 支持 `@` 引用和自动文本提取的文件类型:
168
+ - 在对话里 `@` 引用或直接粘贴文件
169
+ - 启用 Revela 后通过 `read` 工具访问文件
170
+
171
+ 支持提取的文件类型:
166
172
 
167
173
  - `.pdf`
168
174
  - `.docx`
169
175
  - `.pptx`
170
176
  - `.xlsx`
171
177
 
172
- Revela 会在主 agent 处理这些文件前,先透明地完成文本提取。
178
+ 这些提取过程对主 agent 是透明的。
173
179
 
174
180
  ---
175
181
 
176
182
  ## 布局 QA 与合规检查
177
183
 
178
- 每次 agent 写入 `decks/*.html` 时,Revela 都会自动在 `1920x1080` 分辨率下运行一轮基于 Puppeteer 的 QA。
179
- 报告会立刻反馈给 agent,用于继续修正布局或 design compliance 问题。
184
+ 每次 agent 写入 `decks/*.html` 时,Revela 都会自动在 `1920x1080` 下运行一轮基于 Puppeteer 的 QA。
185
+ 报告会立刻返回,便于 agent 继续修正。
180
186
 
181
- 当前 QA 维度如下:
187
+ 当前 QA 维度:
182
188
 
183
189
  | 维度 | 检查内容 |
184
190
  |---|---|
185
191
  | `overflow` | 元素是否超出 slide canvas |
186
- | `balance` | 是否过稀、重心偏移、底部留白过大等 |
192
+ | `balance` | 是否过稀、重心偏移、底部留白过大 |
187
193
  | `symmetry` | 并列列之间的高度或密度是否明显失衡 |
188
194
  | `rhythm` | 垂直堆叠元素之间的间距节奏是否不稳定 |
189
195
  | `compliance` | 是否使用了 active design 之外的 class 或新增 CSS 规则 |
190
196
 
191
- 每张 slide 都必须显式声明 `slide-qa="true"` 或 `slide-qa="false"`。
192
-
193
- - `slide-qa="true"`:适用于内容型页面,执行完整 QA
194
- - `slide-qa="false"`:适用于封面、目录、引用、总结、结尾等结构型页面
197
+ 每张 slide 都必须声明 `slide-qa="true"` 或 `slide-qa="false"`。
195
198
 
196
- `compliance` 不是软建议,而是生成流程的一部分。如果 agent 发明了 design 之外的 class 或 CSS rule,QA 会直接指出并要求修正。
199
+ - `slide-qa="true"` 适用于内容型页面,执行完整 QA
200
+ - `slide-qa="false"` 适用于封面、目录、引用、总结、结尾等结构型页面
197
201
 
198
202
  也可以手动调用 `revela-qa` 工具执行 QA。
199
203
 
200
204
  ---
201
205
 
202
- ## 内置 Designs
203
-
204
- 使用 `/revela designs <name>` 切换。
205
-
206
- | 名称 | 说明 | 预览 |
207
- |---|---|---|
208
- | `aurora` | 深色 executive 风格,信息密度更高,适合结构化商业表达和 ECharts 数据可视化 | ![aurora](assets/img/slide-example-aurora.jpg) |
209
- | `summit` | 年报式 editorial 风格,适合图像丰富、叙事感更强的商业表达 | ![summit](assets/img/slide-example-summit.jpg) |
210
-
211
- ---
206
+ ## Designs 与 Domains
212
207
 
213
- ## 内置 Domains
208
+ `/revela designs` 和 `/revela domains` 查看你当前环境里实际安装的内容。
214
209
 
215
- 使用 `/revela domains <name>` 切换。
210
+ 仓库内置的 domains
216
211
 
217
212
  | 名称 | 说明 |
218
213
  |---|---|
@@ -220,11 +215,18 @@ Revela 会在主 agent 处理这些文件前,先透明地完成文本提取。
220
215
  | `deeptech-investment` | VC / 投资分析:市场规模、技术成熟度、护城河与投资逻辑 |
221
216
  | `consulting` | 战略咨询:go/no-go 判断、战略设计与 belief-change 报告 |
222
217
 
218
+ 仓库中的 design 示例:
219
+
220
+ | 名称 | 说明 | 预览 |
221
+ |---|---|---|
222
+ | `summit` | 年报式 editorial 风格,适合图像更丰富、叙事感更强的商业表达 | ![summit](assets/img/slide-example-summit.jpg) |
223
+ | `monet` | 更轻、更安静的 serif 视觉系统,适合带有 art direction 气质的商业叙事 | 仓库内含 `DESIGN.md` |
224
+
223
225
  ---
224
226
 
225
227
  ## 自定义 Designs
226
228
 
227
- 自定义 design 是一个包含 `DESIGN.md` 的文件夹,文件头使用 frontmatter:
229
+ 自定义 design 是一个包含 `DESIGN.md` 的文件夹,并带有 frontmatter:
228
230
 
229
231
  ```yaml
230
232
  ---
@@ -235,58 +237,42 @@ version: 1.0.0
235
237
  ---
236
238
  ```
237
239
 
238
- 文件正文定义 agent 可使用的视觉系统。
239
-
240
- ### Marker 体系
241
-
242
- 对于较大的 design,建议使用当前 marker 格式:
240
+ 对于较大的 design,建议使用 marker 体系:
243
241
 
244
242
  ```html
245
243
  <!-- @design:foundation:start -->
246
- 色彩、字体、CSS 变量、HTML 外壳、基础 JS...
244
+ Foundation rules
247
245
  <!-- @design:foundation:end -->
248
246
 
249
247
  <!-- @design:rules:start -->
250
- 构图规则、正反案例、design 特定约束...
248
+ Design rules
251
249
  <!-- @design:rules:end -->
252
250
 
253
251
  <!-- @design:layouts:start -->
254
252
  <!-- @layout:cover:start qa=false -->
255
- Layout 详情...
253
+ Layout details
256
254
  <!-- @layout:cover:end -->
257
-
258
- <!-- @layout:two-col:start qa=true -->
259
- Layout 详情...
260
- <!-- @layout:two-col:end -->
261
255
  <!-- @design:layouts:end -->
262
256
 
263
257
  <!-- @design:components:start -->
264
258
  <!-- @component:card:start -->
265
- Component HTML + CSS...
259
+ Component details
266
260
  <!-- @component:card:end -->
267
-
268
- <!-- @component:stat-card:start -->
269
- Component HTML + CSS...
270
- <!-- @component:stat-card:end -->
271
261
  <!-- @design:components:end -->
272
262
 
273
263
  <!-- @design:chart-rules:start -->
274
- 图表规则...
264
+ Chart rules
275
265
  <!-- @design:chart-rules:end -->
276
266
  ```
277
267
 
278
- Prompt 注入行为如下:
268
+ Prompt 注入规则:
279
269
 
280
270
  - 常驻注入:`@design:foundation`、`@design:rules`、layout index、component index
281
271
  - 按需获取:单个 `@layout:*`、单个 `@component:*`、`@design:chart-rules`
282
272
 
283
273
  如果 design 没有 marker,Revela 会退回到整份 `DESIGN.md` 全量注入。
284
274
 
285
- ### design 作者的 compliance 说明
286
-
287
- Revela 会从 design 中提取允许使用的 CSS class vocabulary,并在 QA 的 compliance 维度里做校验。如果 agent 发明了新的 class 或 CSS rule,QA 会直接报出。
288
-
289
- ### 安装自定义 Design
275
+ 安装自定义 design
290
276
 
291
277
  ```text
292
278
  /revela designs-add github:your-org/your-design
@@ -298,31 +284,42 @@ Revela 会从 design 中提取允许使用的 CSS class vocabulary,并在 QA
298
284
 
299
285
  ## 自定义 Domains
300
286
 
301
- 自定义 domain 是一个包含 `INDUSTRY.md` 的文件夹,frontmatter 结构与 design 类似。
287
+ 自定义 domain 是一个包含 `INDUSTRY.md` 的文件夹。
302
288
 
303
289
  ```text
304
290
  /revela domains-add github:your-org/your-domain
305
291
  ```
306
292
 
307
- `INDUSTRY.md` 是为了兼容历史版本而保留的文件名。
293
+ `INDUSTRY.md` 是为兼容历史版本保留的文件名。
308
294
 
309
295
  ---
310
296
 
311
- ## PDF 导出
297
+ ## 导出
312
298
 
313
- 把生成好的 HTML deck 导出为 PDF:
299
+ PDF 导出:
314
300
 
315
301
  ```text
316
302
  /revela pdf decks/my-deck.html
317
303
  ```
318
304
 
319
- Revela 会通过 Chrome / Chromium 渲染每一页 slide,并在同目录生成最终 PDF。
305
+ 可编辑 PPTX 导出:
306
+
307
+ ```text
308
+ /revela pptx decks/my-deck.html
309
+ ```
310
+
311
+ 两种导出都会把结果写到源 HTML deck 同目录。
320
312
 
321
313
  ---
322
314
 
323
- ## 日志
315
+ ## 开发
316
+
317
+ ```bash
318
+ bun test
319
+ bun run typecheck
320
+ ```
324
321
 
325
- Revela 使用 [tslog](https://tslog.js.org/) 输出结构化日志。开启详细调试输出:
322
+ 开启详细日志:
326
323
 
327
324
  ```bash
328
325
  REVELA_DEBUG=1 opencode
@@ -332,4 +329,4 @@ REVELA_DEBUG=1 opencode
332
329
 
333
330
  ## 许可证
334
331
 
335
- MIT,详见 [LICENSE](LICENSE).
332
+ MIT - 见 [LICENSE](LICENSE)
@@ -34,6 +34,7 @@ export async function handleHelp(
34
34
  `\`/revela domains-add <url>\` — install a domain from URL / github:user/repo\n` +
35
35
  `\`/revela designs-rm <name>\` — remove an installed design\n` +
36
36
  `\`/revela domains-rm <name>\` — remove an installed domain\n` +
37
- `\`/revela pdf <file>\` — export HTML slide deck to PDF`
37
+ `\`/revela pdf <file>\` — export HTML slide deck to PDF\n` +
38
+ `\`/revela pptx <file>\` — export HTML slide deck to PPTX`
38
39
  )
39
40
  }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * lib/commands/pptx.ts
3
+ *
4
+ * Handler for `/revela pptx <file_path>` — exports an HTML slide deck to PPTX.
5
+ *
6
+ * Output: same directory and base name as the input, with .pptx extension.
7
+ * Example: decks/my-deck.html → decks/my-deck.pptx
8
+ */
9
+
10
+ import { resolve } from "path"
11
+ import { exportToPptx } from "../pptx/export"
12
+
13
+ function formatSecs(ms: number): string {
14
+ return `${(ms / 1000).toFixed(1)}s`
15
+ }
16
+
17
+ export async function handlePptx(
18
+ filePath: string,
19
+ send: (text: string) => Promise<void>,
20
+ ): Promise<void> {
21
+ if (!filePath) {
22
+ await send(
23
+ "**Usage:** `/revela pptx <file_path>`\n\n" +
24
+ "Example: `/revela pptx decks/my-deck.html`"
25
+ )
26
+ return
27
+ }
28
+
29
+ const abs = resolve(filePath)
30
+ await send(`Exporting \`${abs}\` to PPTX...`)
31
+
32
+ try {
33
+ let lastSlideUpdate = 0
34
+ let longDeckThreshold: number | null = null
35
+
36
+ const result = await exportToPptx(filePath, {
37
+ onProgress: async (progress) => {
38
+ if (progress.kind === "stage") {
39
+ await send(progress.message)
40
+ return
41
+ }
42
+
43
+ const current = progress.current ?? 0
44
+ const total = progress.total ?? 0
45
+ if (!total) return
46
+
47
+ if (longDeckThreshold === null) {
48
+ longDeckThreshold = total >= 8 ? (total > 20 ? 5 : 2) : -1
49
+ }
50
+ if (longDeckThreshold < 0) return
51
+
52
+ const shouldSend = current === 1 || current === total || current - lastSlideUpdate >= longDeckThreshold
53
+ if (!shouldSend) return
54
+
55
+ lastSlideUpdate = current
56
+ await send(`Editable export progress: slide ${current}/${total}`)
57
+ },
58
+ })
59
+
60
+ await send(
61
+ `**PPTX exported successfully**\n\n` +
62
+ `- Output: \`${result.outputPath}\`\n` +
63
+ `- Slides: ${result.slideCount}\n` +
64
+ `- Time: ${formatSecs(result.durationMs)}\n` +
65
+ `- Prepare: ${formatSecs(result.timingsMs.prepareMs)}\n` +
66
+ `- Page setup: ${formatSecs(result.timingsMs.pageSetupMs)}\n` +
67
+ `- Slide export: ${formatSecs(result.timingsMs.slideExportMs)}\n` +
68
+ `- Merge: ${formatSecs(result.timingsMs.mergeMs)}\n` +
69
+ `- Write: ${formatSecs(result.timingsMs.writeMs)}`
70
+ )
71
+ } catch (e) {
72
+ const msg = e instanceof Error ? e.message : String(e)
73
+ await send(`**PPTX export failed**\n\n\`\`\`\n${msg}\n\`\`\``)
74
+ }
75
+ }