@cyber-dash-tech/revela 0.13.0 → 0.15.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.md +75 -55
- package/README.zh-CN.md +75 -55
- package/lib/command-intent.ts +59 -0
- package/lib/commands/brief.ts +1 -1
- package/lib/commands/designs.ts +1 -1
- package/lib/commands/domains.ts +1 -1
- package/lib/commands/edit.ts +7 -5
- package/lib/commands/enable.ts +6 -6
- package/lib/commands/help.ts +19 -10
- package/lib/commands/init.ts +1 -1
- package/lib/commands/inspect.ts +7 -5
- package/lib/commands/research.ts +66 -0
- package/lib/commands/review.ts +3 -3
- package/lib/decks-state.ts +5 -5
- package/lib/inspect/prompt.ts +15 -2
- package/lib/inspect/requests.ts +21 -2
- package/lib/inspection-context/compile.ts +71 -5
- package/lib/inspection-context/match.ts +71 -1
- package/lib/inspection-context/project.ts +116 -13
- package/lib/inspection-context/result.ts +183 -0
- package/lib/narrative-state/queries.ts +1 -0
- package/lib/refine/server.ts +91 -13
- package/package.json +1 -1
- package/plugin.ts +252 -53
- package/skill/NARRATIVE_SKILL.md +103 -25
- package/skill/SKILL.md +1 -1
- package/tools/edit.ts +10 -8
- package/tools/inspection-result.ts +37 -0
package/plugin.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { seedBuiltinDomains } from "./lib/domain/domains"
|
|
|
23
23
|
import { buildPrompt } from "./lib/prompt-builder"
|
|
24
24
|
import { ACTIVE_PROMPT_FILE } from "./lib/config"
|
|
25
25
|
import { ctx } from "./lib/ctx"
|
|
26
|
+
import { formatCommandIntentSystemBlock, setPendingCommandIntent, takePendingCommandIntent } from "./lib/command-intent"
|
|
26
27
|
import { preRead } from "./lib/read-hooks"
|
|
27
28
|
import { postRead } from "./lib/read-hooks"
|
|
28
29
|
import { extractPdfText } from "./lib/read-hooks/extractors/pdf"
|
|
@@ -59,6 +60,7 @@ import {
|
|
|
59
60
|
buildDesignsEditPrompt,
|
|
60
61
|
} from "./lib/commands/designs-new"
|
|
61
62
|
import { buildInitPrompt } from "./lib/commands/init"
|
|
63
|
+
import { buildResearchPrompt } from "./lib/commands/research"
|
|
62
64
|
import { handleBrief, parseBriefArgs } from "./lib/commands/brief"
|
|
63
65
|
import { buildNarrativeViewPrompt, handleNarrative, parseNarrativeArgs } from "./lib/commands/narrative"
|
|
64
66
|
import { parseRememberArgs, buildRememberPrompt } from "./lib/commands/remember"
|
|
@@ -206,6 +208,27 @@ const server: Plugin = (async (pluginCtx) => {
|
|
|
206
208
|
return input?.sessionID ?? input?.session?.id ?? input?.context?.sessionID ?? ""
|
|
207
209
|
}
|
|
208
210
|
|
|
211
|
+
function queueWorkflowCommand(input: {
|
|
212
|
+
sessionID: string
|
|
213
|
+
name: string
|
|
214
|
+
mode: "narrative" | "deck-render"
|
|
215
|
+
visibleText: string
|
|
216
|
+
hiddenPrompt: string
|
|
217
|
+
output: any
|
|
218
|
+
}): void {
|
|
219
|
+
ctx.enabled = true
|
|
220
|
+
buildPrompt({ mode: input.mode })
|
|
221
|
+
setPendingCommandIntent({
|
|
222
|
+
sessionID: input.sessionID,
|
|
223
|
+
name: input.name,
|
|
224
|
+
mode: input.mode,
|
|
225
|
+
visibleText: input.visibleText,
|
|
226
|
+
hiddenPrompt: input.hiddenPrompt,
|
|
227
|
+
})
|
|
228
|
+
input.output.parts.length = 0
|
|
229
|
+
input.output.parts.push({ type: "text", text: input.visibleText } as any)
|
|
230
|
+
}
|
|
231
|
+
|
|
209
232
|
function ensureEditorOpenAfterDeckChange(filePath: string, sessionID: string): void {
|
|
210
233
|
if (!isDeckHtmlPath(filePath) || !sessionID) return
|
|
211
234
|
|
|
@@ -239,7 +262,7 @@ const server: Plugin = (async (pluginCtx) => {
|
|
|
239
262
|
opencodeConfig.command ??= {}
|
|
240
263
|
opencodeConfig.command["revela"] = {
|
|
241
264
|
template: "",
|
|
242
|
-
description: "Revela —
|
|
265
|
+
description: "Revela — narrative artifact workspace (init, research, story, make, refine, design)",
|
|
243
266
|
}
|
|
244
267
|
|
|
245
268
|
// Register the research subagent.
|
|
@@ -329,12 +352,14 @@ const server: Plugin = (async (pluginCtx) => {
|
|
|
329
352
|
throw new Error("__REVELA_DISABLE_HANDLED__")
|
|
330
353
|
}
|
|
331
354
|
if (sub === "init") {
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
355
|
+
queueWorkflowCommand({
|
|
356
|
+
sessionID,
|
|
357
|
+
name: "init",
|
|
358
|
+
mode: "narrative",
|
|
359
|
+
visibleText: "Initialize Revela workspace.",
|
|
360
|
+
hiddenPrompt: buildInitPrompt({ exists: hasDecksState(workspaceRoot), workspaceRoot }),
|
|
361
|
+
output,
|
|
362
|
+
})
|
|
338
363
|
return
|
|
339
364
|
}
|
|
340
365
|
if (sub === "remember") {
|
|
@@ -343,28 +368,73 @@ const server: Plugin = (async (pluginCtx) => {
|
|
|
343
368
|
await send(parsed.error)
|
|
344
369
|
throw new Error("__REVELA_REMEMBER_USAGE_HANDLED__")
|
|
345
370
|
}
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
371
|
+
queueWorkflowCommand({
|
|
372
|
+
sessionID,
|
|
373
|
+
name: "remember",
|
|
374
|
+
mode: "narrative",
|
|
375
|
+
visibleText: "Remember Revela workspace preference.",
|
|
376
|
+
hiddenPrompt: buildRememberPrompt({ memory: parsed.memory, exists: hasDecksState(workspaceRoot) }),
|
|
377
|
+
output,
|
|
378
|
+
})
|
|
379
|
+
return
|
|
380
|
+
}
|
|
381
|
+
if (sub === "research") {
|
|
382
|
+
if (param) {
|
|
383
|
+
await send("`/revela research` does not accept arguments yet. Add the research question in normal chat, or run it to work from open story gaps.")
|
|
384
|
+
throw new Error("__REVELA_RESEARCH_USAGE_HANDLED__")
|
|
385
|
+
}
|
|
386
|
+
queueWorkflowCommand({
|
|
387
|
+
sessionID,
|
|
388
|
+
name: "research",
|
|
389
|
+
mode: "narrative",
|
|
390
|
+
visibleText: "Research Revela story gaps.",
|
|
391
|
+
hiddenPrompt: buildResearchPrompt({ exists: hasDecksState(workspaceRoot), workspaceRoot }),
|
|
392
|
+
output,
|
|
393
|
+
})
|
|
352
394
|
return
|
|
353
395
|
}
|
|
354
396
|
if (sub === "review") {
|
|
355
397
|
if (param) {
|
|
356
|
-
await send("`/revela review` no longer accepts a deck name. It
|
|
398
|
+
await send("`/revela review` no longer accepts a deck name. It is a compatibility alias for `/revela story`. Use `/revela make deck --review` for deck/artifact readiness.")
|
|
357
399
|
throw new Error("__REVELA_REVIEW_USAGE_HANDLED__")
|
|
358
400
|
}
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
401
|
+
queueWorkflowCommand({
|
|
402
|
+
sessionID,
|
|
403
|
+
name: "review",
|
|
404
|
+
mode: "narrative",
|
|
405
|
+
visibleText: "Review Revela story readiness.",
|
|
406
|
+
hiddenPrompt: buildReviewPrompt({ exists: hasDecksState(workspaceRoot), workspaceRoot }),
|
|
407
|
+
output,
|
|
408
|
+
})
|
|
409
|
+
return
|
|
410
|
+
}
|
|
411
|
+
if (sub === "story") {
|
|
412
|
+
if (param) {
|
|
413
|
+
await send("`/revela story` does not accept arguments yet. It opens the current workspace story UI. Use `/revela review` for a readiness report.")
|
|
414
|
+
throw new Error("__REVELA_STORY_USAGE_HANDLED__")
|
|
415
|
+
}
|
|
416
|
+
queueWorkflowCommand({
|
|
417
|
+
sessionID,
|
|
418
|
+
name: "story",
|
|
419
|
+
mode: "narrative",
|
|
420
|
+
visibleText: "Open Revela story workspace.",
|
|
421
|
+
hiddenPrompt: buildNarrativeViewPrompt({ workspaceRoot, language: "en" }),
|
|
422
|
+
output,
|
|
423
|
+
})
|
|
365
424
|
return
|
|
366
425
|
}
|
|
367
426
|
if (sub === "narrative") {
|
|
427
|
+
if (!param) {
|
|
428
|
+
queueWorkflowCommand({
|
|
429
|
+
sessionID,
|
|
430
|
+
name: "narrative",
|
|
431
|
+
mode: "narrative",
|
|
432
|
+
visibleText: "Open Revela story workspace.",
|
|
433
|
+
hiddenPrompt: buildNarrativeViewPrompt({ workspaceRoot, language: "en" }),
|
|
434
|
+
output,
|
|
435
|
+
})
|
|
436
|
+
return
|
|
437
|
+
}
|
|
368
438
|
const parsed = parseNarrativeArgs(param)
|
|
369
439
|
if (!parsed.ok) {
|
|
370
440
|
await send(parsed.error)
|
|
@@ -374,12 +444,14 @@ const server: Plugin = (async (pluginCtx) => {
|
|
|
374
444
|
await handleNarrative({ workspaceRoot, openBrowser: true, language: parsed.args.language }, send)
|
|
375
445
|
throw new Error("__REVELA_NARRATIVE_HANDLED__")
|
|
376
446
|
}
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
447
|
+
queueWorkflowCommand({
|
|
448
|
+
sessionID,
|
|
449
|
+
name: "narrative view",
|
|
450
|
+
mode: "narrative",
|
|
451
|
+
visibleText: "Open read-only Revela narrative map.",
|
|
452
|
+
hiddenPrompt: buildNarrativeViewPrompt({ workspaceRoot, language: parsed.args.language }),
|
|
453
|
+
output,
|
|
454
|
+
})
|
|
383
455
|
return
|
|
384
456
|
}
|
|
385
457
|
if (sub === "brief") {
|
|
@@ -391,26 +463,62 @@ const server: Plugin = (async (pluginCtx) => {
|
|
|
391
463
|
await handleBrief({ workspaceRoot, outputPath: parsed.args.outputPath }, send)
|
|
392
464
|
throw new Error("__REVELA_BRIEF_HANDLED__")
|
|
393
465
|
}
|
|
466
|
+
if (sub === "make") {
|
|
467
|
+
const target = args[1]?.toLowerCase() ?? ""
|
|
468
|
+
const makeParam = args.slice(2).join(" ")
|
|
469
|
+
if (target === "deck") {
|
|
470
|
+
if (makeParam && makeParam !== "--review") {
|
|
471
|
+
await send("Usage: `/revela make deck` starts approved-narrative deck handoff; `/revela make deck --review` reviews deck/artifact readiness.")
|
|
472
|
+
throw new Error("__REVELA_MAKE_DECK_USAGE_HANDLED__")
|
|
473
|
+
}
|
|
474
|
+
queueWorkflowCommand({
|
|
475
|
+
sessionID,
|
|
476
|
+
name: makeParam ? "make deck --review" : "make deck",
|
|
477
|
+
mode: "deck-render",
|
|
478
|
+
visibleText: makeParam ? "Review Revela deck artifact readiness." : "Make Revela deck from approved story.",
|
|
479
|
+
hiddenPrompt: makeParam
|
|
480
|
+
? buildDeckReviewPrompt({ exists: hasDecksState(workspaceRoot), workspaceRoot })
|
|
481
|
+
: buildDeckPrompt({ exists: hasDecksState(workspaceRoot), workspaceRoot }),
|
|
482
|
+
output,
|
|
483
|
+
})
|
|
484
|
+
return
|
|
485
|
+
}
|
|
486
|
+
if (target === "brief") {
|
|
487
|
+
const parsed = parseBriefArgs(makeParam)
|
|
488
|
+
if (!parsed.ok) {
|
|
489
|
+
await send(parsed.error.replace("/revela brief", "/revela make brief"))
|
|
490
|
+
throw new Error("__REVELA_MAKE_BRIEF_USAGE_HANDLED__")
|
|
491
|
+
}
|
|
492
|
+
await handleBrief({ workspaceRoot, outputPath: parsed.args.outputPath }, send)
|
|
493
|
+
throw new Error("__REVELA_MAKE_BRIEF_HANDLED__")
|
|
494
|
+
}
|
|
495
|
+
await send("Usage: `/revela make deck [--review]` or `/revela make brief [workspace-relative-output.md]`.")
|
|
496
|
+
throw new Error("__REVELA_MAKE_USAGE_HANDLED__")
|
|
497
|
+
}
|
|
394
498
|
if (sub === "deck") {
|
|
395
499
|
if (param && param !== "--review") {
|
|
396
|
-
await send("Usage: `/revela deck` starts approved-narrative deck handoff; `/revela deck --review` reviews deck/artifact readiness.")
|
|
500
|
+
await send("Usage: `/revela deck` starts approved-narrative deck handoff; `/revela deck --review` reviews deck/artifact readiness. These are compatibility aliases for `/revela make deck`.")
|
|
397
501
|
throw new Error("__REVELA_DECK_USAGE_HANDLED__")
|
|
398
502
|
}
|
|
399
503
|
if (!param) {
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
504
|
+
queueWorkflowCommand({
|
|
505
|
+
sessionID,
|
|
506
|
+
name: "deck",
|
|
507
|
+
mode: "deck-render",
|
|
508
|
+
visibleText: "Make Revela deck from approved story.",
|
|
509
|
+
hiddenPrompt: buildDeckPrompt({ exists: hasDecksState(workspaceRoot), workspaceRoot }),
|
|
510
|
+
output,
|
|
511
|
+
})
|
|
406
512
|
return
|
|
407
513
|
}
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
514
|
+
queueWorkflowCommand({
|
|
515
|
+
sessionID,
|
|
516
|
+
name: "deck --review",
|
|
517
|
+
mode: "deck-render",
|
|
518
|
+
visibleText: "Review Revela deck artifact readiness.",
|
|
519
|
+
hiddenPrompt: buildDeckReviewPrompt({ exists: hasDecksState(workspaceRoot), workspaceRoot }),
|
|
520
|
+
output,
|
|
521
|
+
})
|
|
414
522
|
return
|
|
415
523
|
}
|
|
416
524
|
if (sub === "refine") {
|
|
@@ -423,7 +531,7 @@ const server: Plugin = (async (pluginCtx) => {
|
|
|
423
531
|
}
|
|
424
532
|
if (sub === "edit") {
|
|
425
533
|
if (param) {
|
|
426
|
-
await send("`/revela edit`
|
|
534
|
+
await send("`/revela edit` is deprecated and does not accept a target. Use `/revela refine` for the unified refinement workspace.")
|
|
427
535
|
throw new Error("__REVELA_EDIT_USAGE_HANDLED__")
|
|
428
536
|
}
|
|
429
537
|
await handleEdit({ client, sessionID, workspaceRoot }, send)
|
|
@@ -431,7 +539,7 @@ const server: Plugin = (async (pluginCtx) => {
|
|
|
431
539
|
}
|
|
432
540
|
if (sub === "inspect") {
|
|
433
541
|
if (param) {
|
|
434
|
-
await send("`/revela inspect` does not accept a target.
|
|
542
|
+
await send("`/revela inspect` is deprecated and does not accept a target. Use `/revela refine` for the unified reading and refinement workspace.")
|
|
435
543
|
throw new Error("__REVELA_INSPECT_USAGE_HANDLED__")
|
|
436
544
|
}
|
|
437
545
|
await handleInspect({ client, sessionID, workspaceRoot }, send)
|
|
@@ -457,17 +565,94 @@ const server: Plugin = (async (pluginCtx) => {
|
|
|
457
565
|
await handleDesignsAdd(param, send)
|
|
458
566
|
throw new Error("__REVELA_DESIGNS_ADD_HANDLED__")
|
|
459
567
|
}
|
|
568
|
+
if (sub === "design") {
|
|
569
|
+
const designAction = args[1]?.toLowerCase() ?? "list"
|
|
570
|
+
const designParam = args.slice(2).join(" ")
|
|
571
|
+
if (designAction === "list") {
|
|
572
|
+
if (designParam) {
|
|
573
|
+
await send("Usage: `/revela design list`.")
|
|
574
|
+
throw new Error("__REVELA_DESIGN_LIST_USAGE_HANDLED__")
|
|
575
|
+
}
|
|
576
|
+
await handleDesignsList(send)
|
|
577
|
+
throw new Error("__REVELA_DESIGN_LIST_HANDLED__")
|
|
578
|
+
}
|
|
579
|
+
if (designAction === "use") {
|
|
580
|
+
if (!designParam) {
|
|
581
|
+
await send("Usage: `/revela design use <name>`.")
|
|
582
|
+
throw new Error("__REVELA_DESIGN_USE_USAGE_HANDLED__")
|
|
583
|
+
}
|
|
584
|
+
await handleDesignsActivate(designParam, send)
|
|
585
|
+
throw new Error("__REVELA_DESIGN_USE_HANDLED__")
|
|
586
|
+
}
|
|
587
|
+
if (designAction === "add") {
|
|
588
|
+
if (!designParam) {
|
|
589
|
+
await send("Usage: `/revela design add <url|github:user/repo|local-path>`.")
|
|
590
|
+
throw new Error("__REVELA_DESIGN_ADD_USAGE_HANDLED__")
|
|
591
|
+
}
|
|
592
|
+
await handleDesignsAdd(designParam, send)
|
|
593
|
+
throw new Error("__REVELA_DESIGN_ADD_HANDLED__")
|
|
594
|
+
}
|
|
595
|
+
if (designAction === "rm" || designAction === "remove") {
|
|
596
|
+
if (!designParam) {
|
|
597
|
+
await send("Usage: `/revela design rm <name>`.")
|
|
598
|
+
throw new Error("__REVELA_DESIGN_RM_USAGE_HANDLED__")
|
|
599
|
+
}
|
|
600
|
+
await handleDesignsRemove(designParam, send)
|
|
601
|
+
throw new Error("__REVELA_DESIGN_RM_HANDLED__")
|
|
602
|
+
}
|
|
603
|
+
if (designAction === "preview") {
|
|
604
|
+
await handleDesignsPreview(designParam, send)
|
|
605
|
+
throw new Error("__REVELA_DESIGN_PREVIEW_HANDLED__")
|
|
606
|
+
}
|
|
607
|
+
if (designAction === "new") {
|
|
608
|
+
const parsed = parseDesignsNewArgs(designParam)
|
|
609
|
+
if (!parsed.ok) {
|
|
610
|
+
await send(parsed.error.replaceAll("/revela designs-new", "/revela design new"))
|
|
611
|
+
throw new Error("__REVELA_DESIGN_NEW_USAGE_HANDLED__")
|
|
612
|
+
}
|
|
613
|
+
queueWorkflowCommand({
|
|
614
|
+
sessionID,
|
|
615
|
+
name: `design new ${parsed.name}`,
|
|
616
|
+
mode: "deck-render",
|
|
617
|
+
visibleText: `Create Revela design ${parsed.name}.`,
|
|
618
|
+
hiddenPrompt: buildDesignsNewPrompt({ name: parsed.name, base: parsed.base }),
|
|
619
|
+
output,
|
|
620
|
+
})
|
|
621
|
+
return
|
|
622
|
+
}
|
|
623
|
+
if (designAction === "edit") {
|
|
624
|
+
const parsed = parseDesignsEditArgs(designParam)
|
|
625
|
+
if (!parsed.ok) {
|
|
626
|
+
await send(parsed.error.replaceAll("/revela designs-edit", "/revela design edit"))
|
|
627
|
+
throw new Error("__REVELA_DESIGN_EDIT_USAGE_HANDLED__")
|
|
628
|
+
}
|
|
629
|
+
queueWorkflowCommand({
|
|
630
|
+
sessionID,
|
|
631
|
+
name: `design edit ${parsed.name}`,
|
|
632
|
+
mode: "deck-render",
|
|
633
|
+
visibleText: `Edit Revela design ${parsed.name}.`,
|
|
634
|
+
hiddenPrompt: buildDesignsEditPrompt({ name: parsed.name }),
|
|
635
|
+
output,
|
|
636
|
+
})
|
|
637
|
+
return
|
|
638
|
+
}
|
|
639
|
+
await send("Usage: `/revela design [list|use <name>|new <name>|edit <name>|preview [name]|add <source>|rm <name>]`.")
|
|
640
|
+
throw new Error("__REVELA_DESIGN_USAGE_HANDLED__")
|
|
641
|
+
}
|
|
460
642
|
if (sub === "designs-new") {
|
|
461
643
|
const parsed = parseDesignsNewArgs(param)
|
|
462
644
|
if (!parsed.ok) {
|
|
463
645
|
await send(parsed.error)
|
|
464
646
|
throw new Error("__REVELA_DESIGNS_NEW_USAGE_HANDLED__")
|
|
465
647
|
}
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
648
|
+
queueWorkflowCommand({
|
|
649
|
+
sessionID,
|
|
650
|
+
name: `designs-new ${parsed.name}`,
|
|
651
|
+
mode: "deck-render",
|
|
652
|
+
visibleText: `Create Revela design ${parsed.name}.`,
|
|
653
|
+
hiddenPrompt: buildDesignsNewPrompt({ name: parsed.name, base: parsed.base }),
|
|
654
|
+
output,
|
|
655
|
+
})
|
|
471
656
|
return
|
|
472
657
|
}
|
|
473
658
|
if (sub === "designs-edit") {
|
|
@@ -476,11 +661,14 @@ const server: Plugin = (async (pluginCtx) => {
|
|
|
476
661
|
await send(parsed.error)
|
|
477
662
|
throw new Error("__REVELA_DESIGNS_EDIT_USAGE_HANDLED__")
|
|
478
663
|
}
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
664
|
+
queueWorkflowCommand({
|
|
665
|
+
sessionID,
|
|
666
|
+
name: `designs-edit ${parsed.name}`,
|
|
667
|
+
mode: "deck-render",
|
|
668
|
+
visibleText: `Edit Revela design ${parsed.name}.`,
|
|
669
|
+
hiddenPrompt: buildDesignsEditPrompt({ name: parsed.name }),
|
|
670
|
+
output,
|
|
671
|
+
})
|
|
484
672
|
return
|
|
485
673
|
}
|
|
486
674
|
if (sub === "designs-preview") {
|
|
@@ -508,8 +696,14 @@ const server: Plugin = (async (pluginCtx) => {
|
|
|
508
696
|
if (args.notes) {
|
|
509
697
|
try {
|
|
510
698
|
const deck = resolvePptxDeck(workspaceRoot, args.filePath)
|
|
511
|
-
|
|
512
|
-
|
|
699
|
+
queueWorkflowCommand({
|
|
700
|
+
sessionID,
|
|
701
|
+
name: "pptx --notes",
|
|
702
|
+
mode: "deck-render",
|
|
703
|
+
visibleText: "Export Revela deck to PPTX with speaker notes.",
|
|
704
|
+
hiddenPrompt: buildPptxNotesPrompt(deck),
|
|
705
|
+
output,
|
|
706
|
+
})
|
|
513
707
|
return
|
|
514
708
|
} catch (e) {
|
|
515
709
|
const msg = e instanceof Error ? e.message : String(e)
|
|
@@ -633,6 +827,11 @@ const server: Plugin = (async (pluginCtx) => {
|
|
|
633
827
|
error: e instanceof Error ? e.message : String(e),
|
|
634
828
|
})
|
|
635
829
|
}
|
|
830
|
+
const sessionID = extractSessionID(input)
|
|
831
|
+
const commandIntent = takePendingCommandIntent(sessionID)
|
|
832
|
+
if (commandIntent) {
|
|
833
|
+
prompt += "\n\n" + formatCommandIntentSystemBlock(commandIntent)
|
|
834
|
+
}
|
|
636
835
|
if (output.system.length > 0) {
|
|
637
836
|
output.system[output.system.length - 1] += "\n\n" + prompt
|
|
638
837
|
} else {
|
package/skill/NARRATIVE_SKILL.md
CHANGED
|
@@ -1,64 +1,142 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: revela-narrative
|
|
3
|
-
description: Build trusted narrative
|
|
3
|
+
description: Build trusted narrative state before rendering communication artifacts
|
|
4
4
|
compatibility: opencode
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Revela — Narrative Workspace
|
|
8
8
|
|
|
9
|
-
You help the user turn source materials, research, and intent into
|
|
9
|
+
You help the user turn source materials, research, data, and intent into trusted, traceable, presentation-ready decision artifacts.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
Decks are important, but they are render targets. The durable source of truth is the canonical narrative state: audience, decision, thesis, claims, evidence boundaries, objections, risks, research gaps, approval provenance, and artifact coverage.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Default mode is narrative-first. Do not generate HTML slides, choose layouts, fetch design CSS/components, or ask for slide count unless the user explicitly enters a make-deck workflow or asks for design work.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
- objections, risks,
|
|
22
|
-
- narrative
|
|
15
|
+
## Workflow Model
|
|
16
|
+
|
|
17
|
+
Use the same phase semantics whether the user invokes a slash command or asks in normal chat:
|
|
18
|
+
|
|
19
|
+
- `Init` discovers local workspace materials, captures intent, initializes or refreshes `DECKS.json`, and creates conservative narrative state only from explicit user statements or source traces.
|
|
20
|
+
- `Research` runs closed loops to fill open story gaps, bind supported findings into canonical evidence, narrow overbroad claims/relations, and reduce caveats without crossing evidence boundaries.
|
|
21
|
+
- `Story` opens the read-only story workspace UI for inspecting claim flow, evidence strength, unsupported scope, caveats, objections, risks, research gaps, approval state, and affected artifacts.
|
|
22
|
+
- `Make` renders an artifact from approved or explicitly overridden narrative state. Supported 0.15 targets are deck and executive brief.
|
|
23
|
+
- `Refine` is the post-artifact workspace for reading, inspection, and targeted editing. Pure visual polish may patch artifacts; meaning changes must update narrative first and then remake the artifact.
|
|
24
|
+
|
|
25
|
+
Public command surface:
|
|
26
|
+
|
|
27
|
+
- `/revela init`
|
|
28
|
+
- `/revela research`
|
|
29
|
+
- `/revela story`
|
|
30
|
+
- `/revela make deck`
|
|
31
|
+
- `/revela make brief`
|
|
32
|
+
- `/revela refine`
|
|
33
|
+
- `/revela design`
|
|
34
|
+
|
|
35
|
+
Compatibility aliases:
|
|
36
|
+
|
|
37
|
+
- `/revela review` means a legacy story readiness report
|
|
38
|
+
- `/revela narrative` means `/revela story`
|
|
39
|
+
- `/revela deck` means `/revela make deck`
|
|
40
|
+
- `/revela brief` means `/revela make brief`
|
|
23
41
|
|
|
24
42
|
## Workspace State
|
|
25
43
|
|
|
26
44
|
Use `DECKS.json` as Revela's current compatibility workspace-state file. Do not write or patch it directly.
|
|
27
45
|
|
|
28
46
|
Use `revela-decks` for state operations:
|
|
47
|
+
|
|
29
48
|
- `read` to inspect current workspace state
|
|
30
49
|
- `init` to register discovered source material candidates during workspace initialization
|
|
31
|
-
- `upsertNarrative` to preserve canonical audience, decision, thesis, claims, evidence bindings, objections, and
|
|
32
|
-
- `
|
|
33
|
-
- `
|
|
50
|
+
- `upsertNarrative` to preserve canonical audience, decision, thesis, claims, evidence bindings, objections, risks, and research gaps
|
|
51
|
+
- `reviewNarrative` to run deterministic story readiness
|
|
52
|
+
- `deriveResearchGaps`, `upsertResearchGaps`, `updateResearchGap`, and `closeResearchGap` to manage research gap lifecycle
|
|
53
|
+
- `attachResearchFindings` to attach saved findings to research state
|
|
54
|
+
- `applyEvidenceCandidates` only when selected candidates should become canonical support
|
|
34
55
|
- `approveNarrative` only when the user explicitly approves or requests an override
|
|
56
|
+
- `compileDeckPlan`, `upsertDeck`, `upsertSlides`, and `review` only inside make-deck or artifact-readiness workflows
|
|
57
|
+
|
|
58
|
+
Never treat `writeReadiness.status`, old review snapshots, existing `decks/*.html`, workspace scans, extraction cache paths, or saved research actions as narrative approval or proof by themselves.
|
|
59
|
+
|
|
60
|
+
## Init Rules
|
|
61
|
+
|
|
62
|
+
During init:
|
|
63
|
+
|
|
64
|
+
- scan local workspace materials before asking broad questions
|
|
65
|
+
- reuse `workspace.sourceMaterials` and extraction cache when fingerprints match
|
|
66
|
+
- extract or read only relevant local materials; do not exhaustively process large workspaces
|
|
67
|
+
- derive claims, evidence bindings, caveats, unsupported scope, source paths, quotes/snippets, pages, sheets, or slide references only when explicit support exists
|
|
68
|
+
- ask the smallest missing intent questions after local evidence has been considered
|
|
69
|
+
- do not require slide count, design choice, layout choice, output path, or visual style unless the user explicitly asks to make an artifact immediately
|
|
70
|
+
|
|
71
|
+
## Research Rules
|
|
72
|
+
|
|
73
|
+
During research:
|
|
74
|
+
|
|
75
|
+
- start from open research gaps, unsupported central claims, objections, risks, and decision questions
|
|
76
|
+
- run multiple review/search/bind/narrow/re-review loops when useful, stopping when no public evidence can improve the state or after the workflow limit
|
|
77
|
+
- avoid generic internet research when workspace evidence already supports the claim
|
|
78
|
+
- delegate external web search to the `revela-research` subagent
|
|
79
|
+
- save findings through `revela-research-save`
|
|
80
|
+
- treat `/revela research` as permission to attach findings and bind clearly supported evidence without item-by-item user confirmation
|
|
81
|
+
- use `applyEvidenceCandidates` or `upsertNarrative` to create canonical evidence bindings when claim id, quote/snippet, source, support scope, unsupported scope, caveat, and strength are explicit
|
|
82
|
+
- narrow overbroad claim scope or relation rationale when the narrower wording preserves strategic meaning and better matches the evidence
|
|
83
|
+
- preserve source path, URL, location/page/sheet/slide, quote/snippet, support scope, unsupported scope, and caveat
|
|
84
|
+
- keep missing or partial evidence visible instead of filling it with model assumptions; classify remaining caveats as internal-data-needed, not-publicly-researchable, source-quality-limit, or still-open
|
|
35
85
|
|
|
36
|
-
|
|
86
|
+
## Story Rules
|
|
37
87
|
|
|
38
|
-
|
|
88
|
+
When the user invokes `/revela story`, open the read-only story workspace UI. Do not turn that command into a blocking readiness report.
|
|
39
89
|
|
|
40
|
-
When
|
|
90
|
+
When the user invokes `/revela review` or explicitly asks for a readiness report, call `revela-decks` action `reviewNarrative` and report the tool result as authoritative.
|
|
41
91
|
|
|
42
92
|
Use this report shape:
|
|
93
|
+
|
|
43
94
|
- `Narrative readiness: <status>`
|
|
44
95
|
- `Narrative hash: <hash>` when available
|
|
45
96
|
- blockers first, with issue type, claim text when available, and suggested next action
|
|
46
97
|
- warnings second, as residual risks
|
|
98
|
+
- research gaps and unattached findings as next work
|
|
47
99
|
- approval state last, clearly distinguishing `ready_for_approval`, `approved`, stale approval, and render override
|
|
48
100
|
|
|
49
101
|
If evidence is missing, say what is missing and what should happen next. Do not invent quotes, sources, page locations, URLs, caveats, or research findings.
|
|
50
102
|
|
|
51
|
-
If research findings were saved but not attached or bound, describe them as unattached research state, not proof.
|
|
52
|
-
|
|
53
103
|
If the narrative is ready for approval, ask the user whether to approve or revise it. Do not approve automatically.
|
|
54
104
|
|
|
105
|
+
## Make Rules
|
|
106
|
+
|
|
107
|
+
For `/revela make deck` and compatible deck handoff:
|
|
108
|
+
|
|
109
|
+
- switch to deck-render mode through the command workflow
|
|
110
|
+
- check narrative readiness and current approval before compiling deck specs
|
|
111
|
+
- use `compileDeckPlan` as the canonical narrative-to-deck planning path
|
|
112
|
+
- run the deck/artifact gate with `revela-decks review` before writing HTML
|
|
113
|
+
- fetch design layouts/components only after narrative handoff is valid
|
|
114
|
+
- keep the HTML deck contract valid: one `<section class="slide">` per slide, canonical 1-based `data-slide-index`, and matching `DECKS.json` slide specs
|
|
115
|
+
|
|
116
|
+
For `/revela make brief`, render the executive brief from canonical narrative state and graph-backed claim/evidence relationships, not from a deck summary.
|
|
117
|
+
|
|
118
|
+
If story readiness, approval, evidence, or artifact blockers remain, report the blocker and suggest `/revela review`, `/revela research`, or a targeted user answer. Do not bypass with invented state.
|
|
119
|
+
|
|
120
|
+
## Refine Rules
|
|
121
|
+
|
|
122
|
+
Use `/revela refine` for post-artifact reading, inspection, and editing.
|
|
123
|
+
|
|
124
|
+
- Reading should explain source, support strength, caveat, unsupported scope, narrative purpose, related risks/objections, research gaps, and artifact coverage.
|
|
125
|
+
- Pure artifact polish may stay artifact-level: layout, typography, spacing, crop, visual hierarchy, export mechanics, and deck contract fixes.
|
|
126
|
+
- Meaning-changing edits must update canonical narrative first, then run story readiness/approval or explicit override, then remake affected artifacts.
|
|
127
|
+
- Deprecated `/revela edit` and `/revela inspect` route to Refine.
|
|
128
|
+
|
|
129
|
+
## Design Surface
|
|
130
|
+
|
|
131
|
+
Use `/revela design` for visual-system work: list/use/new/edit/preview/install/remove designs.
|
|
132
|
+
|
|
133
|
+
Do not inject design CSS, layout catalogs, component indexes, chart rules, or deck HTML skeletons during init, research, or story. Fetch design context only for make-deck or explicit design-authoring workflows.
|
|
134
|
+
|
|
55
135
|
## Boundaries
|
|
56
136
|
|
|
57
137
|
- Do not write or overwrite `decks/*.html` in narrative mode.
|
|
58
|
-
- Do not call `revela-decks review` in
|
|
59
|
-
- Do not apply evidence candidates, bind evidence, or rewrite slide text unless the user explicitly asks.
|
|
60
|
-
- Do not fetch design CSS, layouts, components, chart rules, or HTML skeletons in narrative mode.
|
|
138
|
+
- Do not call `revela-decks review` in story mode; that is the deck/artifact gate.
|
|
139
|
+
- Do not apply evidence candidates, bind evidence, or rewrite slide text unless the user explicitly asks or the active workflow requires it with clear support.
|
|
61
140
|
- Do not store secrets, credentials, tokens, or sensitive personal information.
|
|
62
141
|
- Do not infer long-term user preferences from one-off tasks.
|
|
63
|
-
|
|
64
|
-
When the user wants deck/artifact readiness, direct them to `/revela deck --review`. When they want to render a deck, wait for the explicit deck workflow.
|
|
142
|
+
- If source support is missing, keep the gap visible instead of making the claim sound proven.
|
package/skill/SKILL.md
CHANGED
|
@@ -374,7 +374,7 @@ After the user confirms the slide plan, update `DECKS.json` through `revela-deck
|
|
|
374
374
|
3. Call `revela-designs` tool with `action: "read"` and `component` set to ALL component
|
|
375
375
|
names you plan to use (comma-separated, e.g. `component: "card,stat-card,evidence-list"`).
|
|
376
376
|
4. Use `revela-decks` action `upsertDeck` to mark `requiredInputs.designLayoutsFetched` complete.
|
|
377
|
-
5. Run `/revela review` or call `revela-decks` action `review` yourself. The tool must compute readiness from `DECKS.json`.
|
|
377
|
+
5. Run `/revela make deck --review` or call `revela-decks` action `review` yourself. The tool must compute readiness from `DECKS.json`.
|
|
378
378
|
6. Use `revela-decks` action `read` and confirm `writeReadiness.status` is `ready` with no blockers.
|
|
379
379
|
7. Generate HTML that **exactly matches** the fetched examples — copy the HTML structure verbatim.
|
|
380
380
|
|
package/tools/edit.ts
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* tools/edit.ts
|
|
3
3
|
*
|
|
4
|
-
* revela-edit —
|
|
4
|
+
* revela-edit — Compatibility tool that opens Revela Refine in Edit mode.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { tool } from "@opencode-ai/plugin"
|
|
8
|
-
import {
|
|
8
|
+
import { openRefineDeck } from "../lib/refine/open"
|
|
9
9
|
|
|
10
10
|
export function createEditTool(options: { client: any; workspaceRoot: string; openBrowser?: boolean }) {
|
|
11
11
|
return tool({
|
|
12
12
|
description:
|
|
13
|
-
"Open Revela
|
|
13
|
+
"Open Revela Refine in Edit mode for an existing slide deck. " +
|
|
14
14
|
"Use this when the user asks to edit, revise, annotate, or visually comment on a deck, " +
|
|
15
15
|
"including when they reference the current deck. " +
|
|
16
|
-
"
|
|
17
|
-
"
|
|
16
|
+
"This is a compatibility tool for the older edit-only workflow; the user-facing entry is /revela refine. " +
|
|
17
|
+
"It opens a local browser workspace where the user can Ctrl/Cmd-click deck elements, use the Edit tab, " +
|
|
18
18
|
"and send precise edit requests back to the current OpenCode session.",
|
|
19
19
|
args: {},
|
|
20
20
|
async execute(_args, context: any) {
|
|
@@ -27,10 +27,11 @@ export function createEditTool(options: { client: any; workspaceRoot: string; op
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
try {
|
|
30
|
-
const result =
|
|
30
|
+
const result = openRefineDeck("", {
|
|
31
31
|
client: options.client,
|
|
32
32
|
sessionID,
|
|
33
33
|
workspaceRoot: options.workspaceRoot,
|
|
34
|
+
mode: "edit",
|
|
34
35
|
openBrowser: options.openBrowser,
|
|
35
36
|
})
|
|
36
37
|
|
|
@@ -40,9 +41,10 @@ export function createEditTool(options: { client: any; workspaceRoot: string; op
|
|
|
40
41
|
file: result.deck.file,
|
|
41
42
|
source: result.source,
|
|
42
43
|
url: result.url,
|
|
44
|
+
mode: result.mode,
|
|
43
45
|
message:
|
|
44
|
-
`${result.stateNote} Opened
|
|
45
|
-
"Ask the user to use Ctrl/Cmd
|
|
46
|
+
`${result.stateNote} Opened Revela Refine in Edit mode. ` +
|
|
47
|
+
"Ask the user to use Ctrl/Cmd-click in the browser to reference elements, then use the Edit tab to send comments.",
|
|
46
48
|
}, null, 2)
|
|
47
49
|
} catch (error) {
|
|
48
50
|
return JSON.stringify({
|