@cyber-dash-tech/revela 0.15.1 → 0.15.3
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 +58 -104
- package/README.zh-CN.md +59 -104
- package/designs/starter/DESIGN.md +33 -14
- package/designs/starter/preview.html +23 -16
- package/designs/summit/DESIGN.md +35 -42
- package/designs/summit/preview.html +49 -49
- package/lib/commands/brief.ts +1 -1
- package/lib/commands/designs-new.ts +6 -6
- package/lib/commands/designs.ts +9 -9
- package/lib/commands/domains.ts +9 -9
- package/lib/commands/edit.ts +1 -1
- package/lib/commands/help.ts +34 -37
- package/lib/commands/init.ts +1 -1
- package/lib/commands/inspect.ts +2 -20
- package/lib/commands/narrative.ts +3 -3
- package/lib/commands/pdf.ts +3 -3
- package/lib/commands/pptx.ts +2 -2
- package/lib/commands/review.ts +13 -6
- package/lib/decks-state.ts +6 -6
- package/lib/edit/deck-state.ts +1 -1
- package/lib/edit/prompt.ts +12 -0
- package/lib/edit/resolve-deck.ts +1 -1
- package/lib/inspect/prompt.ts +6 -1
- package/lib/inspect/server.ts +2 -2
- package/lib/media/download.ts +36 -11
- package/lib/media/save.ts +24 -0
- package/lib/media/search.ts +385 -0
- package/lib/media/types.ts +12 -0
- package/lib/prompt-builder.ts +20 -14
- package/lib/qa/checks.ts +2 -1
- package/lib/qa/index.ts +73 -2
- package/lib/refine/server.ts +758 -68
- package/package.json +1 -1
- package/plugin.ts +133 -283
- package/skill/NARRATIVE_SKILL.md +15 -17
- package/skill/SKILL.md +220 -477
- package/tools/edit.ts +1 -1
- package/tools/inspection-result.ts +1 -1
- package/tools/media-save.ts +6 -0
- package/lib/commands/disable.ts +0 -14
- package/lib/commands/enable.ts +0 -48
package/lib/commands/inspect.ts
CHANGED
|
@@ -1,25 +1,7 @@
|
|
|
1
|
-
import { openRefineDeck } from "../refine/open"
|
|
2
|
-
|
|
3
1
|
export async function handleInspect(
|
|
4
2
|
options: { client: any; sessionID: string; workspaceRoot: string; openBrowser?: boolean },
|
|
5
3
|
send: (text: string) => Promise<void>,
|
|
6
4
|
): Promise<void> {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
client: options.client,
|
|
10
|
-
sessionID: options.sessionID,
|
|
11
|
-
workspaceRoot: options.workspaceRoot,
|
|
12
|
-
mode: "inspect",
|
|
13
|
-
openBrowser: options.openBrowser,
|
|
14
|
-
})
|
|
15
|
-
await send(
|
|
16
|
-
`\`/revela inspect\` is deprecated. Opened \`/revela refine\` in Inspect mode for the active HTML deck.\n` +
|
|
17
|
-
`File: \`${result.deck.file}\`\n` +
|
|
18
|
-
`${result.stateNote}\n` +
|
|
19
|
-
`URL: ${result.url}\n\n` +
|
|
20
|
-
`Use \`/revela refine\` directly going forward. Use Ctrl/Cmd-click in the browser to reference deck elements, then use the Inspect tab for read-only Source/Purpose review. There is no chat box or freeform prompt.`
|
|
21
|
-
)
|
|
22
|
-
} catch (e: any) {
|
|
23
|
-
await send(`**Inspect failed:** ${e.message || String(e)}`)
|
|
24
|
-
}
|
|
5
|
+
void options
|
|
6
|
+
await send("`/revela inspect` is no longer a public command. Use `/revela refine --deck` and the Inspect tab for grounded Source/Purpose/Narrative Reading.")
|
|
25
7
|
}
|
|
@@ -34,7 +34,7 @@ export function parseStoryArgs(param: string): ParseStoryArgsResult {
|
|
|
34
34
|
if (token.startsWith("--language=")) {
|
|
35
35
|
return { ok: false, error: "Usage: `/revela story --language <language>` or `/revela story -l <language>`. Do not use `--language=<language>`." }
|
|
36
36
|
}
|
|
37
|
-
return { ok: false, error: "Usage: `/revela story [--language <language> | -l <language>]`.
|
|
37
|
+
return { ok: false, error: "Usage: `/revela story [--language <language> | -l <language>]`." }
|
|
38
38
|
}
|
|
39
39
|
|
|
40
40
|
return { ok: true, args: { language } }
|
|
@@ -130,9 +130,9 @@ export function buildNarrativeViewPrompt(options: { workspaceRoot: string; langu
|
|
|
130
130
|
return `Prepare the read-only Revela narrative UI display model.
|
|
131
131
|
|
|
132
132
|
Target language request: ${options.language}
|
|
133
|
-
- The language value is passed from the user's /revela
|
|
133
|
+
- The language value is passed from the user's /revela story arguments. Interpret it as the desired UI/display language.
|
|
134
134
|
- Examples: --cn maps to zh-CN, --jp maps to ja-JP, while --fr, --de, --es, --ko, --Arabic, --Portuguese-BR, or a written language name should be localized normally into that requested language.
|
|
135
|
-
- Default /revela
|
|
135
|
+
- Default /revela story language is en when the user provides no language request.
|
|
136
136
|
|
|
137
137
|
You must call the \`revela-narrative-view\` tool exactly once.
|
|
138
138
|
|
package/lib/commands/pdf.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lib/commands/pdf.ts
|
|
3
3
|
*
|
|
4
|
-
* Handler for `/revela pdf <file_path>` — exports an HTML slide deck to PDF.
|
|
4
|
+
* Handler for `/revela export --deck pdf <file_path>` — exports an HTML slide deck to PDF.
|
|
5
5
|
*
|
|
6
6
|
* Output: same directory and base name as the input, with .pdf extension.
|
|
7
7
|
* Example: decks/my-deck.html → decks/my-deck.pdf
|
|
@@ -24,8 +24,8 @@ export async function handlePdf(
|
|
|
24
24
|
|
|
25
25
|
if (!resolvedFile) {
|
|
26
26
|
await send(
|
|
27
|
-
"**Usage:** `/revela pdf [file_path]`\n\n" +
|
|
28
|
-
"Example: `/revela pdf decks/my-deck.html`"
|
|
27
|
+
"**Usage:** `/revela export --deck pdf [file_path]`\n\n" +
|
|
28
|
+
"Example: `/revela export --deck pdf decks/my-deck.html`"
|
|
29
29
|
)
|
|
30
30
|
return
|
|
31
31
|
}
|
package/lib/commands/pptx.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* lib/commands/pptx.ts
|
|
3
3
|
*
|
|
4
|
-
* Handler for `/revela pptx [file_path]` — exports an HTML slide deck to PPTX.
|
|
4
|
+
* Handler for `/revela export --deck pptx [file_path]` — exports an HTML slide deck to PPTX.
|
|
5
5
|
*
|
|
6
6
|
* Output: same directory and base name as the input, with .pptx extension.
|
|
7
7
|
* Example: decks/my-deck.html → decks/my-deck.pptx
|
|
@@ -64,7 +64,7 @@ export function resolvePptxDeck(workspaceRoot: string, filePath = ""): ResolvedP
|
|
|
64
64
|
throw new Error("No deck HTML found in decks/. Generate a deck first or pass a file path.")
|
|
65
65
|
}
|
|
66
66
|
if (htmlFiles.length > 1) {
|
|
67
|
-
throw new Error("This workspace contains multiple deck HTML files. Run `/revela pptx decks/<file>.html` to choose one.")
|
|
67
|
+
throw new Error("This workspace contains multiple deck HTML files. Run `/revela export --deck pptx decks/<file>.html` to choose one.")
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
const absoluteFile = resolve(root, htmlFiles[0])
|
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
|
|
21
|
+
- Do not call \`revela-decks\` action \`review\` here. That action is the deck/artifact gate inside \`/revela make --deck\` after the deck plan is confirmed.
|
|
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
|
|
53
|
+
- Keep deck/artifact readiness separate. If the user wants to write or review deck artifacts, tell them to run \`/revela make --deck\`.
|
|
54
54
|
|
|
55
55
|
Rules:
|
|
56
56
|
- Do not write or overwrite \`decks/*.html\` during narrative review.
|
|
@@ -79,6 +79,7 @@ Goal:
|
|
|
79
79
|
- Treat this as the explicit transition from approved narrative state to user-confirmed deck planning.
|
|
80
80
|
- Use the deck-render prompt mode for design, layout, component, HTML, QA, and deck artifact rules.
|
|
81
81
|
- Default behavior is two-stage: first show the compiled deck plan with low-fidelity layout sketches, then stop for user confirmation before any artifact review or HTML writing.
|
|
82
|
+
- Every deck plan must include Cover, Table of Contents, and Closing slides. The TOC must show 3-5 chapter headings that match the deck's slide groups.
|
|
82
83
|
- Do not write or overwrite \`decks/*.html\` until the narrative handoff, explicit user deck-plan confirmation, and deck/artifact gate are all satisfied.
|
|
83
84
|
- Do not treat legacy \`writeReadiness.status\`, old review snapshots, or existing HTML decks as narrative approval.
|
|
84
85
|
- Do not bypass the deck HTML contract, review snapshot freshness, source-trace expectations, or export preflight protections.
|
|
@@ -93,19 +94,22 @@ Workflow:
|
|
|
93
94
|
3. If narrative readiness is \`approved\`, continue. If it is \`ready_for_approval\`, ask the user for explicit approval before continuing. If it is blocked, stale, or needs research, stop and report the smallest next action. Do not call \`approveNarrative\` unless the user explicitly approves or requests a render override.
|
|
94
95
|
4. After approval or explicit render override exists, call \`revela-decks\` action \`compileDeckPlan\`. This projects canonical narrative claims and evidence bindings into compatibility \`slides[]\` and \`slides[].evidence[]\`; it must not write HTML.
|
|
95
96
|
5. If \`compileDeckPlan\` returns \`skipped\`, stop and report the reason. Do not invent slide specs manually to bypass approval.
|
|
96
|
-
6. Present the compiled deck plan to the user and include a low-fidelity layout sketch for every slide. The sketch is ASCII/text structure only; do not generate visual images or HTML mockups.
|
|
97
|
+
6. Present the compiled deck plan to the user and include a low-fidelity layout sketch for every slide. The plan must identify the chapter structure first: 3-5 chapter headings, each chapter's slide range, and which non-structural slides belong to each chapter. The sketch is ASCII/text structure only; do not generate visual images or HTML mockups.
|
|
97
98
|
7. Stop after presenting the plan. Ask the user to confirm or request changes. Do not call \`revela-decks review\`, do not fetch design context, and do not write HTML in the same turn unless the user had already explicitly confirmed the current plan before this command.
|
|
98
99
|
8. Only after explicit user confirmation of the current slide plan, call \`revela-decks\` action \`confirmDeckPlan\` with \`approvalBy=user\` and a compact \`approvalNote\`.
|
|
99
100
|
9. After confirmation is recorded, ask for or confirm visual design only after the narrative deck plan exists. Fetch required design layouts/components with \`revela-designs read\` as needed.
|
|
100
101
|
10. Update only deck/artifact metadata through \`revela-decks upsertDeck\` / \`upsertSlides\` when required by confirmed design/layout choices. Do not change canonical narrative claims unless the user asks to revise the narrative.
|
|
101
102
|
11. Call \`revela-decks\` action \`review\` as the artifact gate. It computes \`writeReadiness\` and review snapshots for deck HTML writing. If it reports \`slide_plan_unconfirmed\`, stop and ask for explicit deck-plan confirmation.
|
|
102
|
-
12. Write \`decks/*.html\` only if the deck/artifact gate is ready and all deck HTML contract requirements can be satisfied.
|
|
103
|
-
13.
|
|
103
|
+
12. Write \`decks/*.html\` only if the deck/artifact gate is ready and all deck HTML contract requirements can be satisfied. Generate the artifact chapter by chapter instead of drafting all content slides in one broad pass. Keep the HTML file valid after every write, preserve already-written slides, and update one chapter's slide sections at a time.
|
|
104
|
+
13. For each chapter, make every content slide carry a distinct claim, evidence item, comparison, risk, or action. If a chapter lacks enough substance for its allocated slides, merge weak slides or reduce the slide count instead of creating sparse filler.
|
|
105
|
+
14. After each HTML write, the system automatically runs artifact QA before opening Refine. If post-write artifact QA reports hard errors, fix them and let QA run again. Refine opens only after hard errors pass. Density warnings about thin claim/evidence substance should be reported and improved when useful, but they do not block Refine.
|
|
104
106
|
|
|
105
107
|
Deck plan report format:
|
|
106
108
|
- Start with \`Deck plan: awaiting confirmation\` when a plan was compiled and has not yet been confirmed.
|
|
107
109
|
- Include narrative readiness status and narrative hash when available.
|
|
108
110
|
- Include whether \`compileDeckPlan\` compiled or skipped.
|
|
111
|
+
- Include \`Required structure: Cover + Table of Contents + Closing\` and do not omit any of those slides.
|
|
112
|
+
- Include a \`Chapters\` section before the slide list. It must list 3-5 TOC headings, their slide ranges, and the non-structural slides assigned to each chapter.
|
|
109
113
|
- For every slide, include: slide index, title, purpose, narrative role, low-fidelity layout sketch, layout, components, primary/supporting claim ids, evidence binding ids or source summary, visual intent, and caveats/unsupported scope.
|
|
110
114
|
- Use this sketch style or similarly simple ASCII boxes:
|
|
111
115
|
|
|
@@ -135,12 +139,15 @@ Caveats / unsupported scope:
|
|
|
135
139
|
Report format before any HTML write after confirmation:
|
|
136
140
|
- Start with \`Deck handoff: <status>\`.
|
|
137
141
|
- Include which user-confirmed plan, approved narrative hash, and deck review snapshot authorized the artifact work.
|
|
142
|
+
- Include the chapter currently being generated and confirm already-written slides are being preserved.
|
|
138
143
|
- If deck/artifact review is blocked, list blockers separately from narrative blockers.
|
|
139
144
|
- After writing HTML, read the appended \`Artifact QA\` report from the tool output. If it failed, fix hard errors before considering the deck ready for Refine.
|
|
140
145
|
|
|
141
146
|
Rules:
|
|
142
147
|
- \`compileDeckPlan\` is the canonical narrative-to-deck planning path. Do not manually invent slide specs to avoid it.
|
|
143
148
|
- Deck slide specs are render-target projections. Canonical narrative remains the authority for audience, decision, claims, evidence boundaries, objections, risks, and approval.
|
|
149
|
+
- Cover, Table of Contents, and Closing are mandatory deck structure. TOC chapter headings must match the chapter grouping used for generation.
|
|
150
|
+
- Do not generate the complete deck content in one broad pass after confirmation. Work chapter by chapter while keeping the artifact valid after each write.
|
|
144
151
|
- Applying evidence candidates, rewriting canonical claims, or approving narratives requires explicit user instruction.
|
|
145
152
|
- If the user requests slide order, layout, component, or visual-intent changes that do not alter meaning, update only the deck projection through \`upsertSlides\` and present the revised plan for confirmation.
|
|
146
153
|
- If the user requests claim, evidence, caveat, decision, or recommendation meaning changes, update canonical narrative first and rerun narrative review/approval or explicit render override before compiling a new deck plan.
|
|
@@ -165,7 +172,7 @@ export function buildDeckReviewPrompt({
|
|
|
165
172
|
|
|
166
173
|
Goal:
|
|
167
174
|
- Use ${DECKS_STATE_FILE} as the source of truth for whether the current workspace deck is ready to be written to \`decks/*.html\`.
|
|
168
|
-
- Treat this as an artifact gate for deck rendering, not strategic narrative approval. Narrative readiness
|
|
175
|
+
- Treat this as an artifact gate for deck rendering, not strategic narrative approval. Narrative readiness is reviewed through \`/revela story\`.
|
|
169
176
|
- 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.
|
|
170
177
|
- Do not write, patch, or directly edit ${DECKS_STATE_FILE}. Use the \`revela-decks\` tool for all state changes.
|
|
171
178
|
- Let \`revela-decks\` action \`review\` compute writeReadiness; do not manually set readiness to ready.
|
package/lib/decks-state.ts
CHANGED
|
@@ -661,7 +661,7 @@ export function evaluateDeckStateWriteReadiness(state: DecksState, filePath: str
|
|
|
661
661
|
type: "missing_slide_spec",
|
|
662
662
|
severity: "blocker",
|
|
663
663
|
message,
|
|
664
|
-
suggestedAction: "Run /revela make deck
|
|
664
|
+
suggestedAction: "Run /revela make --deck and resolve all readiness blockers before writing deck HTML.",
|
|
665
665
|
})
|
|
666
666
|
}
|
|
667
667
|
if (deck.writeReadiness.blockers.length > 0) {
|
|
@@ -671,7 +671,7 @@ export function evaluateDeckStateWriteReadiness(state: DecksState, filePath: str
|
|
|
671
671
|
type: "missing_slide_spec",
|
|
672
672
|
severity: "blocker",
|
|
673
673
|
message,
|
|
674
|
-
suggestedAction: "Resolve the stored writeReadiness blockers and rerun /revela make deck
|
|
674
|
+
suggestedAction: "Resolve the stored writeReadiness blockers and rerun /revela make --deck.",
|
|
675
675
|
})
|
|
676
676
|
}
|
|
677
677
|
if (normalized.reviews.length > 0) {
|
|
@@ -684,7 +684,7 @@ export function evaluateDeckStateWriteReadiness(state: DecksState, filePath: str
|
|
|
684
684
|
type: "missing_slide_spec",
|
|
685
685
|
severity: "blocker",
|
|
686
686
|
message,
|
|
687
|
-
suggestedAction: "Run /revela make deck
|
|
687
|
+
suggestedAction: "Run /revela make --deck so readiness is recorded against the current active render target.",
|
|
688
688
|
})
|
|
689
689
|
} else if (!isReviewSnapshotCurrent(normalized, snapshot, deck.slug)) {
|
|
690
690
|
const message = "Latest review snapshot is stale for the current deck, sources, evidence, narrative state, or render target"
|
|
@@ -693,7 +693,7 @@ export function evaluateDeckStateWriteReadiness(state: DecksState, filePath: str
|
|
|
693
693
|
type: "missing_slide_spec",
|
|
694
694
|
severity: "blocker",
|
|
695
695
|
message,
|
|
696
|
-
suggestedAction: "Run /revela make deck
|
|
696
|
+
suggestedAction: "Run /revela make --deck again after the latest state changes before writing deck HTML.",
|
|
697
697
|
})
|
|
698
698
|
} else if (snapshot.status !== "ready") {
|
|
699
699
|
const message = `Latest review snapshot is ${snapshot.status}, not ready`
|
|
@@ -702,7 +702,7 @@ export function evaluateDeckStateWriteReadiness(state: DecksState, filePath: str
|
|
|
702
702
|
type: "missing_slide_spec",
|
|
703
703
|
severity: "blocker",
|
|
704
704
|
message,
|
|
705
|
-
suggestedAction: "Resolve review blockers and rerun /revela make deck
|
|
705
|
+
suggestedAction: "Resolve review blockers and rerun /revela make --deck before writing deck HTML.",
|
|
706
706
|
})
|
|
707
707
|
}
|
|
708
708
|
}
|
|
@@ -748,7 +748,7 @@ export function buildDecksStatePromptLayer(workspaceRoot: string, maxChars = 140
|
|
|
748
748
|
}
|
|
749
749
|
let text = JSON.stringify(compact, null, 2)
|
|
750
750
|
if (text.length > maxChars) text = text.slice(0, maxChars).trimEnd() + "\n[DECKS.json state truncated for prompt size.]"
|
|
751
|
-
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, evidence, render targets, and write readiness.\n- The decks map is compatibility storage; operate only on the current workspace deck.\n- ${DECKS_STATE_FILE} deck slides use 1-based \`slides[].index\` values. Render every HTML \`<section class="slide">\` with a matching 1-based \`data-slide-index\` attribute, and do not use 0-based \`data-index\` as slide identity.\n- The active HTML deck is represented as a \`renderTarget\` of type \`html_deck\`; PDF/PPTX exports should be recorded as derived render targets, not as separate deck specs.\n- \`writeReadiness\` is a compatibility projection for the /revela make deck generation workflow, not a hard blocker for targeted artifact-level HTML fixes.\n- Do not edit ${DECKS_STATE_FILE} directly; use the revela-decks tool.\n- For /revela make deck generated HTML, use the current deck's outputPath and satisfy the deck HTML contract. For targeted artifact-level edits, patch the requested deck HTML directly without treating \`writeReadiness\` or \`planReview\` as a precondition.`
|
|
751
|
+
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, evidence, render targets, and write readiness.\n- The decks map is compatibility storage; operate only on the current workspace deck.\n- ${DECKS_STATE_FILE} deck slides use 1-based \`slides[].index\` values. Render every HTML \`<section class="slide">\` with a matching 1-based \`data-slide-index\` attribute, and do not use 0-based \`data-index\` as slide identity.\n- The active HTML deck is represented as a \`renderTarget\` of type \`html_deck\`; PDF/PPTX exports should be recorded as derived render targets, not as separate deck specs.\n- \`writeReadiness\` is a compatibility projection for the /revela make --deck generation workflow, not a hard blocker for targeted artifact-level HTML fixes.\n- Do not edit ${DECKS_STATE_FILE} directly; use the revela-decks tool.\n- For /revela make --deck generated HTML, use the current deck's outputPath and satisfy the deck HTML contract. For targeted artifact-level edits, patch the requested deck HTML directly without treating \`writeReadiness\` or \`planReview\` as a precondition.`
|
|
752
752
|
}
|
|
753
753
|
|
|
754
754
|
function compactWorkspaceForPrompt(workspace: DecksState["workspace"]): DecksState["workspace"] {
|
package/lib/edit/deck-state.ts
CHANGED
package/lib/edit/prompt.ts
CHANGED
|
@@ -24,6 +24,8 @@ export interface EditCommentPayload extends EditSelectedElementPayload {
|
|
|
24
24
|
comment: string
|
|
25
25
|
elements?: EditSelectedElementPayload[]
|
|
26
26
|
comments?: EditCommentDraftPayload[]
|
|
27
|
+
asset?: Record<string, unknown>
|
|
28
|
+
drop?: Record<string, unknown>
|
|
27
29
|
}
|
|
28
30
|
|
|
29
31
|
export function buildEditPrompt(payload: EditCommentPayload): string {
|
|
@@ -54,6 +56,8 @@ export function buildEditPrompt(payload: EditCommentPayload): string {
|
|
|
54
56
|
deck: payload.deck,
|
|
55
57
|
file: payload.file,
|
|
56
58
|
comments,
|
|
59
|
+
asset: payload.asset,
|
|
60
|
+
drop: payload.drop,
|
|
57
61
|
}
|
|
58
62
|
|
|
59
63
|
return `The user left a visual edit comment on a Revela slide deck.
|
|
@@ -75,6 +79,14 @@ Instructions:
|
|
|
75
79
|
- Pure artifact polish such as layout, spacing, typography, alignment, color, image crop, animation, export fidelity, runtime JavaScript fixes, or deck HTML contract fixes may remain an artifact-level edit.
|
|
76
80
|
- If the request mixes content meaning and visual polish, treat it as narrative-impacting unless the user clarifies otherwise.
|
|
77
81
|
- Preserve the existing deck structure, active design language, typography, spacing system, animations, and slide count unless the comment explicitly asks otherwise.
|
|
82
|
+
- If an asset/drop payload is present, this is an asset placement request. Use only the saved local asset path from the asset payload in deck HTML. Prefer asset.deckPath when present because it is relative to the target HTML file; otherwise use asset.path.
|
|
83
|
+
- Do not write remote imageUrl, thumbnailUrl, source page URLs, or ${"`/__revela_asset`"} proxy URLs into deck HTML.
|
|
84
|
+
- Logo assets should remain small, clear, and brand-like; do not use logos as decorative backgrounds.
|
|
85
|
+
- Photography can be cropped or masked when appropriate, but must not cover text, charts, tables, evidence, or important claims.
|
|
86
|
+
- Screenshots, diagrams, charts, tables, and evidence images must remain readable and should not be converted into decorative hero imagery.
|
|
87
|
+
- For asset targetMode ${"`replace`"}, prefer replacing the targeted image or visual element. For ${"`insert-into`"}, place the asset inside the targeted card, media box, or semantic container while preserving that element's layout role. For ${"`add`"}, place the asset near the drop coordinates within the existing layout or semantic box. Do not invent a new visual system when the existing deck grammar can express the placement.
|
|
88
|
+
- If an asset payload is present without drop coordinates, use the user's comment and selected element context to choose placement; if placement remains ambiguous, ask one concise clarification question instead of guessing.
|
|
89
|
+
- Preserve source/license/attribution facts if you surface them in visible notes; do not invent missing licensing or attribution.
|
|
78
90
|
- Do not rewrite unrelated slides or broad sections of the deck.
|
|
79
91
|
- Locate each target primarily with slideIndex, slideTitle, selected text, nearbyText, and outerHTMLExcerpt. Use selector/domPath as hints; they may be approximate.
|
|
80
92
|
- For targeted artifact-level edits, patch ${"`decks/*.html`"} directly. Do not call ${"`revela-decks`"} action ${"`review`"} as a precondition, and do not let ${"`writeReadiness`"}, ${"`planReview`"}, or ${"`slide_plan_unconfirmed`"} block the patch.
|
package/lib/edit/resolve-deck.ts
CHANGED
|
@@ -12,7 +12,7 @@ export interface EditableDeck {
|
|
|
12
12
|
|
|
13
13
|
export function resolveEditableDeck(workspaceRoot: string, input = ""): EditableDeck {
|
|
14
14
|
if (input.trim()) {
|
|
15
|
-
throw new Error("/revela refine does not accept a target. It opens the active HTML deck or the only HTML deck in decks/.")
|
|
15
|
+
throw new Error("/revela refine --deck does not accept a target. It opens the active HTML deck or the only HTML deck in decks/.")
|
|
16
16
|
}
|
|
17
17
|
|
|
18
18
|
if (hasDecksState(workspaceRoot)) {
|
package/lib/inspect/prompt.ts
CHANGED
|
@@ -5,21 +5,25 @@ export function buildInspectionPrompt(input: {
|
|
|
5
5
|
file: string
|
|
6
6
|
projection: InspectionPromptProjection
|
|
7
7
|
language?: string
|
|
8
|
+
comment?: string
|
|
8
9
|
}): string {
|
|
9
10
|
const language = normalizeInspectLanguage(input.language)
|
|
11
|
+
const comment = typeof input.comment === "string" && input.comment.trim() ? input.comment.trim() : ""
|
|
10
12
|
return `A user selected slide content in Revela Evidence Inspector. The selection may contain one referenced element, a whole slide, or multiple referenced elements selected with Cmd/Ctrl-click.
|
|
11
13
|
|
|
12
14
|
Target file: ${input.file}
|
|
13
15
|
Inspection request id: ${input.requestId}
|
|
14
16
|
Display language: ${language}
|
|
17
|
+
User inspect comment: ${comment || "(none; explain purpose and source only)"}
|
|
15
18
|
|
|
16
|
-
Use the structured projection below to produce the final inspector cards. This is LLM judgment with grounded boundaries
|
|
19
|
+
Use the structured projection below to produce the final inspector cards. This is LLM judgment with grounded boundaries. The user's inspect comment is the complete request about the selected reference; do not parse it into a separate question field. The user primarily wants to understand the selected component: what purpose it serves and what source support exists. Use narrative reading and exploratory reading only as internal grounding unless needed to answer the user's comment. Do not edit files. Do not mutate DECKS.json. Do not invent claim ids, evidence binding ids, sources, quotes, URLs, page references, caveats, objections, risks, artifact coverage, or evidence not present in the projection.
|
|
17
20
|
|
|
18
21
|
Language boundary: the selected display language affects only human-readable card copy. Preserve all claim ids, canonical claim ids, evidence binding ids, source paths, findings files, URLs, numbers, quoted/source facts, caveats, artifact ids, and coverage statuses exactly as grounded in the projection. If the display language is Auto, use projection.deck.language when available; otherwise follow the user's/browser context or default to English.
|
|
19
22
|
|
|
20
23
|
Return the result only by calling the \`revela-inspection-result\` tool with this request id. Do not answer in chat.
|
|
21
24
|
|
|
22
25
|
Required card model:
|
|
26
|
+
- User inspect comment: if present, answer it through the Purpose and Source cards first. If it asks about trust, provenance, evidence, factuality, or where a number came from, prioritize Source. If it asks why something is on the slide or what it is doing, prioritize Purpose.
|
|
23
27
|
- Narrative Reading: when the projection includes a matched claim, preserve its claim id, canonical claim id, evidence binding ids, supported scope, unsupported scope, caveats, related objections, related risks, and artifact coverage. Artifact coverage must come only from projection.cards.artifacts; do not invent where a claim appears or whether an artifact is stale/current/partial/missing. If canonical narrative linkage is missing, say so and fall back to the matched slide claim; do not invent canonical ids.
|
|
24
28
|
- Candidate boundary: when projection.match.claim is absent but projection.match.candidateClaims is present, explain the selected child element only within those candidate claim boundaries. You may describe that the child element functions as a detail, prerequisite, source note, risk cue, or evidence cue inside the slide, but you must not select one candidate claim id by semantic guess. If projection.match.confidence is none or candidateClaims is empty, explain the mapping gap instead of inventing a plausible claim.
|
|
25
29
|
- Exploratory Reading: provide bounded, non-official reading cues for objection prep, audience reframing, appendix leads, and meeting prep only from the projection. Mark official as false. Keep missing evidence, caveats, unsupported scope, and stale artifacts visible. Do not make exploratory text sound like approved artifact content, and do not turn this into chat or a fix plan.
|
|
@@ -29,6 +33,7 @@ Required card model:
|
|
|
29
33
|
Boundaries:
|
|
30
34
|
- Do not hunt for problems. If it works, say it works.
|
|
31
35
|
- Do not recommend edits or fixes; this inspector view only explains narrative context, bounded exploratory reading context, purpose, and source credibility.
|
|
36
|
+
- Keep Purpose and Source concise and directly useful. Avoid long narrative-reading exposition unless the selected content cannot be explained without it.
|
|
32
37
|
- Do not turn every caveat into a problem.
|
|
33
38
|
- If confidence is low, use unclear or unknown instead of pretending certainty.
|
|
34
39
|
|
package/lib/inspect/server.ts
CHANGED
|
@@ -484,7 +484,7 @@ export function renderInspectorShell(token: string): string {
|
|
|
484
484
|
<aside>
|
|
485
485
|
<div>
|
|
486
486
|
<h1>Evidence Inspector</h1>
|
|
487
|
-
<p class="hint">Cmd/Ctrl-click slide elements to attach them as references
|
|
487
|
+
<p class="hint">Cmd/Ctrl-click slide elements to attach them as references in /revela refine --deck. Then click <b>Inspect Selection</b>. This is not chat.</p>
|
|
488
488
|
</div>
|
|
489
489
|
<div id="selection" class="selection">
|
|
490
490
|
<strong>Selection</strong>
|
|
@@ -632,7 +632,7 @@ export function renderInspectorShell(token: string): string {
|
|
|
632
632
|
if (bindAttempts >= 80) {
|
|
633
633
|
clearInterval(bindTimer);
|
|
634
634
|
bindTimer = 0;
|
|
635
|
-
setBindingStatus('error', 'Selection binding timed out. Reopen /revela
|
|
635
|
+
setBindingStatus('error', 'Selection binding timed out. Reopen /revela refine --deck or reload this page.');
|
|
636
636
|
}
|
|
637
637
|
}, 150);
|
|
638
638
|
}
|
package/lib/media/download.ts
CHANGED
|
@@ -3,11 +3,15 @@ import { extname } from "path"
|
|
|
3
3
|
const MIME_TO_EXT: Record<string, string> = {
|
|
4
4
|
"image/png": ".png",
|
|
5
5
|
"image/jpeg": ".jpg",
|
|
6
|
+
"image/svg+xml": ".svg",
|
|
6
7
|
"image/webp": ".webp",
|
|
7
8
|
"image/gif": ".gif",
|
|
9
|
+
"image/x-icon": ".ico",
|
|
10
|
+
"image/vnd.microsoft.icon": ".ico",
|
|
8
11
|
}
|
|
9
12
|
|
|
10
|
-
const ALLOWED_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".webp", ".gif"])
|
|
13
|
+
const ALLOWED_EXTENSIONS = new Set([".png", ".jpg", ".jpeg", ".svg", ".webp", ".gif", ".ico"])
|
|
14
|
+
const DEFAULT_DOWNLOAD_TIMEOUT_MS = 10_000
|
|
11
15
|
|
|
12
16
|
function normalizeExtension(ext: string): string {
|
|
13
17
|
const value = ext.toLowerCase()
|
|
@@ -30,6 +34,7 @@ export function inferImageExtension(contentType: string | null, sourceName = "")
|
|
|
30
34
|
|
|
31
35
|
export async function downloadImageFromUrl(
|
|
32
36
|
url: string,
|
|
37
|
+
options: { timeoutMs?: number } = {},
|
|
33
38
|
): Promise<{ buffer: Buffer; contentType: string | null; extension: string }> {
|
|
34
39
|
let parsed: URL
|
|
35
40
|
try {
|
|
@@ -42,16 +47,36 @@ export async function downloadImageFromUrl(
|
|
|
42
47
|
throw new Error("INVALID_URL")
|
|
43
48
|
}
|
|
44
49
|
|
|
45
|
-
const
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
})
|
|
50
|
+
const controller = new AbortController()
|
|
51
|
+
let timedOut = false
|
|
52
|
+
const timer = setTimeout(() => {
|
|
53
|
+
timedOut = true
|
|
54
|
+
controller.abort()
|
|
55
|
+
}, Math.max(1, options.timeoutMs ?? DEFAULT_DOWNLOAD_TIMEOUT_MS))
|
|
52
56
|
|
|
53
|
-
|
|
54
|
-
|
|
57
|
+
let response: Response
|
|
58
|
+
let buffer: Buffer
|
|
59
|
+
try {
|
|
60
|
+
response = await fetch(parsed, {
|
|
61
|
+
headers: {
|
|
62
|
+
Accept: "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8",
|
|
63
|
+
"User-Agent":
|
|
64
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 " +
|
|
65
|
+
"(KHTML, like Gecko) Chrome/125.0.0.0 Safari/537.36",
|
|
66
|
+
},
|
|
67
|
+
signal: controller.signal,
|
|
68
|
+
})
|
|
69
|
+
|
|
70
|
+
if (!response.ok) {
|
|
71
|
+
throw new Error(`DOWNLOAD_FAILED:${response.status}`)
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
buffer = Buffer.from(await response.arrayBuffer())
|
|
75
|
+
} catch (error) {
|
|
76
|
+
if (timedOut) throw new Error("DOWNLOAD_TIMEOUT")
|
|
77
|
+
throw error
|
|
78
|
+
} finally {
|
|
79
|
+
clearTimeout(timer)
|
|
55
80
|
}
|
|
56
81
|
|
|
57
82
|
const contentType = response.headers.get("content-type")
|
|
@@ -61,7 +86,7 @@ export async function downloadImageFromUrl(
|
|
|
61
86
|
}
|
|
62
87
|
|
|
63
88
|
return {
|
|
64
|
-
buffer
|
|
89
|
+
buffer,
|
|
65
90
|
contentType,
|
|
66
91
|
extension,
|
|
67
92
|
}
|
package/lib/media/save.ts
CHANGED
|
@@ -69,6 +69,12 @@ function buildFailureRecord(
|
|
|
69
69
|
alt: input.alt,
|
|
70
70
|
notes: input.notes,
|
|
71
71
|
failureReason,
|
|
72
|
+
provider: input.provider,
|
|
73
|
+
sourcePageUrl: input.sourcePageUrl,
|
|
74
|
+
license: input.license,
|
|
75
|
+
attribution: input.attribution,
|
|
76
|
+
width: input.width,
|
|
77
|
+
height: input.height,
|
|
72
78
|
savedAt: nowIso(),
|
|
73
79
|
}
|
|
74
80
|
}
|
|
@@ -112,6 +118,12 @@ function saveFailureResult(
|
|
|
112
118
|
sourcePath: sourcePath ?? existing.sourcePath,
|
|
113
119
|
alt: input.alt ?? existing.alt,
|
|
114
120
|
notes: input.notes ?? existing.notes,
|
|
121
|
+
provider: input.provider ?? existing.provider,
|
|
122
|
+
sourcePageUrl: input.sourcePageUrl ?? existing.sourcePageUrl,
|
|
123
|
+
license: input.license ?? existing.license,
|
|
124
|
+
attribution: input.attribution ?? existing.attribution,
|
|
125
|
+
width: input.width ?? existing.width,
|
|
126
|
+
height: input.height ?? existing.height,
|
|
115
127
|
failureReason,
|
|
116
128
|
}
|
|
117
129
|
: buildFailureRecord(input, topic, status, failureReason, sourcePath)
|
|
@@ -196,6 +208,12 @@ export async function saveMediaAsset(input: MediaSaveInput, workspaceDir: string
|
|
|
196
208
|
intendedSection: input.intendedSection,
|
|
197
209
|
alt: input.alt,
|
|
198
210
|
notes: input.notes,
|
|
211
|
+
provider: input.provider,
|
|
212
|
+
sourcePageUrl: input.sourcePageUrl,
|
|
213
|
+
license: input.license,
|
|
214
|
+
attribution: input.attribution,
|
|
215
|
+
width: input.width,
|
|
216
|
+
height: input.height,
|
|
199
217
|
savedAt: nowIso(),
|
|
200
218
|
}
|
|
201
219
|
const { manifest: nextManifest } = upsertAsset(manifest, record)
|
|
@@ -248,6 +266,12 @@ export async function saveMediaAsset(input: MediaSaveInput, workspaceDir: string
|
|
|
248
266
|
intendedSection: input.intendedSection,
|
|
249
267
|
alt: input.alt,
|
|
250
268
|
notes: input.notes,
|
|
269
|
+
provider: input.provider,
|
|
270
|
+
sourcePageUrl: input.sourcePageUrl,
|
|
271
|
+
license: input.license,
|
|
272
|
+
attribution: input.attribution,
|
|
273
|
+
width: input.width,
|
|
274
|
+
height: input.height,
|
|
251
275
|
savedAt: nowIso(),
|
|
252
276
|
}
|
|
253
277
|
const { manifest: nextManifest } = upsertAsset(manifest, record)
|