@cyber-dash-tech/revela 0.1.2 → 0.1.4
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 +3 -4
- package/README.zh-CN.md +3 -4
- package/lib/agents/research-prompt.ts +7 -3
- package/lib/config.ts +1 -1
- package/lib/design/designs.ts +97 -7
- package/lib/log.ts +3 -2
- package/lib/prompt-builder.ts +29 -50
- package/lib/qa/checks.ts +6 -49
- package/lib/qa/measure.ts +8 -7
- package/package.json +1 -1
- package/plugin.ts +15 -14
- package/skill/SKILL.md +23 -198
- package/tools/designs.ts +21 -5
- package/tools/workspace-scan.ts +17 -2
- package/designs/default/DESIGN.md +0 -1100
- package/designs/editorial-ribbon/DESIGN.md +0 -1092
- package/designs/minimal/DESIGN.md +0 -1079
package/skill/SKILL.md
CHANGED
|
@@ -125,9 +125,10 @@ extracts text from binary formats (PDF, Excel, Word, PowerPoint) — just call
|
|
|
125
125
|
with `subagent_type: "revela-research"`) is available.** It is the primary
|
|
126
126
|
research workhorse — not an optional enhancement.
|
|
127
127
|
|
|
128
|
-
The research agent searches the web
|
|
129
|
-
|
|
130
|
-
file `researches/{topic-slug}/{axis-name}.md`
|
|
128
|
+
The research agent searches the web using `websearch` for broad discovery and
|
|
129
|
+
`webfetch` for depth on specific pages, reads workspace documents, and writes
|
|
130
|
+
structured findings to a single file `researches/{topic-slug}/{axis-name}.md`
|
|
131
|
+
in the workspace.
|
|
131
132
|
|
|
132
133
|
##### Parallelization Rule
|
|
133
134
|
|
|
@@ -136,8 +137,9 @@ Each axis gets its own dedicated agent with a focused brief. Launch ALL agents
|
|
|
136
137
|
in a single message (parallel Task tool calls).
|
|
137
138
|
|
|
138
139
|
**How to decompose:** Look at what the presentation needs to cover. Each major
|
|
139
|
-
entity, comparison dimension, or macro question is a separate axis.
|
|
140
|
-
|
|
140
|
+
entity, comparison dimension, or macro question is a separate axis. Decompose
|
|
141
|
+
based on topic breadth and the depth each axis warrants — a narrow topic may
|
|
142
|
+
need 2 axes; a complex comparison may need 4 or more. Typical decompositions:
|
|
141
143
|
|
|
142
144
|
| Topic type | Example axes |
|
|
143
145
|
|---|---|
|
|
@@ -146,38 +148,7 @@ decompositions:
|
|
|
146
148
|
| Investment thesis | Opportunity metrics, risk factors, comparable deals, macro trends |
|
|
147
149
|
| Product strategy | User research, competitor features, technology feasibility, go-to-market |
|
|
148
150
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
##### Invocation Example — Multi-Agent Parallel
|
|
152
|
-
|
|
153
|
-
For a topic like "Investment shift from OpenAI to Anthropic":
|
|
154
|
-
|
|
155
|
-
```
|
|
156
|
-
Agent 1 (Anthropic):
|
|
157
|
-
> Research Anthropic's trajectory for a presentation on AI investment shifts:
|
|
158
|
-
> - Funding history: Series rounds, valuations, key investors
|
|
159
|
-
> - Revenue and growth: run-rate, enterprise adoption, key products
|
|
160
|
-
> - Competitive advantages: safety positioning, developer tools, reliability
|
|
161
|
-
> Focus on 2025-2026 data. Write findings to researches/ai-investment-shift/
|
|
162
|
-
|
|
163
|
-
Agent 2 (OpenAI):
|
|
164
|
-
> Research OpenAI's challenges for a presentation on AI investment shifts:
|
|
165
|
-
> - Financial: burn rate, profitability timeline, cost structure
|
|
166
|
-
> - Operational: API reliability data, outage frequency, enterprise complaints
|
|
167
|
-
> - Governance: nonprofit conversion, leadership controversies, investor concerns
|
|
168
|
-
> Focus on 2025-2026 data. Write findings to researches/ai-investment-shift/
|
|
169
|
-
|
|
170
|
-
Agent 3 (Market & Capital Flow):
|
|
171
|
-
> Research AI industry capital flow for a presentation on AI investment shifts:
|
|
172
|
-
> - VC investment patterns: dual-backing trends, secondary market activity
|
|
173
|
-
> - Enterprise adoption: market share shifts, switching patterns
|
|
174
|
-
> - Macro trends: agentic AI adoption, market size projections
|
|
175
|
-
> Focus on 2025-2026 data. Write findings to researches/ai-investment-shift/
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
Launch all agents in **one message** using parallel Task tool calls. Each agent
|
|
179
|
-
runs independently and writes its own findings file:
|
|
180
|
-
`researches/{topic-slug}/{axis-name}.md`
|
|
151
|
+
Launch ALL agents in a single message (parallel Task tool calls).
|
|
181
152
|
|
|
182
153
|
Each agent's brief should specify:
|
|
183
154
|
- The topic slug (shared, e.g. `ai-investment-shift`)
|
|
@@ -192,20 +163,9 @@ each `.md` file. Each file contains structured `## Data`, `## Cases`,
|
|
|
192
163
|
Cross-reference agent findings with workspace documents (Layer 1). Flag any
|
|
193
164
|
contradictions.
|
|
194
165
|
|
|
195
|
-
##### Fallback — ONLY if Research Agent is Unavailable
|
|
196
|
-
|
|
197
|
-
If and only if the Task tool with `subagent_type: "revela-research"` is **not
|
|
198
|
-
available as a tool** (i.e., it does not exist in your tool list), fall back to
|
|
199
|
-
using `webfetch` directly on targeted URLs. Even in fallback mode, structure
|
|
200
|
-
your research by axis — do not run a single vague query.
|
|
201
|
-
|
|
202
|
-
Note: `websearch` is blocked by the Revela plugin when agents are available.
|
|
203
|
-
In fallback mode, use `webfetch` with specific URLs from your knowledge.
|
|
204
|
-
|
|
205
166
|
**Anti-pattern — NEVER do this:**
|
|
206
|
-
- Do NOT use `websearch` directly — it is blocked by the Revela plugin
|
|
207
|
-
|
|
208
|
-
URLs in fallback mode).
|
|
167
|
+
- Do NOT use `websearch` directly — it is blocked by the Revela plugin;
|
|
168
|
+
use research agents instead.
|
|
209
169
|
- Do NOT run a few quick searches, decide "that's enough data", and skip the
|
|
210
170
|
research agent. The agent's job is deep, systematic research — ad-hoc
|
|
211
171
|
fetches cannot replace it.
|
|
@@ -273,13 +233,15 @@ A 6-slide deck might be: Cover → Background → Content × 3 → Closing.
|
|
|
273
233
|
An 8-slide deck might be: Cover → TOC → Background → Content × 3 → Summary → Closing.
|
|
274
234
|
Never skip Cover, Background, or Closing regardless of slide count.
|
|
275
235
|
|
|
276
|
-
**Every `<section class="slide">` must include a `
|
|
236
|
+
**Every `<section class="slide">` must include a `slide-qa` attribute.** Set
|
|
237
|
+
`slide-qa="true"` for content-heavy layouts (those marked ✓ in the Layout Index
|
|
238
|
+
QA column of the active design). Set `slide-qa="false"` for structural or sparse
|
|
239
|
+
layouts (cover, TOC, closing, quote, summary, etc.). When unsure, use `"false"`.
|
|
277
240
|
|
|
278
|
-
|
|
241
|
+
Example: `<section class="slide" slide-qa="true" data-index="0">`
|
|
279
242
|
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
The layout QA system uses this to skip fill-ratio and spacing checks on structural slides that are intentionally sparse.
|
|
243
|
+
The layout QA system uses this to skip fill-ratio and spacing checks on slides
|
|
244
|
+
that are intentionally sparse.
|
|
283
245
|
|
|
284
246
|
### Domain Context
|
|
285
247
|
|
|
@@ -342,96 +304,17 @@ Follow these rules on every generation. They are non-negotiable.
|
|
|
342
304
|
|
|
343
305
|
### Inline Editing
|
|
344
306
|
|
|
345
|
-
**Always include inline editing** in every generated presentation.
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
Implementation rules:
|
|
350
|
-
|
|
351
|
-
- **JS-based hover activation** — attach `mouseenter` / `mouseleave` listeners
|
|
352
|
-
on editable elements. After a 400ms hover delay, show a subtle outline to
|
|
353
|
-
indicate editability. Click activates `contenteditable`. Click outside or
|
|
354
|
-
press Escape to deactivate.
|
|
355
|
-
- **Never** use CSS `~` sibling selector (breaks due to `pointer-events: none`
|
|
356
|
-
interrupting the hover chain).
|
|
357
|
-
- **Editable elements** — only text content: `h1, h2, h3, h4, p, span, li,
|
|
358
|
-
blockquote, cite` and design-specific text classes (`.card-title`, `.card-body`,
|
|
359
|
-
`.stat-number`, `.stat-label`, `.stat-desc`, `.step-title`, `.step-desc`).
|
|
360
|
-
Never make structural containers or images editable.
|
|
361
|
-
- **Hover style** — `outline: 1px dashed rgba(128,128,128,0.3)` on hover,
|
|
362
|
-
`outline: 2px solid rgba(59,130,246,0.5)` when actively editing. Keep it
|
|
363
|
-
subtle — must not interfere with the design aesthetic.
|
|
364
|
-
- **`window.getEditedHTML()`** — always define this global function. It returns
|
|
365
|
-
the full edited HTML (`'<!DOCTYPE html>\n' + document.documentElement.outerHTML`).
|
|
366
|
-
The parent frame calls this to retrieve the full edited HTML for saving.
|
|
367
|
-
|
|
368
|
-
Reference implementation (include in the `<script>` block after `SlidePresentation`):
|
|
369
|
-
|
|
370
|
-
```javascript
|
|
371
|
-
// --- Inline Editing ---
|
|
372
|
-
(function() {
|
|
373
|
-
const EDITABLE = 'h1,h2,h3,h4,p,span,li,blockquote,cite,' +
|
|
374
|
-
'.card-title,.card-body,.stat-number,.stat-label,.stat-desc,' +
|
|
375
|
-
'.step-title,.step-desc';
|
|
376
|
-
let hoverTimer = null;
|
|
377
|
-
let activeEl = null;
|
|
378
|
-
|
|
379
|
-
document.querySelectorAll(EDITABLE).forEach(el => {
|
|
380
|
-
el.addEventListener('mouseenter', () => {
|
|
381
|
-
hoverTimer = setTimeout(() => {
|
|
382
|
-
el.style.outline = '1px dashed rgba(128,128,128,0.3)';
|
|
383
|
-
el.style.cursor = 'text';
|
|
384
|
-
}, 400);
|
|
385
|
-
});
|
|
386
|
-
el.addEventListener('mouseleave', () => {
|
|
387
|
-
clearTimeout(hoverTimer);
|
|
388
|
-
if (el !== activeEl) {
|
|
389
|
-
el.style.outline = '';
|
|
390
|
-
el.style.cursor = '';
|
|
391
|
-
}
|
|
392
|
-
});
|
|
393
|
-
el.addEventListener('click', (e) => {
|
|
394
|
-
if (activeEl && activeEl !== el) {
|
|
395
|
-
activeEl.contentEditable = 'false';
|
|
396
|
-
activeEl.style.outline = '';
|
|
397
|
-
activeEl.style.cursor = '';
|
|
398
|
-
}
|
|
399
|
-
el.contentEditable = 'true';
|
|
400
|
-
el.style.outline = '2px solid rgba(59,130,246,0.5)';
|
|
401
|
-
el.style.cursor = 'text';
|
|
402
|
-
activeEl = el;
|
|
403
|
-
e.stopPropagation();
|
|
404
|
-
});
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
document.addEventListener('click', () => {
|
|
408
|
-
if (activeEl) {
|
|
409
|
-
activeEl.contentEditable = 'false';
|
|
410
|
-
activeEl.style.outline = '';
|
|
411
|
-
activeEl.style.cursor = '';
|
|
412
|
-
activeEl = null;
|
|
413
|
-
}
|
|
414
|
-
});
|
|
415
|
-
document.addEventListener('keydown', (e) => {
|
|
416
|
-
if (e.key === 'Escape' && activeEl) {
|
|
417
|
-
activeEl.contentEditable = 'false';
|
|
418
|
-
activeEl.style.outline = '';
|
|
419
|
-
activeEl.style.cursor = '';
|
|
420
|
-
activeEl = null;
|
|
421
|
-
}
|
|
422
|
-
});
|
|
423
|
-
|
|
424
|
-
window.getEditedHTML = () =>
|
|
425
|
-
'<!DOCTYPE html>\n' + document.documentElement.outerHTML;
|
|
426
|
-
})();
|
|
427
|
-
```
|
|
307
|
+
**Always include inline editing** in every generated presentation. The complete
|
|
308
|
+
reference implementation is provided in the active design's `@design:foundation`
|
|
309
|
+
section. Follow it exactly — pay attention to the hover-delay pattern, editable
|
|
310
|
+
element selector list, and `window.getEditedHTML()` definition.
|
|
428
311
|
|
|
429
312
|
### Image Rules
|
|
430
313
|
|
|
431
314
|
- Use direct file paths (`src="assets/logo.png"`) in HTML — not base64
|
|
432
315
|
- Always use the **original** file path in HTML `<img src>` for full-quality rendering
|
|
433
316
|
- Never repeat the same image on multiple slides (logos: title + closing only)
|
|
434
|
-
- Image compression is handled automatically by the server
|
|
317
|
+
- Image compression is handled automatically by the server
|
|
435
318
|
- **Use the active design's image components** (`.image-card`, `.card-img`, `.avatar`)
|
|
436
319
|
for displaying images — they provide proper rounded corners and cropping
|
|
437
320
|
|
|
@@ -615,62 +498,4 @@ The active design name is in the HTML comment at the top of this prompt:
|
|
|
615
498
|
The active design's complete visual specification — Component Library, Layout
|
|
616
499
|
Primitives, Composition Guide, and Data Visualization rules — is injected
|
|
617
500
|
below after the `---` separator. This is your sole visual reference for
|
|
618
|
-
generating slides.
|
|
619
|
-
|
|
620
|
-
---
|
|
621
|
-
|
|
622
|
-
## What Comes Next in This System Prompt
|
|
623
|
-
|
|
624
|
-
After the `---` separators below, two additional sections may appear:
|
|
625
|
-
|
|
626
|
-
1. **Domain definition** (if a domain other than "general" is active) — report
|
|
627
|
-
structure, AI logic rules, terminology, and visual preferences for the domain.
|
|
628
|
-
2. **Visual style** (from the active design) — colors, fonts, animation specifics,
|
|
629
|
-
layout variants. Apply it precisely — it overrides any default aesthetic preferences.
|
|
630
|
-
|
|
631
|
-
If only one `---` section follows, it is the visual style (no domain is active).
|
|
632
|
-
|
|
633
|
-
---
|
|
634
|
-
|
|
635
|
-
## File Access Rules
|
|
636
|
-
|
|
637
|
-
### How tools interact with files
|
|
638
|
-
|
|
639
|
-
The workspace `.ignore` file prevents binary formats from appearing in `grep`,
|
|
640
|
-
`glob`, and `list` results (these tools use ripgrep, which honours `.ignore`).
|
|
641
|
-
The `read` tool works by **direct file path** and is **not** affected by
|
|
642
|
-
`.ignore` — if you have a path, you can always `read` it.
|
|
643
|
-
|
|
644
|
-
### Documents
|
|
645
|
-
|
|
646
|
-
`.pdf` `.xlsx` `.xls` `.docx` `.doc` `.pptx` `.ppt` `.csv`
|
|
647
|
-
|
|
648
|
-
If the `extract` tool is available, use it to extract document content as clean
|
|
649
|
-
markdown text. **NEVER** use the `read` tool on PDF, Excel, Word, or PPT files
|
|
650
|
-
— their raw binary content will flood the context window.
|
|
651
|
-
|
|
652
|
-
If the `extract` tool is NOT available, use `read` only for text-based formats
|
|
653
|
-
(`.csv`, plain text). For binary document formats, inform the user and ask them
|
|
654
|
-
to provide the content in text form.
|
|
655
|
-
|
|
656
|
-
### Images
|
|
657
|
-
|
|
658
|
-
`.png` `.jpg` `.jpeg` `.gif` `.webp` `.bmp` `.tiff` `.tif` `.avif`
|
|
659
|
-
|
|
660
|
-
Use the `read` tool to view images directly (OpenCode returns them as visual
|
|
661
|
-
attachments). In HTML, always reference the original file path for full-quality
|
|
662
|
-
rendering:
|
|
663
|
-
|
|
664
|
-
```html
|
|
665
|
-
<img src="photos/screenshot.png" alt="...">
|
|
666
|
-
```
|
|
667
|
-
|
|
668
|
-
### Binary files — never access
|
|
669
|
-
|
|
670
|
-
These are excluded from all tools:
|
|
671
|
-
|
|
672
|
-
`.mp4` `.mov` `.avi` `.mkv` `.webm` `.m4v` `.wmv`
|
|
673
|
-
`.mp3` `.wav` `.ogg` `.flac` `.aac` `.m4a`
|
|
674
|
-
`.zip` `.tar` `.gz` `.bz2` `.7z` `.rar` `.tgz`
|
|
675
|
-
`.woff` `.woff2` `.ttf` `.otf` `.eot`
|
|
676
|
-
`.db` `.sqlite` `.sqlite3` `.bin` `.exe` `.dll` `.so` `.dylib`
|
|
501
|
+
generating slides.
|
package/tools/designs.ts
CHANGED
|
@@ -7,8 +7,10 @@ import {
|
|
|
7
7
|
removeDesign,
|
|
8
8
|
parseDesignSections,
|
|
9
9
|
generateComponentIndex,
|
|
10
|
+
generateLayoutIndex,
|
|
10
11
|
getDesignSection,
|
|
11
12
|
getDesignComponent,
|
|
13
|
+
getDesignLayout,
|
|
12
14
|
} from "../lib/design/designs"
|
|
13
15
|
import { buildPrompt } from "../lib/prompt-builder"
|
|
14
16
|
import { existsSync, readFileSync } from "fs"
|
|
@@ -23,7 +25,7 @@ export default tool({
|
|
|
23
25
|
"Use action 'activate' to switch to a different design (requires name). " +
|
|
24
26
|
"Use action 'install' to add a new design from a URL, local path, or github:user/repo shorthand (requires source). " +
|
|
25
27
|
"Use action 'remove' to uninstall a design (requires name). " +
|
|
26
|
-
"Use action 'read' to fetch on-demand design content: pass component (comma-separated names) to get full CSS/HTML for specific components, or section ('
|
|
28
|
+
"Use action 'read' to fetch on-demand design content: pass layout (comma-separated names) to get full HTML/CSS for specific layouts, pass component (comma-separated names) to get full CSS/HTML for specific components, or section ('chart-rules' | 'foundation' | 'layouts' | 'components') to get an entire section. Pass neither to get the Component Index table. " +
|
|
27
29
|
"After activating a new design, the system prompt is automatically rebuilt.",
|
|
28
30
|
args: {
|
|
29
31
|
action: tool.schema
|
|
@@ -39,6 +41,12 @@ export default tool({
|
|
|
39
41
|
.describe(
|
|
40
42
|
"Install source — URL, local path, github:user/repo. Required for install."
|
|
41
43
|
),
|
|
44
|
+
layout: tool.schema
|
|
45
|
+
.string()
|
|
46
|
+
.optional()
|
|
47
|
+
.describe(
|
|
48
|
+
"For action 'read': comma-separated layout name(s) to fetch (e.g. 'cover', 'two-col,card-grid')"
|
|
49
|
+
),
|
|
42
50
|
component: tool.schema
|
|
43
51
|
.string()
|
|
44
52
|
.optional()
|
|
@@ -49,7 +57,7 @@ export default tool({
|
|
|
49
57
|
.string()
|
|
50
58
|
.optional()
|
|
51
59
|
.describe(
|
|
52
|
-
"For action 'read': section name to fetch — '
|
|
60
|
+
"For action 'read': section name to fetch — 'chart-rules', 'foundation', 'rules', 'layouts', or 'components'"
|
|
53
61
|
),
|
|
54
62
|
},
|
|
55
63
|
async execute(args) {
|
|
@@ -96,13 +104,18 @@ export default tool({
|
|
|
96
104
|
}
|
|
97
105
|
const raw = readFileSync(mdPath, "utf-8")
|
|
98
106
|
const { body } = parseFrontmatter(raw)
|
|
99
|
-
const { components, hasMarkers } = parseDesignSections(body)
|
|
107
|
+
const { layouts, components, hasMarkers } = parseDesignSections(body)
|
|
100
108
|
|
|
101
109
|
if (!hasMarkers) {
|
|
102
110
|
// No markers — return full body
|
|
103
111
|
return body
|
|
104
112
|
}
|
|
105
113
|
|
|
114
|
+
// Specific layout(s) requested
|
|
115
|
+
if (args.layout) {
|
|
116
|
+
return getDesignLayout(args.layout, designName)
|
|
117
|
+
}
|
|
118
|
+
|
|
106
119
|
// Specific component(s) requested
|
|
107
120
|
if (args.component) {
|
|
108
121
|
return getDesignComponent(args.component, designName)
|
|
@@ -113,8 +126,11 @@ export default tool({
|
|
|
113
126
|
return getDesignSection(args.section, designName)
|
|
114
127
|
}
|
|
115
128
|
|
|
116
|
-
// Default: return Component Index
|
|
117
|
-
|
|
129
|
+
// Default: return Layout Index + Component Index
|
|
130
|
+
const li = generateLayoutIndex(layouts)
|
|
131
|
+
const ci = generateComponentIndex(components)
|
|
132
|
+
const parts = [li, ci].filter(Boolean)
|
|
133
|
+
return parts.join("\n\n---\n\n") || "(no layouts or components found)"
|
|
118
134
|
}
|
|
119
135
|
default:
|
|
120
136
|
return JSON.stringify({ error: `Unknown action: ${args.action}` })
|
package/tools/workspace-scan.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { tool } from "@opencode-ai/plugin"
|
|
2
2
|
import { readdirSync, statSync, existsSync } from "fs"
|
|
3
|
-
import { join, relative, extname } from "path"
|
|
3
|
+
import { join, relative, extname, resolve, sep, isAbsolute } from "path"
|
|
4
4
|
|
|
5
5
|
const DOC_EXTENSIONS = new Set([
|
|
6
6
|
".pdf", ".docx", ".doc", ".xlsx", ".xls",
|
|
@@ -113,7 +113,22 @@ export default tool({
|
|
|
113
113
|
async execute(args, context) {
|
|
114
114
|
try {
|
|
115
115
|
const workspaceDir = context.directory ?? process.cwd()
|
|
116
|
-
|
|
116
|
+
|
|
117
|
+
// Validate and resolve scanRoot — must stay within workspaceDir
|
|
118
|
+
let scanRoot = workspaceDir
|
|
119
|
+
if (args.path) {
|
|
120
|
+
if (isAbsolute(args.path)) {
|
|
121
|
+
return JSON.stringify({ error: "path must be relative to workspace root" })
|
|
122
|
+
}
|
|
123
|
+
const candidate = join(workspaceDir, args.path)
|
|
124
|
+
const resolvedCandidate = resolve(candidate)
|
|
125
|
+
const resolvedWorkspace = resolve(workspaceDir)
|
|
126
|
+
if (resolvedCandidate !== resolvedWorkspace && !resolvedCandidate.startsWith(resolvedWorkspace + sep)) {
|
|
127
|
+
return JSON.stringify({ error: "path must be within workspace" })
|
|
128
|
+
}
|
|
129
|
+
scanRoot = candidate
|
|
130
|
+
}
|
|
131
|
+
|
|
117
132
|
const maxDepth = args.max_depth ?? 6
|
|
118
133
|
|
|
119
134
|
if (!existsSync(scanRoot)) {
|