@cyber-dash-tech/revela 0.12.0 → 0.13.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/lib/commands/brief.ts +63 -0
- package/lib/commands/help.ts +2 -0
- package/lib/commands/narrative.ts +160 -0
- package/lib/decks-state.ts +33 -0
- package/lib/edit/prompt.ts +3 -0
- package/lib/inspection-context/compile.ts +159 -5
- package/lib/inspection-context/project.ts +20 -0
- package/lib/narrative-state/coverage.ts +100 -0
- package/lib/narrative-state/display.ts +219 -0
- package/lib/narrative-state/executive-brief.ts +246 -0
- package/lib/narrative-state/hash.ts +9 -0
- package/lib/narrative-state/map-html.ts +348 -0
- package/lib/narrative-state/map.ts +282 -0
- package/lib/narrative-state/normalize.ts +54 -0
- package/lib/narrative-state/queries.ts +433 -0
- package/lib/narrative-state/readiness.ts +71 -1
- package/lib/narrative-state/render-plan.ts +44 -1
- package/lib/narrative-state/research-gaps.ts +191 -0
- package/lib/narrative-state/types.ts +33 -0
- package/lib/workspace-state/evidence-status.ts +21 -1
- package/lib/workspace-state/graph.ts +56 -2
- package/lib/workspace-state/types.ts +10 -1
- package/package.json +1 -1
- package/plugin.ts +31 -0
- package/tools/decks.ts +86 -1
- package/tools/narrative-view.ts +84 -0
package/tools/decks.ts
CHANGED
|
@@ -28,6 +28,8 @@ import {
|
|
|
28
28
|
reviewNarrativeState,
|
|
29
29
|
} from "../lib/narrative-state/readiness"
|
|
30
30
|
import { compileDeckPlanFromNarrative } from "../lib/narrative-state/render-plan"
|
|
31
|
+
import { backfillSlideClaimRefsFromCoverage } from "../lib/narrative-state/coverage"
|
|
32
|
+
import { closeResearchGapInState, deriveResearchGapsFromReadiness, updateResearchGapInState, upsertResearchGapsInState } from "../lib/narrative-state/research-gaps"
|
|
31
33
|
import { normalizeCanonicalNarrativeState, normalizeNarrativeState } from "../lib/narrative-state/normalize"
|
|
32
34
|
import { narrativeToBrief } from "../lib/narrative-state/project-compat"
|
|
33
35
|
import type { NarrativeStateV1 } from "../lib/narrative-state/types"
|
|
@@ -48,9 +50,11 @@ function mergeNarrativeInput(current: NarrativeStateV1, input: Partial<Narrative
|
|
|
48
50
|
},
|
|
49
51
|
thesis: input.thesis ? { ...current.thesis, ...input.thesis } as NarrativeStateV1["thesis"] : current.thesis,
|
|
50
52
|
claims: input.claims ?? current.claims,
|
|
53
|
+
claimRelations: input.claimRelations ?? current.claimRelations,
|
|
51
54
|
evidenceBindings: input.evidenceBindings ?? current.evidenceBindings,
|
|
52
55
|
objections: input.objections ?? current.objections,
|
|
53
56
|
risks: input.risks ?? current.risks,
|
|
57
|
+
researchGaps: input.researchGaps ?? current.researchGaps,
|
|
54
58
|
approvals: current.approvals,
|
|
55
59
|
updatedAt: new Date().toISOString(),
|
|
56
60
|
}
|
|
@@ -63,7 +67,7 @@ export default tool({
|
|
|
63
67
|
"It stores workspace narrative state, active deck specs, per-slide content/layout/components, and computes narrative or deck readiness.",
|
|
64
68
|
args: {
|
|
65
69
|
action: tool.schema
|
|
66
|
-
.enum(["read", "init", "upsertDeck", "upsertSlides", "upsertNarrative", "compileDeckPlan", "review", "reviewNarrative", "approveNarrative", "applyEvidenceCandidates", "attachResearchFindings", "remember"])
|
|
70
|
+
.enum(["read", "init", "upsertDeck", "upsertSlides", "upsertNarrative", "compileDeckPlan", "backfillClaimRefs", "review", "reviewNarrative", "approveNarrative", "deriveResearchGaps", "upsertResearchGaps", "updateResearchGap", "closeResearchGap", "applyEvidenceCandidates", "attachResearchFindings", "remember"])
|
|
67
71
|
.describe("Action to perform on DECKS.json."),
|
|
68
72
|
summary: tool.schema.boolean().optional().describe("For read: return a compact summary instead of full state."),
|
|
69
73
|
goal: tool.schema.string().optional().describe("For upsertDeck: deck goal."),
|
|
@@ -113,6 +117,13 @@ export default tool({
|
|
|
113
117
|
unsupportedScope: tool.schema.string().optional(),
|
|
114
118
|
caveats: tool.schema.array(tool.schema.string()).optional(),
|
|
115
119
|
})).optional(),
|
|
120
|
+
claimRelations: tool.schema.array(tool.schema.object({
|
|
121
|
+
id: tool.schema.string().optional(),
|
|
122
|
+
fromClaimId: tool.schema.string().describe("Canonical claim id the relation starts from."),
|
|
123
|
+
toClaimId: tool.schema.string().describe("Canonical claim id the relation points to."),
|
|
124
|
+
relation: tool.schema.enum(["leads_to", "supports", "depends_on", "contrasts_with", "constrains", "answers"]).optional(),
|
|
125
|
+
rationale: tool.schema.string().optional().describe("Short explanation of why this narrative relation exists."),
|
|
126
|
+
})).optional().describe("Canonical claim-to-claim narrative progression relationships. These affect narrative approval hash."),
|
|
116
127
|
evidenceBindings: tool.schema.array(tool.schema.object({
|
|
117
128
|
id: tool.schema.string().optional(),
|
|
118
129
|
claimId: tool.schema.string().describe("Canonical claim id this evidence supports."),
|
|
@@ -141,6 +152,18 @@ export default tool({
|
|
|
141
152
|
severity: tool.schema.enum(["high", "medium", "low"]).optional(),
|
|
142
153
|
mitigation: tool.schema.string().optional(),
|
|
143
154
|
})).optional(),
|
|
155
|
+
researchGaps: tool.schema.array(tool.schema.object({
|
|
156
|
+
id: tool.schema.string().optional(),
|
|
157
|
+
targetType: tool.schema.enum(["claim", "objection", "risk", "decision", "narrative"]).optional(),
|
|
158
|
+
targetId: tool.schema.string().optional(),
|
|
159
|
+
question: tool.schema.string().describe("Research question or gap to resolve."),
|
|
160
|
+
status: tool.schema.enum(["open", "in_progress", "findings_saved", "attached", "evidence_bound", "closed"]).optional(),
|
|
161
|
+
priority: tool.schema.enum(["high", "medium", "low"]).optional(),
|
|
162
|
+
findingsFile: tool.schema.string().optional(),
|
|
163
|
+
evidenceBindingIds: tool.schema.array(tool.schema.string()).optional(),
|
|
164
|
+
createdFromIssueType: tool.schema.string().optional(),
|
|
165
|
+
notes: tool.schema.string().optional(),
|
|
166
|
+
})).optional(),
|
|
144
167
|
}).optional().describe("For upsertNarrative: canonical narrative state fields to merge into DECKS.json. Replaces provided arrays, preserves approvals."),
|
|
145
168
|
design: tool.schema.string().optional().describe("For upsertDeck: active design name."),
|
|
146
169
|
domain: tool.schema.string().optional().describe("For upsertDeck: active domain name."),
|
|
@@ -190,6 +213,13 @@ export default tool({
|
|
|
190
213
|
layout: tool.schema.string().describe("Design layout name."),
|
|
191
214
|
qa: tool.schema.boolean().optional().describe("Whether the slide is marked QA-relevant deck metadata."),
|
|
192
215
|
components: tool.schema.array(tool.schema.string()).describe("Design components used by this slide."),
|
|
216
|
+
claimIds: tool.schema.array(tool.schema.string()).optional().describe("Canonical narrative claim ids directly expressed by this slide."),
|
|
217
|
+
claimRefs: tool.schema.array(tool.schema.object({
|
|
218
|
+
claimId: tool.schema.string().describe("Canonical narrative claim id referenced by this slide."),
|
|
219
|
+
role: tool.schema.enum(["primary", "supporting", "evidence", "risk", "objection"]).describe("How the slide uses this claim."),
|
|
220
|
+
note: tool.schema.string().optional().describe("Optional short rationale for this claim-slide relationship."),
|
|
221
|
+
})).optional().describe("Structured canonical claim references for this slide; preferred over flat claimIds when available."),
|
|
222
|
+
evidenceBindingIds: tool.schema.array(tool.schema.string()).optional().describe("Canonical narrative evidence binding ids used by this slide."),
|
|
193
223
|
content: tool.schema.object({
|
|
194
224
|
headline: tool.schema.string().optional(),
|
|
195
225
|
body: tool.schema.array(tool.schema.string()).optional(),
|
|
@@ -224,6 +254,22 @@ export default tool({
|
|
|
224
254
|
approvalNote: tool.schema.string().optional().describe("For approveNarrative: optional note explaining the approval or override."),
|
|
225
255
|
approvalBy: tool.schema.enum(["user", "override"]).optional().describe("For approveNarrative: use override only for explicit render overrides, not normal strategic approval."),
|
|
226
256
|
approvalScope: tool.schema.enum(["narrative", "render_override"]).optional().describe("For approveNarrative: narrative approval or explicit render override scope."),
|
|
257
|
+
gapId: tool.schema.string().optional().describe("For updateResearchGap/closeResearchGap: canonical research gap id."),
|
|
258
|
+
researchGaps: tool.schema.array(tool.schema.object({
|
|
259
|
+
id: tool.schema.string().optional(),
|
|
260
|
+
targetType: tool.schema.enum(["claim", "objection", "risk", "decision", "narrative"]).optional(),
|
|
261
|
+
targetId: tool.schema.string().optional(),
|
|
262
|
+
question: tool.schema.string().describe("Research question or gap to resolve."),
|
|
263
|
+
status: tool.schema.enum(["open", "in_progress", "findings_saved", "attached", "evidence_bound", "closed"]).optional(),
|
|
264
|
+
priority: tool.schema.enum(["high", "medium", "low"]).optional(),
|
|
265
|
+
findingsFile: tool.schema.string().optional(),
|
|
266
|
+
evidenceBindingIds: tool.schema.array(tool.schema.string()).optional(),
|
|
267
|
+
createdFromIssueType: tool.schema.string().optional(),
|
|
268
|
+
notes: tool.schema.string().optional(),
|
|
269
|
+
})).optional().describe("For upsertResearchGaps: explicit canonical research gaps to create or update."),
|
|
270
|
+
gapStatus: tool.schema.enum(["open", "in_progress", "findings_saved", "attached", "evidence_bound", "closed"]).optional().describe("For updateResearchGap: lifecycle status."),
|
|
271
|
+
gapNotes: tool.schema.string().optional().describe("For updateResearchGap/closeResearchGap: notes or close reason."),
|
|
272
|
+
evidenceBindingIds: tool.schema.array(tool.schema.string()).optional().describe("For updateResearchGap: canonical narrative evidence binding ids associated with the gap."),
|
|
227
273
|
},
|
|
228
274
|
async execute(args, context) {
|
|
229
275
|
try {
|
|
@@ -382,6 +428,12 @@ export default tool({
|
|
|
382
428
|
return JSON.stringify({ ok: true, path: DECKS_STATE_FILE, result: compiled.result, deck: compiled.state.activeDeck ? compiled.state.decks[compiled.state.activeDeck] : undefined, narrative: compiled.state.narrative }, null, 2)
|
|
383
429
|
}
|
|
384
430
|
|
|
431
|
+
if (args.action === "backfillClaimRefs") {
|
|
432
|
+
const backfilled = backfillSlideClaimRefsFromCoverage(state)
|
|
433
|
+
writeDecksState(workspaceRoot, backfilled.state)
|
|
434
|
+
return JSON.stringify({ ok: true, path: DECKS_STATE_FILE, result: backfilled.result, deck: backfilled.state.activeDeck ? backfilled.state.decks[backfilled.state.activeDeck] : undefined, narrative: backfilled.state.narrative }, null, 2)
|
|
435
|
+
}
|
|
436
|
+
|
|
385
437
|
if (args.action === "reviewNarrative") {
|
|
386
438
|
const reviewed = reviewNarrativeState(state)
|
|
387
439
|
recordNarrativeReviewAction(reviewed.state, reviewed.result)
|
|
@@ -400,6 +452,39 @@ export default tool({
|
|
|
400
452
|
return JSON.stringify({ ok: true, path: DECKS_STATE_FILE, result: approved.result, narrative: approved.state.narrative }, null, 2)
|
|
401
453
|
}
|
|
402
454
|
|
|
455
|
+
if (args.action === "deriveResearchGaps") {
|
|
456
|
+
const derived = deriveResearchGapsFromReadiness(state)
|
|
457
|
+
writeDecksState(workspaceRoot, derived.state)
|
|
458
|
+
return JSON.stringify({ ok: true, path: DECKS_STATE_FILE, result: derived.result, narrative: derived.state.narrative }, null, 2)
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (args.action === "upsertResearchGaps") {
|
|
462
|
+
if (!args.researchGaps?.length) return JSON.stringify({ ok: false, error: "researchGaps are required for upsertResearchGaps" })
|
|
463
|
+
const upserted = upsertResearchGapsInState(state, args.researchGaps as any[])
|
|
464
|
+
writeDecksState(workspaceRoot, upserted.state)
|
|
465
|
+
return JSON.stringify({ ok: true, path: DECKS_STATE_FILE, result: upserted.result, narrative: upserted.state.narrative }, null, 2)
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (args.action === "updateResearchGap") {
|
|
469
|
+
if (!args.gapId?.trim()) return JSON.stringify({ ok: false, error: "gapId is required for updateResearchGap" })
|
|
470
|
+
const updated = updateResearchGapInState(state, {
|
|
471
|
+
id: args.gapId,
|
|
472
|
+
status: args.gapStatus as any,
|
|
473
|
+
findingsFile: args.findingsFile,
|
|
474
|
+
evidenceBindingIds: args.evidenceBindingIds,
|
|
475
|
+
notes: args.gapNotes,
|
|
476
|
+
})
|
|
477
|
+
writeDecksState(workspaceRoot, updated.state)
|
|
478
|
+
return JSON.stringify({ ok: true, path: DECKS_STATE_FILE, result: updated.result, narrative: updated.state.narrative }, null, 2)
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
if (args.action === "closeResearchGap") {
|
|
482
|
+
if (!args.gapId?.trim()) return JSON.stringify({ ok: false, error: "gapId is required for closeResearchGap" })
|
|
483
|
+
const closed = closeResearchGapInState(state, args.gapId, args.gapNotes)
|
|
484
|
+
writeDecksState(workspaceRoot, closed.state)
|
|
485
|
+
return JSON.stringify({ ok: true, path: DECKS_STATE_FILE, result: closed.result, narrative: closed.state.narrative }, null, 2)
|
|
486
|
+
}
|
|
487
|
+
|
|
403
488
|
if (args.action === "applyEvidenceCandidates") {
|
|
404
489
|
const candidateIds = args.candidateIds ?? []
|
|
405
490
|
if (candidateIds.length === 0) return JSON.stringify({ ok: false, error: "candidateIds are required for applyEvidenceCandidates" })
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { tool } from "@opencode-ai/plugin"
|
|
2
|
+
import { hasDecksState, readDecksState } from "../lib/decks-state"
|
|
3
|
+
import { buildNarrativeMap } from "../lib/narrative-state/map"
|
|
4
|
+
import { validateNarrativeDisplayModel, type NarrativeDisplayModel, type NarrativeViewLanguage } from "../lib/narrative-state/display"
|
|
5
|
+
import { writeNarrativeMapHtml } from "../lib/commands/narrative"
|
|
6
|
+
import { openUrl } from "../lib/edit/open"
|
|
7
|
+
|
|
8
|
+
export default tool({
|
|
9
|
+
description:
|
|
10
|
+
"Render Revela's read-only narrative claim-flow UI from the current deterministic narrative map plus an optional localized display model. " +
|
|
11
|
+
"This tool validates display IDs against DECKS.json, opens a local HTML view, and never mutates workspace state.",
|
|
12
|
+
args: {
|
|
13
|
+
language: tool.schema.string().describe("UI language request from /revela narrative. May be any language tag or language name, such as en, zh-CN, fr, de, Korean, Arabic, or Portuguese-BR."),
|
|
14
|
+
narrativeHash: tool.schema.string().optional().describe("Narrative hash from the prompt projection. Used to detect stale display prompts."),
|
|
15
|
+
displayModel: tool.schema.object({
|
|
16
|
+
version: tool.schema.number().describe("Must be 1."),
|
|
17
|
+
language: tool.schema.string().describe("Must exactly match the top-level language request."),
|
|
18
|
+
pageTitle: tool.schema.string().optional(),
|
|
19
|
+
summaryLine: tool.schema.string().optional(),
|
|
20
|
+
labels: tool.schema.object({
|
|
21
|
+
eyebrow: tool.schema.string().optional(),
|
|
22
|
+
claimFlow: tool.schema.string().optional(),
|
|
23
|
+
flowNote: tool.schema.string().optional(),
|
|
24
|
+
selectedClaim: tool.schema.string().optional(),
|
|
25
|
+
claim: tool.schema.string().optional(),
|
|
26
|
+
claimId: tool.schema.string().optional(),
|
|
27
|
+
status: tool.schema.string().optional(),
|
|
28
|
+
supportedScope: tool.schema.string().optional(),
|
|
29
|
+
unsupportedScope: tool.schema.string().optional(),
|
|
30
|
+
incomingRelations: tool.schema.string().optional(),
|
|
31
|
+
outgoingRelations: tool.schema.string().optional(),
|
|
32
|
+
evidence: tool.schema.string().optional(),
|
|
33
|
+
objections: tool.schema.string().optional(),
|
|
34
|
+
risks: tool.schema.string().optional(),
|
|
35
|
+
researchGaps: tool.schema.string().optional(),
|
|
36
|
+
coveredSlides: tool.schema.string().optional(),
|
|
37
|
+
noClaims: tool.schema.string().optional(),
|
|
38
|
+
none: tool.schema.string().optional(),
|
|
39
|
+
}).optional(),
|
|
40
|
+
claimCards: tool.schema.array(tool.schema.object({
|
|
41
|
+
claimId: tool.schema.string().describe("Existing canonical claim id. Must match the deterministic map."),
|
|
42
|
+
displayTitle: tool.schema.string().optional().describe("Display-only localized claim title in the requested language. For Chinese manufacturing/industrial AI context, translate autonomy as 自主化/自主能力, not 自治; convert slug-like claim text into a readable title."),
|
|
43
|
+
roleLabel: tool.schema.string().optional(),
|
|
44
|
+
narrativeJob: tool.schema.string().optional(),
|
|
45
|
+
evidenceSummary: tool.schema.string().optional(),
|
|
46
|
+
riskOrGapSummary: tool.schema.string().optional(),
|
|
47
|
+
})).optional(),
|
|
48
|
+
relations: tool.schema.array(tool.schema.object({
|
|
49
|
+
fromClaimId: tool.schema.string(),
|
|
50
|
+
toClaimId: tool.schema.string(),
|
|
51
|
+
relation: tool.schema.enum(["leads_to", "supports", "depends_on", "contrasts_with", "constrains", "answers"]),
|
|
52
|
+
displayLabel: tool.schema.string().optional().describe("Display-only localization of an existing canonical relation label. Omit for inferred relations."),
|
|
53
|
+
displayRationale: tool.schema.string().optional().describe("Display-only localization of an existing canonical rationale. Omit when canonical rationale is missing or the relation is inferred."),
|
|
54
|
+
})).optional(),
|
|
55
|
+
}).optional().describe("Localized and organized display-only projection. It must not add facts or alter IDs."),
|
|
56
|
+
},
|
|
57
|
+
async execute(args, context) {
|
|
58
|
+
const workspaceRoot = context.directory ?? process.cwd()
|
|
59
|
+
try {
|
|
60
|
+
if (!hasDecksState(workspaceRoot)) {
|
|
61
|
+
return JSON.stringify({ ok: false, error: "No DECKS.json found. Run /revela init first." })
|
|
62
|
+
}
|
|
63
|
+
const language = args.language as NarrativeViewLanguage
|
|
64
|
+
const map = buildNarrativeMap(readDecksState(workspaceRoot))
|
|
65
|
+
const stalePrompt = Boolean(args.narrativeHash && args.narrativeHash !== map.snapshot.narrativeHash)
|
|
66
|
+
const display = validateNarrativeDisplayModel(map, args.displayModel as NarrativeDisplayModel | undefined, language)
|
|
67
|
+
const htmlPath = writeNarrativeMapHtml(map, display)
|
|
68
|
+
const url = `file://${htmlPath}`
|
|
69
|
+
openUrl(url)
|
|
70
|
+
return JSON.stringify({ ok: true, url, path: htmlPath, narrativeHash: map.snapshot.narrativeHash, stalePrompt, fallback: false }, null, 2)
|
|
71
|
+
} catch (e: any) {
|
|
72
|
+
try {
|
|
73
|
+
const language = (args.language ?? "en") as NarrativeViewLanguage
|
|
74
|
+
const map = buildNarrativeMap(readDecksState(workspaceRoot))
|
|
75
|
+
const htmlPath = writeNarrativeMapHtml(map)
|
|
76
|
+
const url = `file://${htmlPath}`
|
|
77
|
+
openUrl(url)
|
|
78
|
+
return JSON.stringify({ ok: false, fallback: true, url, path: htmlPath, error: e.message || String(e) }, null, 2)
|
|
79
|
+
} catch (fallbackError: any) {
|
|
80
|
+
return JSON.stringify({ ok: false, fallback: false, error: e.message || String(e), fallbackError: fallbackError.message || String(fallbackError) })
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
})
|