@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/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
+ })