@cyber-dash-tech/revela 0.8.7 → 0.8.9

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.md CHANGED
@@ -20,8 +20,8 @@ Enable it for the current session, assign a presentation task, and the agent can
20
20
  - injects a presentation-specific system prompt into your current agent with `/revela enable`
21
21
  - builds that prompt from 3 layers: core skill, active domain, active design
22
22
  - supports workspace document discovery, transparent text extraction for `.pdf`, `.docx`, `.pptx`, and `.xlsx`, and cached embedded-material extraction for those formats
23
- - uses workspace `DECKS.json` as machine-readable deck memory, slide spec, and prewrite readiness state
24
- - blocks premature writes to `decks/*.html` until the active deck is marked structurally ready
23
+ - keeps track of deck context, slide structure, sources, and readiness across the current workspace
24
+ - checks for missing context, weak evidence, and incomplete structure before writing `decks/*.html`
25
25
  - runs fast design compliance checks whenever the agent writes, patches, or edits `decks/*.html`
26
26
  - opens a visual comment editor for existing decks so users can Ctrl/Cmd-click elements and send precise edit requests back to OpenCode
27
27
  - exports finished decks to PDF and editable PPTX
@@ -89,7 +89,7 @@ Enable Revela in the current session:
89
89
  /revela enable
90
90
  ```
91
91
 
92
- Initialize workspace deck memory when starting in a new project:
92
+ Prepare the workspace when starting a new deck project:
93
93
 
94
94
  ```text
95
95
  /revela init
@@ -109,7 +109,7 @@ Then give the agent a deck task:
109
109
  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.
110
110
  ```
111
111
 
112
- Before the agent writes `decks/humanoid-robotics.html`, it must update `DECKS.json` through the `revela-decks` tool with the active deck, confirmed slide specs, layouts, components, and computed `writeReadiness.status: ready`. You can ask for an explicit readiness check at any time:
112
+ If the task depends on sources, research, or a confirmed slide plan, you can ask Revela to review whether the deck has enough context, structure, and evidence before writing:
113
113
 
114
114
  ```text
115
115
  /revela review
@@ -137,8 +137,8 @@ Disable presentation mode when done:
137
137
  /revela enable enable presentation mode for this session
138
138
  /revela disable disable presentation mode
139
139
 
140
- /revela init initialize or refresh workspace DECKS.json
141
- /revela review review current deck readiness before writing HTML
140
+ /revela init prepare the workspace for a deck project
141
+ /revela review check whether context, structure, and evidence are ready
142
142
  /revela remember <text> save an explicit user/workflow preference
143
143
  /revela edit open visual editor for the only deck in decks/
144
144
 
@@ -178,56 +178,22 @@ The enabled or disabled state is session-level only.
178
178
 
179
179
  ---
180
180
 
181
- ## DECKS.json Memory And Readiness Gate
181
+ ## Recommended Workflow
182
182
 
183
- Revela uses a workspace-root `DECKS.json` file for cross-session continuity and deck production control. It is intended for tools and the LLM, not manual editing.
183
+ Use Revela as a guided deck-production mode:
184
184
 
185
- It has two jobs:
185
+ 1. Enable Revela with `/revela enable`.
186
+ 2. Run `/revela init` when starting in a new project or when the workspace has changed significantly.
187
+ 3. Choose a design or domain if the default style is not right.
188
+ 4. Give the agent a clear deck task: audience, goal, language, number of slides, source requirements, and output path.
189
+ 5. Use `/revela review` before writing if the deck needs research, citations, or a confirmed slide plan.
190
+ 6. Let the agent write the HTML deck under `decks/`.
191
+ 7. Use `/revela edit` for visual comments and targeted revisions.
192
+ 8. Export with `/revela pdf <file>` or `/revela pptx <file>`.
186
193
 
187
- - workspace memory: stable project context, source materials, explicit user preferences, deck history, and open questions
188
- - active deck spec: current deck output path, prerequisites, research plan, per-slide content, layouts, components, evidence, visuals, blockers, and write readiness
194
+ `/revela review` checks for practical readiness problems: unclear audience, missing source material, unfinished research, unsupported claims, weak source trace, incomplete slide structure, missing design/layout information, or lightweight narrative issues such as weak so-what, missing risk/assumption handling, and abrupt transitions. It does not write the final deck.
189
195
 
190
- `DECKS.json` is the source of truth for workspace memory and deck readiness.
191
-
192
- Create or refresh it with:
193
-
194
- ```text
195
- /revela init
196
- ```
197
-
198
- Review the current deck state with:
199
-
200
- ```text
201
- /revela review
202
- ```
203
-
204
- `/revela review` does not write the final HTML deck. It reads and updates `DECKS.json` through the `revela-decks` tool, checks what is missing, and sets `writeReadiness.status` to `ready` only when the deck is ready to generate.
205
-
206
- Minimum readiness conditions:
207
-
208
- - topic, audience, language, visual style/design, and slide plan are decided
209
- - source materials are identified or explicitly deemed unnecessary
210
- - research need is assessed
211
- - needed research findings have been read and reflected in the slide specs
212
- - the user has confirmed the slide plan
213
- - required design layouts and components have been fetched
214
- - every slide has a title, layout, components, and structured content
215
- - no blockers remain
216
-
217
- The plugin enforces this before deck HTML is written. A write or patch touching `decks/*.html` is allowed only when the matching deck in `DECKS.json` passes the readiness gate. Direct writes or patches to `DECKS.json` are blocked; use `revela-decks` instead.
218
-
219
- The gate checks:
220
-
221
- - `writeReadiness.status` is `ready`
222
- - `writeReadiness.blockers` is empty
223
- - the deck `outputPath` exactly matches the target `decks/*.html` path
224
- - all `requiredInputs` booleans are true
225
- - every slide has title, layout, components, and structured content
226
- - every needed research axis is `done`, `read`, or `skipped`
227
-
228
- If the gate blocks a write, Revela writes a marker file under `.opencode/revela/blocked-writes/` instead of creating or overwriting the deck HTML. This makes the failure visible to the agent while keeping the real deck file untouched.
229
-
230
- For `apply_patch`, Revela only checks whether the patch touches `decks/*.html`. If not ready, the whole patch is replaced with a blocked marker patch. The `edit` tool is not gated.
196
+ If Revela blocks a deck write, ask the agent to run `/revela review`, resolve the reported gaps, and try again. This protects the deck file from being overwritten before the plan, evidence, and structure are ready.
231
197
 
232
198
  To remember long-term preferences, use:
233
199
 
@@ -235,7 +201,7 @@ To remember long-term preferences, use:
235
201
  /revela remember Prefer concise Chinese consulting-style decks.
236
202
  ```
237
203
 
238
- Do not use `remember` for temporary checklist state; temporary state belongs in the active deck spec in `DECKS.json`.
204
+ Do not use `remember` for temporary checklist state; use it only for durable user or workflow preferences.
239
205
 
240
206
  ---
241
207
 
@@ -604,7 +570,7 @@ The editor opens in your browser. Use `Ctrl`/`Cmd` + click to reference deck ele
604
570
 
605
571
  LLM tool equivalent: `revela-edit` with no target. This lets the agent open the same editor when you say things like “I want to edit the deck”.
606
572
 
607
- `/revela edit` prepares minimal `DECKS.json` state for the existing HTML deck if needed, so the normal deck write gate can still protect `decks/*.html` while allowing targeted edits.
573
+ For existing decks, `/revela edit` prepares whatever minimal project context is needed so targeted edits can still use the normal safety checks.
608
574
 
609
575
  ---
610
576
 
package/README.zh-CN.md CHANGED
@@ -20,8 +20,8 @@ Revela 是一个 [OpenCode](https://opencode.ai) 插件,可以把你当前使
20
20
  - 通过 `/revela enable` 向当前 agent 注入演示文稿专用 system prompt
21
21
  - prompt 由 3 层组成:核心 skill、当前 domain、当前 design
22
22
  - 支持工作区文档扫描,以及 `.pdf`、`.docx`、`.pptx`、`.xlsx` 的透明文本提取和嵌入素材缓存提取
23
- - 使用工作区 `DECKS.json` 保存机器可读的 deck 记忆、逐页规格和写入前 readiness 状态
24
- - active deck 结构化 ready 前,阻止过早写入 `decks/*.html`
23
+ - 在当前工作区持续跟踪 deck 背景、页面结构、来源材料和 readiness
24
+ - 写入 `decks/*.html` 前检查是否缺上下文、证据或结构
25
25
  - agent 每次写入、patch 或 edit `decks/*.html` 时自动执行快速 design compliance 检查
26
26
  - 为已有 deck 打开可视化评论编辑器,用户可以 Ctrl/Cmd + 点击元素,并把精确修改意见发回 OpenCode
27
27
  - 支持导出成 PDF 和可编辑 PPTX
@@ -88,7 +88,7 @@ export { default } from "/absolute/path/to/revela/index.ts";
88
88
  /revela enable
89
89
  ```
90
90
 
91
- 在新项目里可以先初始化工作区 deck 状态:
91
+ 在新项目里可以先准备工作区:
92
92
 
93
93
  ```text
94
94
  /revela init
@@ -108,7 +108,7 @@ export { default } from "/absolute/path/to/revela/index.ts";
108
108
  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.
109
109
  ```
110
110
 
111
- agent 写入 `decks/humanoid-robotics.html` 之前,它必须通过 `revela-decks` 工具更新 `DECKS.json`:记录 active deck、已确认的逐页规格、layout、component,并由工具计算出 `writeReadiness.status: ready`。你也可以随时显式触发 readiness 检查:
111
+ 如果任务依赖来源材料、调研或已确认的 slide plan,可以先让 Revela 检查上下文、结构和证据是否足够:
112
112
 
113
113
  ```text
114
114
  /revela review
@@ -136,8 +136,8 @@ Create a 6-slide HTML deck on humanoid robotics supply chains. Cite the main mar
136
136
  /revela enable 为当前会话启用演示文稿模式
137
137
  /revela disable 关闭演示文稿模式
138
138
 
139
- /revela init 初始化或刷新工作区 DECKS.json
140
- /revela review HTML 前检查当前 deck readiness
139
+ /revela init 为当前 deck 项目准备工作区
140
+ /revela review 检查上下文、结构和证据是否 ready
141
141
  /revela remember <text> 保存明确的用户/工作流偏好
142
142
  /revela edit 打开 decks/ 下唯一 deck 的可视化编辑器
143
143
 
@@ -177,56 +177,22 @@ Create a 6-slide HTML deck on humanoid robotics supply chains. Cite the main mar
177
177
 
178
178
  ---
179
179
 
180
- ## DECKS.json 记忆与写入门禁
180
+ ## 推荐使用流程
181
181
 
182
- Revela 使用工作区根目录的 `DECKS.json` 做跨会话记忆和 deck 生产控制。它主要给工具和 LLM 使用,不建议人工直接编辑。
182
+ Revela 当成一个有检查环节的 deck 生产模式:
183
183
 
184
- 它有两个职责:
184
+ 1. 用 `/revela enable` 启用 Revela。
185
+ 2. 新项目或工作区明显变化时,运行 `/revela init`。
186
+ 3. 如果默认风格不合适,先选择 design 或 domain。
187
+ 4. 给 agent 一个清楚的 deck 任务:受众、目标、语言、页数、来源要求和输出路径。
188
+ 5. 如果 deck 需要调研、引用或已确认的 slide plan,写作前先运行 `/revela review`。
189
+ 6. 让 agent 把 HTML deck 写到 `decks/` 下。
190
+ 7. 用 `/revela edit` 做可视化评论和精准修改。
191
+ 8. 用 `/revela pdf <file>` 或 `/revela pptx <file>` 导出。
185
192
 
186
- - 工作区记忆:稳定项目背景、源材料、明确用户偏好、历史 deck 和开放问题
187
- - active deck 规格:当前 deck 输出路径、前置条件、research plan、逐页内容、layout、component、证据、视觉需求、blocker 和 write readiness
193
+ `/revela review` 检查的是实际生产问题:受众是否清楚、是否缺来源材料、调研是否完成、关键 claim 是否有证据、source trace 是否太弱、页面结构是否完整、design/layout 信息是否齐全,以及轻量叙事问题,例如 so-what 不清晰、缺少风险/假设处理或转场突兀。它不会写最终 deck
188
194
 
189
- `DECKS.json` 是工作区记忆和 deck readiness source of truth。
190
-
191
- 创建或刷新:
192
-
193
- ```text
194
- /revela init
195
- ```
196
-
197
- 检查当前 deck 状态:
198
-
199
- ```text
200
- /revela review
201
- ```
202
-
203
- `/revela review` 不会写最终 HTML deck。它通过 `revela-decks` 工具读取并更新 `DECKS.json`,检查缺失项,并且只有在 deck 真的可以生成时才把 `writeReadiness.status` 设为 `ready`。
204
-
205
- 最小 readiness 条件:
206
-
207
- - topic、audience、language、visual style/design 和 slide plan 已确定
208
- - source materials 已识别,或明确不需要源材料
209
- - research need 已评估
210
- - 需要调研时,相关 findings 已读取并反映到逐页规格中
211
- - 用户已确认 slide plan
212
- - 需要的 design layouts 和 components 已获取
213
- - 每页都有 title、layout、components 和结构化 content
214
- - 没有 unresolved blockers
215
-
216
- 插件会在写 deck HTML 前强制检查这些条件。任何触碰 `decks/*.html` 的 `write` 或 `apply_patch`,只有在 `DECKS.json` 里匹配的 deck 通过 readiness gate 后才允许执行。直接写入或 patch `DECKS.json` 会被阻止;必须使用 `revela-decks` 工具更新状态。
217
-
218
- 门禁会检查:
219
-
220
- - `writeReadiness.status` 是 `ready`
221
- - `writeReadiness.blockers` 为空
222
- - deck `outputPath` 精确匹配目标 `decks/*.html` 路径
223
- - 所有 `requiredInputs` 布尔值都是 true
224
- - 每页都有 title、layout、components 和结构化 content
225
- - 每个 needed research axis 都是 `done`、`read` 或 `skipped`
226
-
227
- 如果门禁阻止写入,Revela 会在 `.opencode/revela/blocked-writes/` 下写一个 marker 文件,而不是创建或覆盖真实 HTML deck。这样 agent 能看到失败原因,同时真实 deck 文件不会被污染。
228
-
229
- 对 `apply_patch`,Revela 只检查 patch 是否触碰 `decks/*.html`。如果未 ready,整个 patch 会被替换成 blocked marker patch。`edit` 工具不做门禁。
195
+ 如果 Revela 阻止写入 deck,直接让 agent 运行 `/revela review`,根据报告补齐缺口后再写。这样可以避免在计划、证据或结构还不完整时覆盖真实 deck 文件。
230
196
 
231
197
  记住长期偏好请使用:
232
198
 
@@ -234,7 +200,7 @@ Revela 使用工作区根目录的 `DECKS.json` 做跨会话记忆和 deck 生
234
200
  /revela remember 我偏好中文、咨询风格、每页只表达一个核心观点。
235
201
  ```
236
202
 
237
- 不要用 `remember` 保存临时 checklist 状态;临时状态应该保存在 `DECKS.json` 的 active deck spec 中。
203
+ 不要用 `remember` 保存临时 checklist 状态;它只适合保存长期用户偏好或工作流偏好。
238
204
 
239
205
  ---
240
206
 
@@ -569,7 +535,7 @@ Revela 0.8 中 `/revela edit` 不接受 target。如果 `decks/` 下正好有一
569
535
 
570
536
  对应的 LLM tool:`revela-edit`,不需要 target。因此当你说“我要编辑这个 deck”时,agent 也可以主动打开同一个编辑器。
571
537
 
572
- 如果已有 HTML deck 缺少 `DECKS.json` 状态,`/revela edit` 会自动准备最小 deck state,让正常的 `decks/*.html` 写入门禁仍然生效,同时允许后续精准修改。
538
+ 对于已有 HTML deck,`/revela edit` 会自动准备必要的最小项目上下文,让后续精准修改仍然经过正常安全检查。
573
539
 
574
540
  ---
575
541
 
@@ -29,7 +29,7 @@ Given a research brief specifying your topic and axis, you will:
29
29
  2. Use \`DECKS.json\` through \`revela-decks\` as the workspace material index when it exists
30
30
  3. Run a lightweight workspace freshness check when needed
31
31
  4. Search the web for current data, reports, and case studies when the brief requires it
32
- 5. Write all findings to ONE structured file: \`researches/{topic-key}/{axis-name}.md\`
32
+ 5. Write all findings to ONE structured file: \`researches/{topic-key}/{axis-name}.md\` with source trace detailed enough for slide-level evidence mapping
33
33
  6. Return a brief summary of what you found
34
34
 
35
35
  ---
@@ -112,18 +112,21 @@ Use **\`revela-research-save\`** to write ONE file with all your findings.
112
112
  - \`content\`: structured findings using the four sections below
113
113
  - \`sources\`: list of all URLs and filenames used
114
114
 
115
+ The primary agent will map your findings into \`DECKS.json\` slide-level evidence.
116
+ Preserve compact source trace so it can do that without rediscovering sources.
117
+
115
118
  ### Findings file format
116
119
 
117
120
  Use these four sections — omit any that are empty:
118
121
 
119
122
  \`\`\`markdown
120
123
  ## Data
121
- - {stat or finding} [Source: {url or filename}]
122
- - {stat or finding} [Source: {url or filename}]
124
+ - {stat or finding} [Source: {url or filename}; Location: {page/slide/sheet/section if known}; Quote: "{short exact snippet if available}"; Caveat: {scope/uncertainty if relevant}]
125
+ - {stat or finding} [Source: {url or filename}; Location: {page/slide/sheet/section if known}; Quote: "{short exact snippet if available}"; Caveat: {scope/uncertainty if relevant}]
123
126
  (5–10 items, most argument-worthy only)
124
127
 
125
128
  ## Cases
126
- - **{Company/Entity}**: {1–2 sentence profile with key metrics} [Source: {url}]
129
+ - **{Company/Entity}**: {1–2 sentence profile with key metrics} [Source: {url or filename}; Location: {page/slide/sheet/section if known}; Caveat: {scope/uncertainty if relevant}]
127
130
  (2–4 entries max)
128
131
 
129
132
  ## Images
@@ -136,6 +139,10 @@ Use these four sections — omit any that are empty:
136
139
 
137
140
  Content rules:
138
141
  - Every data point MUST have inline source attribution: \`[Source: {url}]\` or \`[Source: AI knowledge — verify]\` or \`[Source: {filename}]\`
142
+ - For workspace documents, identify the original filename and available page, slide, sheet, or section location. Do not cite only the extracted summary.
143
+ - When extracted materials were used, include \`extractedTextPath\` or \`extractedManifestPath\` when useful for traceability.
144
+ - Preserve compact direct snippets or quotes when available. Do not invent quotes, page references, locations, URLs, or caveats.
145
+ - Include caveats or scope limitations for estimates, rankings, market sizes, forecasts, and conflicting sources.
139
146
  - Preserve raw numbers and direct quotes — do not summarize prematurely
140
147
  - Use tables for comparative data when 3+ entities are compared
141
148
  - Include publication dates where available
@@ -170,6 +177,7 @@ Gaps:
170
177
  - **Always** call \`revela-extract-document-materials\` for every selected workspace file before deciding which extracted materials to read next
171
178
  - **Avoid** repeated extraction or deep reading for files that are clearly irrelevant to this axis
172
179
  - **Always** include source attribution on every data point
180
+ - **Always** preserve source trace: URL or filename, location when available, compact quote/snippet when available, and caveat/scope where relevant
173
181
  - **Always** use tables for comparative data (more useful than bullets for presentations)
174
182
  - **Preserve** raw data — the primary agent will select what to include in slides
175
183
  - **Note** data freshness — include publication dates where available
@@ -48,8 +48,10 @@ Workflow:
48
48
  6. Before extracting or deeply reading a selected document, check \`DECKS.json.workspace.sourceMaterials\`. If the same path has the same fingerprint and valid extraction paths, reuse those paths instead of repeating extraction.
49
49
  7. Read only the materials needed to form a conservative workspace memory. Do not exhaustively read every file if the workspace is large.
50
50
  8. If this conversation or the workspace contains a concrete deck task or an existing deck artifact, call \`revela-decks\` with action \`upsertDeck\` and later \`upsertSlides\` for explicit deck information. Do not pass or ask for a deck key; the tool uses the workspace folder name internally. Do not mark readiness ready during init.
51
- 9. When adopting an existing HTML deck, analyze the artifact and create one conservative \`SlideSpec\` per identifiable slide/page. The \`SlideSpec[]\` itself is the worklist; do not create a separate target slide count.
52
- 10. Report what was initialized or updated and list any open questions.
51
+ 9. When adopting an existing HTML deck, analyze the artifact and create one conservative \`SlideSpec\` per identifiable slide/page. Record only visible source notes or explicit source information as evidence; do not infer original evidence that is not present in the artifact.
52
+ 10. When a read or extracted source material clearly supports a specific slide claim, you may attach compact evidence fields such as \`sourcePath\`, \`location\`, \`extractedTextPath\`, or \`extractedManifestPath\`. Attach extraction cache paths only when they support that specific claim, not to every slide by default.
53
+ 11. Treat \`workspace.sourceMaterials\` as a reusable candidate index, not proof by itself. A source material record alone is not slide evidence.
54
+ 12. Report what was initialized or updated and list any open questions.
53
55
 
54
56
  Memory rules:
55
57
  - Only write facts supported by workspace files into ${DECKS_STATE_FILE} workspace state, source materials, deck memory, and open questions.
@@ -18,7 +18,8 @@ Goal:
18
18
  - Preserve the deck spec for future sessions: every slide's content, layout, components, evidence, visuals, and production status.
19
19
  - Do not write, patch, or directly edit ${DECKS_STATE_FILE}. Use the \`revela-decks\` tool for all state changes.
20
20
  - Let \`revela-decks\` action \`review\` compute writeReadiness; do not manually set readiness to ready.
21
- - Treat this as an evidence-readiness review, not only a checklist review: unsupported numbers, market sizing, recommendations, competitor comparisons, technical assertions, or investment conclusions should be made visible before writing.
21
+ - Treat this as an evidence and lightweight narrative readiness review, not only a checklist review: unsupported numbers, market sizing, recommendations, competitor comparisons, technical assertions, investment conclusions, weak so-what, missing risk/assumption handling, or abrupt narrative transitions should be made visible before writing.
22
+ - Treat source trace mapping as part of evidence readiness: when research findings have been read, relevant findings should appear in slide-level \`slides[].evidence[]\` records rather than only in raw research files.
22
23
 
23
24
  Current state:
24
25
  - ${state}
@@ -33,19 +34,24 @@ Workspace boundary rules:
33
34
  Workflow:
34
35
  1. Call \`revela-decks\` with action \`read\` for the current workspace deck.
35
36
  2. If no current deck exists but the conversation contains enough deck context, call \`revela-decks\` action \`upsertDeck\` with goal, outputPath, theme, requiredInputs, and researchPlan. Do not invent or ask for a deck key; the tool uses the workspace folder name internally.
36
- 3. If a user-confirmed slide plan is available, call \`revela-decks\` action \`upsertSlides\` with every slide's title, purpose, layout, components, structured content, evidence, visuals, and status.
37
- 4. Only set requiredInputs fields true when explicit conversation state, files read, research findings read, selected design, fetched layouts/components, or user confirmation supports them. Do not infer completion.
38
- 5. Call \`revela-decks\` action \`review\`. The tool computes and writes \`writeReadiness\` plus structured readiness issues for the current workspace deck.
39
- 6. Briefly report whether the deck is ready. If blocked, list the exact blockers returned by the tool. If warnings exist, list them after blockers as residual risks.
37
+ 3. If \`researchPlan[].status\` is \`done\` or \`read\` and \`researchPlan[].findingsFile\` exists, verify that evidence-sensitive slide claims are backed by compact \`slides[].evidence[]\` records that reference the relevant findings file or source material where known.
38
+ 4. If a user-confirmed slide plan is available, call \`revela-decks\` action \`upsertSlides\` with every slide's title, purpose, narrativeRole, layout, components, structured content, evidence, visuals, and status. Use only lightweight narrativeRole values that are clear from the plan: \`context\`, \`tension\`, \`evidence\`, \`recommendation\`, \`risk\`, \`ask\`, \`appendix\`, or \`close\`.
39
+ 5. Prefer evidence records with \`findingsFile\`, \`sourcePath\`, \`location\`, \`quote\`, \`url\`, \`caveat\`, \`extractedTextPath\`, or \`extractedManifestPath\` when those fields are known from research files or extracted workspace materials.
40
+ 6. Do not invent quotes, page references, locations, URLs, caveats, or extraction paths. If source trace is missing, preserve the blocker or warning and report exactly what trace is needed.
41
+ 7. Only set requiredInputs fields true when explicit conversation state, files read, research findings read, selected design, fetched layouts/components, or user confirmation supports them. Do not infer completion.
42
+ 8. Call \`revela-decks\` action \`review\`. The tool computes and writes \`writeReadiness\` plus structured readiness issues for the current workspace deck.
43
+ 9. Briefly report whether the deck is ready. If blocked, list the exact blockers returned by the tool. If warnings exist, list them after blockers as residual risks; separate evidence/source warnings from narrative warnings when possible.
40
44
 
41
45
  Minimum conditions for \`ready\`:
42
46
  - Topic, audience, slide count, language, and visual style/design are decided.
43
47
  - Source materials have been identified or explicitly deemed unnecessary.
44
48
  - Research need has been assessed.
45
49
  - If research is needed, all relevant findings have been read and reflected in the slide specs.
50
+ - Read or done research findings are mapped into \`slides[].evidence[]\` where they support evidence-sensitive slide claims.
46
51
  - The user has confirmed the slide plan.
47
52
  - ${DECKS_STATE_FILE} contains per-slide specs with content, layout, components, and evidence where applicable.
48
- - Evidence-sensitive slide claims have compact evidence references. Numeric claims and strong recommendations should not be unsupported.
53
+ - Evidence-sensitive slide claims have compact evidence references with source trace where available. Numeric claims and strong recommendations should not be unsupported or source-only when trace exists.
54
+ - Multi-slide decision decks have a practical narrative flow: context/tension before evidence, recommendations after support, risk or assumption handling when recommending action, and a clear so-what or ask at the end. Narrative gaps are normally warnings, not hard blockers.
49
55
  - The needed design layouts and components have been fetched with \`revela-designs read\`.
50
56
  - No unresolved blockers remain.
51
57
 
@@ -53,7 +59,9 @@ Report format:
53
59
  - Start with \`Ready: yes/no\`.
54
60
  - If blocked, list each blocker with slide index/title when the tool provides it, the issue type, and the suggested next action.
55
61
  - If warnings exist but the deck is otherwise ready, say the deck can be written but note the residual risks.
62
+ - Report \`narrative_gap\` warnings as story-structure risks such as weak so-what, missing risk/assumption handling, conclusion before support, missing audience framing, or abrupt transition.
56
63
  - Do not invent evidence or silently downgrade blockers. Use the tool result as authoritative.
64
+ - When reporting weak evidence, say whether the missing trace is \`findingsFile\`, \`sourcePath\`, \`location\`, \`quote\`, \`url\`, or \`caveat\` if that is clear from the reviewed materials.
57
65
 
58
66
  Rules:
59
67
  - Do not write or overwrite \`decks/*.html\` during review.
@@ -6,6 +6,7 @@ export const DECKS_STATE_FILE = "DECKS.json"
6
6
  export type DeckProductionStatus = "planning" | "blocked" | "ready" | "written"
7
7
  export type SlideProductionStatus = "planned" | "ready" | "written" | "qa_passed" | "qa_failed"
8
8
  export type WriteReadinessStatus = "blocked" | "ready" | "written"
9
+ export type NarrativeRole = "context" | "tension" | "evidence" | "recommendation" | "risk" | "ask" | "appendix" | "close"
9
10
 
10
11
  export interface DecksState {
11
12
  version: 1
@@ -96,6 +97,7 @@ export interface SlideSpec {
96
97
  index: number
97
98
  title: string
98
99
  purpose?: string
100
+ narrativeRole?: NarrativeRole
99
101
  layout: string
100
102
  qa?: boolean
101
103
  components: string[]
@@ -117,6 +119,12 @@ export interface EvidenceRef {
117
119
  quote?: string
118
120
  page?: string
119
121
  url?: string
122
+ sourcePath?: string
123
+ location?: string
124
+ findingsFile?: string
125
+ caveat?: string
126
+ extractedTextPath?: string
127
+ extractedManifestPath?: string
120
128
  }
121
129
 
122
130
  export interface VisualBrief {
@@ -153,6 +161,7 @@ export type ReadinessIssueType =
153
161
  | "missing_evidence"
154
162
  | "weak_evidence"
155
163
  | "source_not_processed"
164
+ | "narrative_gap"
156
165
 
157
166
  export interface ReadinessIssue {
158
167
  type: ReadinessIssueType
@@ -164,6 +173,8 @@ export interface ReadinessIssue {
164
173
  claimText?: string
165
174
  }
166
175
 
176
+ const SOURCE_TRACE_ACTION = "Add slide evidence with source plus source trace such as findingsFile or sourcePath, and quote, location, url, or caveat where available; otherwise reframe the claim as an explicit assumption/opinion."
177
+
167
178
  export function decksStatePath(workspaceRoot: string): string {
168
179
  return join(workspaceRoot, DECKS_STATE_FILE)
169
180
  }
@@ -432,14 +443,58 @@ export function buildDecksStatePromptLayer(workspaceRoot: string, maxChars = 140
432
443
  const compact = {
433
444
  sourceOfTruth: DECKS_STATE_FILE,
434
445
  activeDeck: activeKey,
435
- workspace: state.workspace,
436
- deck: active,
446
+ workspace: compactWorkspaceForPrompt(state.workspace),
447
+ deck: active ? compactDeckForPrompt(active) : undefined,
437
448
  }
438
449
  let text = JSON.stringify(compact, null, 2)
439
450
  if (text.length > maxChars) text = text.slice(0, maxChars).trimEnd() + "\n[DECKS.json state truncated for prompt size.]"
440
451
  return `---\n\n# Revela Workspace State From ${DECKS_STATE_FILE}\n\n\`\`\`json\n${text}\n\`\`\`\n\nRules for this state layer:\n- Treat ${DECKS_STATE_FILE} as the source of truth for the single current deck's specs, slide plan, and write readiness.\n- The decks map is compatibility storage; operate only on the current workspace deck.\n- Do not edit ${DECKS_STATE_FILE} directly; use the revela-decks tool.\n- Before writing decks/*.html, the current deck must have writeReadiness.status=ready and a complete slide spec, and its outputPath must match the target file.`
441
452
  }
442
453
 
454
+ function compactWorkspaceForPrompt(workspace: DecksState["workspace"]): DecksState["workspace"] {
455
+ return {
456
+ brief: truncatePromptText(workspace.brief),
457
+ sourceMaterials: workspace.sourceMaterials.map((source) => ({
458
+ ...source,
459
+ summary: truncatePromptText(source.summary),
460
+ bestUsedFor: truncatePromptText(source.bestUsedFor),
461
+ })),
462
+ preferences: workspace.preferences,
463
+ deckMemory: workspace.deckMemory,
464
+ openQuestions: workspace.openQuestions.map((question) => truncatePromptText(question)).filter(Boolean) as string[],
465
+ }
466
+ }
467
+
468
+ function compactDeckForPrompt(deck: DeckSpec): DeckSpec {
469
+ return {
470
+ ...deck,
471
+ slides: deck.slides.map((slide) => ({
472
+ ...slide,
473
+ content: {
474
+ ...slide.content,
475
+ speakerNotes: truncatePromptText(slide.content.speakerNotes),
476
+ },
477
+ evidence: slide.evidence.map(compactEvidenceForPrompt),
478
+ notes: truncatePromptText(slide.notes),
479
+ })),
480
+ }
481
+ }
482
+
483
+ function compactEvidenceForPrompt(evidence: EvidenceRef): EvidenceRef {
484
+ return {
485
+ ...evidence,
486
+ source: truncatePromptText(evidence.source, 180) ?? evidence.source,
487
+ quote: truncatePromptText(evidence.quote, 320),
488
+ caveat: truncatePromptText(evidence.caveat, 220),
489
+ }
490
+ }
491
+
492
+ function truncatePromptText(text: string | undefined, maxLength = 400): string | undefined {
493
+ if (!text) return undefined
494
+ if (text.length <= maxLength) return text
495
+ return `${text.slice(0, maxLength).trimEnd()}... [truncated]`
496
+ }
497
+
443
498
  function normalizeDecksState(input: DecksState): DecksState {
444
499
  const state: DecksState = {
445
500
  version: 1,
@@ -515,19 +570,21 @@ function computeDeckReadinessIssues(deck: DeckSpec, workspace: DecksState["works
515
570
  issues.push(blockerIssue(
516
571
  "missing_evidence",
517
572
  `Slide ${slide.index} has an evidence-sensitive claim without evidence: ${claim}`,
518
- "Add a compact evidence reference to slides[].evidence or reframe the claim as an explicit assumption/opinion.",
573
+ SOURCE_TRACE_ACTION,
519
574
  { ...slideRef, claimText: claim },
520
575
  ))
521
576
  } else if (claim && slide.evidence.some((item) => !hasEvidenceDetail(item))) {
522
577
  issues.push(warningIssue(
523
578
  "weak_evidence",
524
- `Slide ${slide.index} evidence for a high-risk claim has no quote, page, or URL detail: ${claim}`,
525
- "Add quote/page/url details where available so the writing agent can ground the slide more reliably.",
579
+ `Slide ${slide.index} evidence for a high-risk claim has no source trace detail: ${claim}`,
580
+ "Add source trace detail to this evidence record: findingsFile or sourcePath plus quote, location, url, or caveat where available so the writing agent can ground the slide reliably.",
526
581
  { ...slideRef, claimText: claim },
527
582
  ))
528
583
  }
529
584
  }
530
585
 
586
+ issues.push(...computeNarrativeReadinessIssues(deck))
587
+
531
588
  for (const axis of deck.researchPlan) {
532
589
  if (axis.needed && axis.status !== "done" && axis.status !== "read" && axis.status !== "skipped") {
533
590
  issues.push(blockerIssue(
@@ -560,6 +617,89 @@ function computeDeckReadinessIssues(deck: DeckSpec, workspace: DecksState["works
560
617
  return issues
561
618
  }
562
619
 
620
+ function computeNarrativeReadinessIssues(deck: DeckSpec): ReadinessIssue[] {
621
+ const issues: ReadinessIssue[] = []
622
+ const slides = deck.slides.filter((slide) => slide.index > 0).sort((a, b) => a.index - b.index)
623
+ if (slides.length === 0) return issues
624
+
625
+ if (slides.length >= 4 && slides.every((slide) => !slide.narrativeRole)) {
626
+ issues.push(warningIssue(
627
+ "narrative_gap",
628
+ "No slide narrativeRole values are recorded for a multi-slide deck",
629
+ "Add lightweight narrativeRole values such as context, tension, evidence, recommendation, risk, ask, appendix, or close to improve story-structure review.",
630
+ ))
631
+ }
632
+
633
+ if (slides.length >= 4 && deck.audience?.trim() && slides.every(hasWeakNarrativePurpose)) {
634
+ issues.push(warningIssue(
635
+ "narrative_gap",
636
+ `Slide purposes do not clearly frame the story for the audience: ${deck.audience}`,
637
+ "Rewrite slide purpose fields to explain what this audience should understand, believe, decide, or do after each slide.",
638
+ ))
639
+ }
640
+
641
+ const firstRecommendationIndex = slides.findIndex(isRecommendationSlide)
642
+ if (firstRecommendationIndex >= 0) {
643
+ const recommendation = slides[firstRecommendationIndex]
644
+ const priorSlides = slides.slice(0, firstRecommendationIndex)
645
+ const earlyBoundary = Math.max(1, Math.ceil(slides.length * 0.3))
646
+ if (firstRecommendationIndex < earlyBoundary && !priorSlides.some(hasEvidenceOrTensionRole)) {
647
+ issues.push(warningIssue(
648
+ "narrative_gap",
649
+ `Slide ${recommendation.index} presents a recommendation before context, tension, or evidence has been established`,
650
+ "Consider moving the recommendation later or adding preceding context, tension, or evidence slides so the conclusion does not arrive before support.",
651
+ { slideIndex: recommendation.index, slideTitle: recommendation.title },
652
+ ))
653
+ }
654
+
655
+ if (!slides.some(hasRiskOrAssumptionHandling)) {
656
+ issues.push(warningIssue(
657
+ "narrative_gap",
658
+ "Recommendation has no visible risk, assumption, caveat, or tradeoff handling",
659
+ "Add a risk/assumption/tradeoff slide or make the relevant caveats explicit before writing a decision-oriented recommendation deck.",
660
+ { slideIndex: recommendation.index, slideTitle: recommendation.title },
661
+ ))
662
+ }
663
+ }
664
+
665
+ if (slides.length >= 4 && !hasClearEnding(slides)) {
666
+ const last = slides[slides.length - 1]
667
+ issues.push(warningIssue(
668
+ "narrative_gap",
669
+ "Deck may end without a clear so-what, ask, or closing takeaway",
670
+ "Use the final slide or final section to state the decision, action request, recommendation, or closing takeaway explicitly.",
671
+ { slideIndex: last.index, slideTitle: last.title },
672
+ ))
673
+ }
674
+
675
+ const firstAskIndex = slides.findIndex(isAskSlide)
676
+ if (firstAskIndex === 0 && slides.length > 2) {
677
+ const ask = slides[firstAskIndex]
678
+ issues.push(warningIssue(
679
+ "narrative_gap",
680
+ `Slide ${ask.index} asks for action before the deck has established the case`,
681
+ "Consider moving the ask later or opening with context before requesting a decision or action.",
682
+ { slideIndex: ask.index, slideTitle: ask.title },
683
+ ))
684
+ } else if (firstAskIndex > 0) {
685
+ const contextIndex = slides.findIndex((slide) => slide.narrativeRole === "context")
686
+ if (contextIndex >= 0 && contextIndex < firstAskIndex) {
687
+ const bridgeSlides = slides.slice(contextIndex + 1, firstAskIndex)
688
+ if (!bridgeSlides.some((slide) => hasEvidenceOrTensionRole(slide) || isRecommendationSlide(slide))) {
689
+ const ask = slides[firstAskIndex]
690
+ issues.push(warningIssue(
691
+ "narrative_gap",
692
+ `Slide ${ask.index} jumps from context to ask without evidence, tension, or recommendation in between`,
693
+ "Add an evidence, tension, or recommendation bridge before the ask so the narrative transition is easier to follow.",
694
+ { slideIndex: ask.index, slideTitle: ask.title },
695
+ ))
696
+ }
697
+ }
698
+ }
699
+
700
+ return issues
701
+ }
702
+
563
703
  function blockerIssue(type: ReadinessIssueType, message: string, suggestedAction: string, extra: Partial<ReadinessIssue> = {}): ReadinessIssue {
564
704
  return { type, severity: "blocker", message, suggestedAction, ...extra }
565
705
  }
@@ -582,6 +722,46 @@ function findEvidenceSensitiveClaim(slide: SlideSpec): string | undefined {
582
722
  return candidates.find(isEvidenceSensitiveClaim)
583
723
  }
584
724
 
725
+ function isRecommendationSlide(slide: SlideSpec): boolean {
726
+ return slide.narrativeRole === "recommendation" || /\b(recommend(?:ation|ed)?|should|must|go\/?no-go)\b|建议|必须/.test(slideSearchText(slide))
727
+ }
728
+
729
+ function isAskSlide(slide: SlideSpec): boolean {
730
+ return slide.narrativeRole === "ask" || /\b(ask|decision|approve|approval|next step|action required|call to action)\b|请求|决策|批准|下一步|行动/.test(slideSearchText(slide))
731
+ }
732
+
733
+ function hasEvidenceOrTensionRole(slide: SlideSpec): boolean {
734
+ return slide.narrativeRole === "evidence" || slide.narrativeRole === "tension"
735
+ }
736
+
737
+ function hasRiskOrAssumptionHandling(slide: SlideSpec): boolean {
738
+ return slide.narrativeRole === "risk" || /\b(risk|assumption|caveat|trade-?off|constraint|limitation|uncertainty)\b|风险|假设|取舍|限制|不确定|前提/.test(slideSearchText(slide))
739
+ }
740
+
741
+ function hasWeakNarrativePurpose(slide: SlideSpec): boolean {
742
+ const purpose = slide.purpose?.trim().toLowerCase()
743
+ if (!purpose) return true
744
+ return /^(explain|show|introduce|present|describe|overview|clarify)\b/.test(purpose) || /^(说明|展示|介绍|呈现|概述)/.test(purpose)
745
+ }
746
+
747
+ function hasClearEnding(slides: SlideSpec[]): boolean {
748
+ const finalSlides = slides.slice(-2)
749
+ return finalSlides.some((slide) => slide.narrativeRole === "recommendation" || slide.narrativeRole === "ask" || slide.narrativeRole === "close" || /\b(so what|takeaway|recommend(?:ation)?|decision|ask|next step|conclusion|close)\b|结论|建议|决策|请求|下一步|收尾|总结/.test(slideSearchText(slide)))
750
+ }
751
+
752
+ function slideSearchText(slide: SlideSpec): string {
753
+ return [
754
+ slide.title,
755
+ slide.purpose,
756
+ slide.content?.headline,
757
+ ...(slide.content?.body ?? []),
758
+ ...(slide.content?.bullets ?? []),
759
+ ]
760
+ .filter((item): item is string => Boolean(item))
761
+ .join("\n")
762
+ .toLowerCase()
763
+ }
764
+
585
765
  function isEvidenceSensitiveClaim(text: string): boolean {
586
766
  const normalized = text.toLowerCase()
587
767
  return hasNumericClaim(normalized) || EVIDENCE_SENSITIVE_TERMS.some((pattern) => pattern.test(normalized))
@@ -592,7 +772,15 @@ function hasNumericClaim(text: string): boolean {
592
772
  }
593
773
 
594
774
  function hasEvidenceDetail(evidence: EvidenceRef): boolean {
595
- return Boolean(evidence.quote?.trim() || evidence.page?.trim() || evidence.url?.trim())
775
+ return Boolean(
776
+ evidence.quote?.trim() ||
777
+ evidence.page?.trim() ||
778
+ evidence.location?.trim() ||
779
+ evidence.url?.trim() ||
780
+ evidence.findingsFile?.trim() ||
781
+ evidence.sourcePath?.trim() ||
782
+ evidence.extractedTextPath?.trim()
783
+ )
596
784
  }
597
785
 
598
786
  const EVIDENCE_SENSITIVE_TERMS = [
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cyber-dash-tech/revela",
3
- "version": "0.8.7",
3
+ "version": "0.8.9",
4
4
  "description": "OpenCode plugin that turns AI into an HTML slide deck generator",
5
5
  "type": "module",
6
6
  "main": "./index.ts",
package/skill/SKILL.md CHANGED
@@ -292,15 +292,17 @@ slide plan to the user **before writing any HTML**.
292
292
 
293
293
  Format the plan as a markdown table:
294
294
 
295
- | # | Title | Content Summary | Layout | Components |
296
- |---|-------|-----------------|--------|------------|
297
- | 1 | Cover | Topic title, subtitle, presenter, date | `cover` | `gradient-text`, `deco-blob`, `accent-line` |
298
- | 2 | Table of Contents | 5 chapter headings | `toc` | `toc-list` |
299
- | 3 | Market Background | Key problem, 3 pain points, $4.2B TAM | `two-col` | `evidence-list`, `card` |
300
- | 4 | Key Metrics | Growth 85%, TAM $12B, NPS 72 | `stats` | `stat-card ×3`, `gradient-text` |
295
+ | # | Title | Narrative Role | Content Summary | Layout | Components |
296
+ |---|-------|----------------|-----------------|--------|------------|
297
+ | 1 | Cover | `context` | Topic title, subtitle, presenter, date | `cover` | `gradient-text`, `deco-blob`, `accent-line` |
298
+ | 2 | Table of Contents | `context` | 5 chapter headings | `toc` | `toc-list` |
299
+ | 3 | Market Background | `tension` | Key problem, 3 pain points, $4.2B TAM | `two-col` | `evidence-list`, `card` |
300
+ | 4 | Key Metrics | `evidence` | Growth 85%, TAM $12B, NPS 72 | `stats` | `stat-card ×3`, `gradient-text` |
301
301
 
302
302
  Rules for filling the table:
303
303
  - **Layout**: use the exact layout name from the Layout Index (e.g. `cover`, `two-col`, `card-grid`, `stats`)
304
+ - **Narrative Role**: use one lightweight role when clear: `context`, `tension`, `evidence`,
305
+ `recommendation`, `risk`, `ask`, `appendix`, or `close`
304
306
  - **Components**: list component names from the Component Index — no CSS details
305
307
  (e.g. `card ×3`, `stat-card`, `evidence-list`, `step-flow`, `quote-block`)
306
308
  - **Content Summary**: 1 sentence of actual content — specific numbers, key points, or
@@ -319,7 +321,7 @@ Then ask:
319
321
 
320
322
  After the user confirms the slide plan, update `DECKS.json` through `revela-decks`:
321
323
  - Call `upsertDeck` to mark completed `requiredInputs` only when explicitly satisfied.
322
- - Call `upsertSlides` with the confirmed per-slide content, layout, components, and evidence.
324
+ - Call `upsertSlides` with the confirmed per-slide content, narrativeRole, layout, components, and evidence.
323
325
  - Keep write readiness blocked until Phase 5 calls `revela-decks review` and the tool returns ready.
324
326
 
325
327
  ---
package/tools/decks.ts CHANGED
@@ -75,6 +75,7 @@ export default tool({
75
75
  index: tool.schema.number().describe("1-based slide index."),
76
76
  title: tool.schema.string().describe("Slide title."),
77
77
  purpose: tool.schema.string().optional().describe("Narrative purpose of this slide."),
78
+ narrativeRole: tool.schema.enum(["context", "tension", "evidence", "recommendation", "risk", "ask", "appendix", "close"]).optional().describe("Lightweight narrative role for review guidance."),
78
79
  layout: tool.schema.string().describe("Design layout name."),
79
80
  qa: tool.schema.boolean().optional().describe("Whether the slide is marked QA-relevant deck metadata."),
80
81
  components: tool.schema.array(tool.schema.string()).describe("Design components used by this slide."),
@@ -86,10 +87,16 @@ export default tool({
86
87
  }).describe("Structured slide content."),
87
88
  evidence: tool.schema.array(tool.schema.object({
88
89
  source: tool.schema.string().describe("Source file, URL, or research note."),
89
- quote: tool.schema.string().optional(),
90
- page: tool.schema.string().optional(),
91
- url: tool.schema.string().optional(),
92
- })).optional().describe("Evidence references for this slide."),
90
+ quote: tool.schema.string().optional().describe("Compact quote or snippet supporting the slide claim."),
91
+ page: tool.schema.string().optional().describe("Legacy page reference; prefer location for new page/slide/sheet/section references."),
92
+ url: tool.schema.string().optional().describe("Source URL when available."),
93
+ sourcePath: tool.schema.string().optional().describe("Workspace source file path when the evidence came from a local material."),
94
+ location: tool.schema.string().optional().describe("Generic page, slide, sheet, section, or other source location reference."),
95
+ findingsFile: tool.schema.string().optional().describe("researches/{topic}/{axis}.md findings file that records the supporting evidence."),
96
+ caveat: tool.schema.string().optional().describe("Scope, uncertainty, or limitation that should travel with this evidence."),
97
+ extractedTextPath: tool.schema.string().optional().describe("Reusable extracted text cache path when this evidence came from extracted materials."),
98
+ extractedManifestPath: tool.schema.string().optional().describe("Reusable extracted materials manifest path when available."),
99
+ })).optional().describe("Compact evidence references and source trace for this slide."),
93
100
  visuals: tool.schema.array(tool.schema.object({
94
101
  id: tool.schema.string().optional(),
95
102
  purpose: tool.schema.string().optional(),