@cyber-dash-tech/revela 0.14.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 +60 -40
- package/README.zh-CN.md +60 -40
- 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/enable.ts +6 -6
- package/lib/commands/help.ts +16 -7
- package/lib/commands/init.ts +1 -1
- package/lib/commands/research.ts +66 -0
- package/lib/commands/review.ts +3 -3
- package/lib/decks-state.ts +5 -5
- package/package.json +1 -1
- package/plugin.ts +250 -51
- package/skill/NARRATIVE_SKILL.md +103 -25
- package/skill/SKILL.md +1 -1
package/lib/commands/enable.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lib/commands/enable.ts
|
|
3
3
|
*
|
|
4
|
-
* Handler for `/revela enable` — activates Revela narrative
|
|
4
|
+
* Handler for `/revela enable` — activates Revela ambient narrative mode.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import { existsSync } from "fs"
|
|
@@ -19,7 +19,7 @@ export async function handleEnable(
|
|
|
19
19
|
const design = activeDesign()
|
|
20
20
|
const domain = activeDomain()
|
|
21
21
|
|
|
22
|
-
// Always rebuild narrative mode on enable. A prior `/revela deck` handoff may
|
|
22
|
+
// Always rebuild narrative mode on enable. A prior `/revela make deck` handoff may
|
|
23
23
|
// have intentionally switched the active prompt to deck-render mode.
|
|
24
24
|
if (!existsSync(ACTIVE_PROMPT_FILE)) {
|
|
25
25
|
log.warn("active prompt file missing on enable — rebuilding", { promptFile: ACTIVE_PROMPT_FILE })
|
|
@@ -30,7 +30,7 @@ export async function handleEnable(
|
|
|
30
30
|
} catch (e) {
|
|
31
31
|
log.error("prompt rebuild failed on enable", { error: e instanceof Error ? e.message : String(e) })
|
|
32
32
|
await send(
|
|
33
|
-
`**Revela enabled (with warnings).** Narrative
|
|
33
|
+
`**Revela ambient mode enabled (with warnings).** Narrative mode is active for normal chat, ` +
|
|
34
34
|
`but the prompt file could not be built. ` +
|
|
35
35
|
`Try \`/revela disable\` then \`/revela enable\` again, or check that the package is correctly installed.\n\n` +
|
|
36
36
|
`Design: \`${design}\` · Domain: \`${domain}\``
|
|
@@ -40,9 +40,9 @@ export async function handleEnable(
|
|
|
40
40
|
|
|
41
41
|
log.info("revela enabled", { design, domain })
|
|
42
42
|
await send(
|
|
43
|
-
`**Revela enabled.**
|
|
43
|
+
`**Revela ambient mode enabled.** Normal chat will stay in Revela narrative mode.\n` +
|
|
44
44
|
`Design: \`${design}\` · Domain: \`${domain}\`\n\n` +
|
|
45
|
-
`
|
|
46
|
-
`Use \`/revela
|
|
45
|
+
`Explicit workflow commands like \`/revela init\`, \`/revela story\`, and \`/revela make deck\` work without enabling first. ` +
|
|
46
|
+
`Use \`/revela disable\` to return normal chat to plain OpenCode mode.`
|
|
47
47
|
)
|
|
48
48
|
}
|
package/lib/commands/help.ts
CHANGED
|
@@ -24,18 +24,27 @@ export async function handleHelp(
|
|
|
24
24
|
`🟠 **Domain:** \`${domain}\`\n\n` +
|
|
25
25
|
`---\n\n` +
|
|
26
26
|
`**Commands**\n\n` +
|
|
27
|
-
`\`/revela enable\` —
|
|
28
|
-
`\`/revela disable\` — disable Revela mode\n` +
|
|
27
|
+
`\`/revela enable\` — optional ambient narrative mode for normal chat\n` +
|
|
28
|
+
`\`/revela disable\` — disable ambient Revela mode\n` +
|
|
29
29
|
`\`/revela init\` — initialize or refresh workspace DECKS.json\n` +
|
|
30
|
-
`\`/revela
|
|
31
|
-
`\`/revela
|
|
32
|
-
`\`/revela
|
|
33
|
-
`\`/revela
|
|
34
|
-
`\`/revela deck
|
|
30
|
+
`\`/revela research\` — research, bind evidence, and reduce story gaps\n` +
|
|
31
|
+
`\`/revela story\` — open the read-only story workspace UI\n` +
|
|
32
|
+
`\`/revela review\` — legacy readiness report for story state\n` +
|
|
33
|
+
`\`/revela narrative\` — compatibility alias for /revela story\n` +
|
|
34
|
+
`\`/revela make deck\` — make a deck from approved story state\n` +
|
|
35
|
+
`\`/revela make deck --review\` — review deck/artifact readiness before writing HTML\n` +
|
|
36
|
+
`\`/revela make brief [file.md]\` — render executive brief from approved story\n` +
|
|
37
|
+
`\`/revela deck\` — compatibility alias for /revela make deck\n` +
|
|
38
|
+
`\`/revela brief [file.md]\` — compatibility alias for /revela make brief\n` +
|
|
35
39
|
`\`/revela refine\` — open unified reading, inspection, and editing workspace\n` +
|
|
36
40
|
`\`/revela edit\` — deprecated compatibility shim to /revela refine Edit\n` +
|
|
37
41
|
`\`/revela inspect\` — deprecated compatibility shim to /revela refine Inspect\n` +
|
|
38
42
|
`\`/revela remember <text>\` — save an explicit preference to DECKS.json\n` +
|
|
43
|
+
`\`/revela design\` — list installed designs\n` +
|
|
44
|
+
`\`/revela design use <name>\` — activate a design\n` +
|
|
45
|
+
`\`/revela design new <name>\` — create a custom design with AI\n` +
|
|
46
|
+
`\`/revela design edit <name>\` — refine an existing custom design with AI\n` +
|
|
47
|
+
`\`/revela design preview [name]\` — open a design preview in browser\n` +
|
|
39
48
|
`\`/revela designs\` — list installed designs\n` +
|
|
40
49
|
`\`/revela designs <name>\` — activate a design\n` +
|
|
41
50
|
`\`/revela designs-new <name>\` — create a new custom design with AI\n` +
|
package/lib/commands/init.ts
CHANGED
|
@@ -17,7 +17,7 @@ Goal:
|
|
|
17
17
|
- Build or update ${DECKS_STATE_FILE}, the workspace-level machine-readable state file for Revela narrative and artifact work.
|
|
18
18
|
- Use the \`revela-decks\` tool for state updates. Do not write or patch ${DECKS_STATE_FILE} directly.
|
|
19
19
|
- Capture stable narrative context first: primary audience, belief before, belief after, decision/action, thesis, central claims, evidence availability, objections, risks, available source materials, existing artifact history, and open questions.
|
|
20
|
-
- Do not treat initialization as permission to write a deck. Narrative readiness is reviewed later by \`/revela
|
|
20
|
+
- Do not treat initialization as permission to write a deck. Narrative readiness is reviewed later by \`/revela story\`; deck/artifact readiness is reviewed by \`/revela make deck --review\`.
|
|
21
21
|
- Do not require slide count, visual style, design selection, output path, layout choices, or component choices during narrative initialization unless the user explicitly asks to render a deck now.
|
|
22
22
|
- ${DECKS_STATE_FILE} is the compatibility workspace-state file. Deck specs are render-target projections, not the center of initialization.
|
|
23
23
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { DECKS_STATE_FILE } from "../decks-state"
|
|
2
|
+
|
|
3
|
+
export function buildResearchPrompt({
|
|
4
|
+
exists,
|
|
5
|
+
workspaceRoot,
|
|
6
|
+
}: {
|
|
7
|
+
exists: boolean
|
|
8
|
+
workspaceRoot?: string
|
|
9
|
+
}): string {
|
|
10
|
+
const state = exists
|
|
11
|
+
? `${DECKS_STATE_FILE} exists. Read it through the revela-decks tool before researching.`
|
|
12
|
+
: `${DECKS_STATE_FILE} does not exist yet. Do not start broad internet research; initialize the workspace first with /revela init unless the user supplied a specific research question in chat.`
|
|
13
|
+
|
|
14
|
+
return `Run Revela closed-loop research.
|
|
15
|
+
|
|
16
|
+
Goal:
|
|
17
|
+
- Reduce open gaps, unsupported scope, weak evidence, unattached findings, and overextended relation rationale for the current story.
|
|
18
|
+
- Drive research from canonical narrative gaps: unsupported central claims, objections, risks, decision questions, explicit researchGaps, and claim_chain_gap warnings.
|
|
19
|
+
- Treat /revela research as authorization to bind clearly supported findings into canonical evidence without asking for item-by-item user confirmation.
|
|
20
|
+
- Preserve evidence boundaries: eliminate caveats only when evidence or narrower wording actually resolves them; otherwise keep precise caveats visible.
|
|
21
|
+
- Do not write decks, briefs, or design artifacts during research.
|
|
22
|
+
|
|
23
|
+
Current state:
|
|
24
|
+
- ${state}
|
|
25
|
+
${workspaceRoot ? `- Current workspace root: \`${workspaceRoot}\`` : ""}
|
|
26
|
+
|
|
27
|
+
Closed-loop workflow:
|
|
28
|
+
1. Call \`revela-decks\` action \`read\`, then \`reviewNarrative\`.
|
|
29
|
+
2. If current research gaps are missing or stale, call \`deriveResearchGaps\` when useful. Do not invent gaps that are not tied to a claim, objection, risk, decision, or narrative issue.
|
|
30
|
+
3. Run up to 3 research loops unless the stop conditions below are met earlier.
|
|
31
|
+
4. At the start of each loop, choose the highest-value 2-3 targets: open/in-progress high-priority gaps, unattached saved findings, unsupported central claims, weak evidence, high-priority objections/risks, and claim_chain_gap relations. Prefer targets that can improve readiness or materially reduce caveats. Do not repeat searches for claims already strongly supported.
|
|
32
|
+
5. If a target already has saved findings, prioritize binding/narrowing before doing more external search.
|
|
33
|
+
6. For targets needing external evidence, mark matching gaps \`in_progress\` with \`revela-decks updateResearchGap\`, then delegate search to the \`revela-research\` subagent. Ask it for source URLs, quotes/snippets, dates or locations when available, caveats, remaining gaps, and a \`## Recommended evidence bindings\` section with claimId, quote, source, supportScope, unsupportedScope, caveat, and strength. Save findings with \`revela-research-save\` under \`researches/{topic}/{axis}.md\` using \`## Data\`, \`## Cases\`, \`## Images\`, and \`## Gaps\` sections as applicable.
|
|
34
|
+
7. After findings are saved or existing findings are selected, read or inspect the findings file. Attach it with \`revela-decks attachResearchFindings\` when it maps to an existing research axis.
|
|
35
|
+
8. Automatically bind evidence when all binding criteria are met. Use \`revela-decks applyEvidenceCandidates\` for concrete candidate ids when available, or \`upsertNarrative\` to preserve canonical evidence bindings with exact source, URL/path, quote/snippet, support scope, unsupported scope, caveat, and strength.
|
|
36
|
+
9. Binding criteria: claimId exists; quote/snippet is traceable to the source and is not invented; source URL or workspace source path is present; supportScope and unsupportedScope are explicit; strength is strong or useful partial; caveat is preserved; binding does not expand the claim beyond the evidence.
|
|
37
|
+
10. If a claim or relation is broader than the evidence, narrow the claim text, supportedScope, unsupportedScope, or relation rationale through \`upsertNarrative\` only when the narrower wording preserves the user's strategic meaning and source boundaries. Do not silently change the decision ask, central recommendation, or approval meaning.
|
|
38
|
+
11. Update matching gaps after binding: use \`evidence_bound\` when canonical evidence was added, \`closed\` when the gap is resolved or non-researchable, \`findings_saved\` only when findings exist but binding criteria are not met, and \`open\` with notes when more external research is still warranted.
|
|
39
|
+
12. Re-run \`reviewNarrative\` after each loop. Compare against the previous loop: fewer open gaps, fewer unattached findings, stronger evidence, narrower unsupported scope, or clearer internal-data caveats should count as progress.
|
|
40
|
+
|
|
41
|
+
Stop conditions:
|
|
42
|
+
- No open externally researchable gaps remain.
|
|
43
|
+
- All useful saved findings have been attached or evidence-bound.
|
|
44
|
+
- A full loop produces no new bindable evidence, narrower wording, or gap status improvement.
|
|
45
|
+
- Remaining gaps require internal user/company data, confidential sources, or strategic judgment.
|
|
46
|
+
- 3 loops have completed.
|
|
47
|
+
|
|
48
|
+
Report format:
|
|
49
|
+
- Start with \`Research loop completed after <n> round(s).\`
|
|
50
|
+
- List bound evidence by claim id and count.
|
|
51
|
+
- List gaps closed or moved to evidence_bound.
|
|
52
|
+
- List claims or relations narrowed, with the remaining unsupported scope.
|
|
53
|
+
- List remaining caveats only as one of: \`internal_data_needed\`, \`not_publicly_researchable\`, \`source_quality_limit\`, or \`still_open\`.
|
|
54
|
+
- If no binding happened, say why binding criteria failed and what exact source type is needed next.
|
|
55
|
+
- End with the next smallest story action, not a generic request for user confirmation.
|
|
56
|
+
|
|
57
|
+
Rules:
|
|
58
|
+
- Do not use primary-agent broad websearch. Use the \`revela-research\` subagent for external search.
|
|
59
|
+
- Do not invent quotes, source paths, URLs, page references, locations, or caveats.
|
|
60
|
+
- Do not treat \`researches/**/*.md\` as canonical evidence until attached or evidence-bound, but do not stop at findings_saved when binding criteria are met.
|
|
61
|
+
- Do not mutate canonical claims merely to fit a source; narrow only to preserve evidence boundaries and avoid overstated claims.
|
|
62
|
+
- Do not ask the user to approve each evidence binding. Ask only when binding would change strategic meaning, downgrade a central claim, rely on suspicious sources, or require narrative approval.
|
|
63
|
+
- Do not store secrets, credentials, tokens, or sensitive personal information.
|
|
64
|
+
|
|
65
|
+
Start now by reading ${DECKS_STATE_FILE} through \`revela-decks\`, reviewing current readiness, and running the first research/binding loop.`
|
|
66
|
+
}
|
package/lib/commands/review.ts
CHANGED
|
@@ -18,7 +18,7 @@ Goal:
|
|
|
18
18
|
- Treat this as a narrative readiness review, not a deck HTML write-readiness review.
|
|
19
19
|
- Do not write, patch, or directly edit ${DECKS_STATE_FILE}. Use the \`revela-decks\` tool for all state changes.
|
|
20
20
|
- Call \`revela-decks\` action \`reviewNarrative\` as the authoritative deterministic readiness engine.
|
|
21
|
-
- Do not call \`revela-decks\` action \`review\` here. That action is the deck/artifact gate and belongs to \`/revela deck --review\`.
|
|
21
|
+
- Do not call \`revela-decks\` action \`review\` here. That action is the deck/artifact gate and belongs to \`/revela make deck --review\`.
|
|
22
22
|
- Do not treat legacy \`writeReadiness.status\`, old review snapshots, or an existing HTML deck as narrative approval.
|
|
23
23
|
- Do not write or overwrite \`decks/*.html\` during narrative review.
|
|
24
24
|
- If the narrative is \`ready_for_approval\`, ask whether the user wants to approve it or revise it. Do not approve automatically.
|
|
@@ -50,7 +50,7 @@ Report format:
|
|
|
50
50
|
- If warnings exist, list them after blockers as residual risks.
|
|
51
51
|
- If approval is missing, ask whether the user wants to approve the narrative or revise it.
|
|
52
52
|
- If approval is stale, say the prior approval no longer matches the current narrative hash.
|
|
53
|
-
- Keep deck/artifact readiness separate. If the user wants to review slide-writing readiness, tell them to run \`/revela deck --review\`.
|
|
53
|
+
- Keep deck/artifact readiness separate. If the user wants to review slide-writing readiness, tell them to run \`/revela make deck --review\`.
|
|
54
54
|
|
|
55
55
|
Rules:
|
|
56
56
|
- Do not write or overwrite \`decks/*.html\` during narrative review.
|
|
@@ -128,7 +128,7 @@ export function buildDeckReviewPrompt({
|
|
|
128
128
|
|
|
129
129
|
Goal:
|
|
130
130
|
- Use ${DECKS_STATE_FILE} as the source of truth for whether the current workspace deck is ready to be written to \`decks/*.html\`.
|
|
131
|
-
- Treat this as an artifact gate for deck rendering, not strategic narrative approval. Narrative readiness
|
|
131
|
+
- Treat this as an artifact gate for deck rendering, not strategic narrative approval. Narrative readiness reports are reviewed by \`/revela review\`.
|
|
132
132
|
- Preserve the deck spec for future sessions: every slide's content, layout, components, evidence, visuals, production status, and the 0.9 narrative compiler brief when available.
|
|
133
133
|
- Do not write, patch, or directly edit ${DECKS_STATE_FILE}. Use the \`revela-decks\` tool for all state changes.
|
|
134
134
|
- Let \`revela-decks\` action \`review\` compute writeReadiness; do not manually set readiness to ready.
|
package/lib/decks-state.ts
CHANGED
|
@@ -564,7 +564,7 @@ export function evaluateDeckStateWriteReadiness(state: DecksState, filePath: str
|
|
|
564
564
|
type: "missing_slide_spec",
|
|
565
565
|
severity: "blocker",
|
|
566
566
|
message,
|
|
567
|
-
suggestedAction: "Run /revela review and resolve all readiness blockers before writing deck HTML.",
|
|
567
|
+
suggestedAction: "Run /revela make deck --review and resolve all readiness blockers before writing deck HTML.",
|
|
568
568
|
})
|
|
569
569
|
}
|
|
570
570
|
if (deck.writeReadiness.blockers.length > 0) {
|
|
@@ -574,7 +574,7 @@ export function evaluateDeckStateWriteReadiness(state: DecksState, filePath: str
|
|
|
574
574
|
type: "missing_slide_spec",
|
|
575
575
|
severity: "blocker",
|
|
576
576
|
message,
|
|
577
|
-
suggestedAction: "Resolve the stored writeReadiness blockers and rerun /revela review.",
|
|
577
|
+
suggestedAction: "Resolve the stored writeReadiness blockers and rerun /revela make deck --review.",
|
|
578
578
|
})
|
|
579
579
|
}
|
|
580
580
|
if (normalized.reviews.length > 0) {
|
|
@@ -587,7 +587,7 @@ export function evaluateDeckStateWriteReadiness(state: DecksState, filePath: str
|
|
|
587
587
|
type: "missing_slide_spec",
|
|
588
588
|
severity: "blocker",
|
|
589
589
|
message,
|
|
590
|
-
suggestedAction: "Run /revela review so readiness is recorded against the current active render target.",
|
|
590
|
+
suggestedAction: "Run /revela make deck --review so readiness is recorded against the current active render target.",
|
|
591
591
|
})
|
|
592
592
|
} else if (!isReviewSnapshotCurrent(normalized, snapshot, deck.slug)) {
|
|
593
593
|
const message = "Latest review snapshot is stale for the current deck, sources, evidence, narrative state, or render target"
|
|
@@ -596,7 +596,7 @@ export function evaluateDeckStateWriteReadiness(state: DecksState, filePath: str
|
|
|
596
596
|
type: "missing_slide_spec",
|
|
597
597
|
severity: "blocker",
|
|
598
598
|
message,
|
|
599
|
-
suggestedAction: "Run /revela review again after the latest state changes before writing deck HTML.",
|
|
599
|
+
suggestedAction: "Run /revela make deck --review again after the latest state changes before writing deck HTML.",
|
|
600
600
|
})
|
|
601
601
|
} else if (snapshot.status !== "ready") {
|
|
602
602
|
const message = `Latest review snapshot is ${snapshot.status}, not ready`
|
|
@@ -605,7 +605,7 @@ export function evaluateDeckStateWriteReadiness(state: DecksState, filePath: str
|
|
|
605
605
|
type: "missing_slide_spec",
|
|
606
606
|
severity: "blocker",
|
|
607
607
|
message,
|
|
608
|
-
suggestedAction: "Resolve review blockers and rerun /revela review before writing deck HTML.",
|
|
608
|
+
suggestedAction: "Resolve review blockers and rerun /revela make deck --review before writing deck HTML.",
|
|
609
609
|
})
|
|
610
610
|
}
|
|
611
611
|
}
|
package/package.json
CHANGED
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") {
|
|
@@ -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 {
|