@cyber-dash-tech/revela 0.9.0 → 0.11.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.
Files changed (48) hide show
  1. package/README.md +54 -9
  2. package/README.zh-CN.md +54 -9
  3. package/designs/monet/DESIGN.md +9 -9
  4. package/designs/starter/DESIGN.md +8 -8
  5. package/designs/summit/DESIGN.md +9 -9
  6. package/lib/commands/help.ts +2 -0
  7. package/lib/commands/inspect.ts +23 -0
  8. package/lib/commands/pdf.ts +33 -5
  9. package/lib/commands/pptx.ts +14 -9
  10. package/lib/commands/refine.ts +26 -0
  11. package/lib/commands/review.ts +8 -2
  12. package/lib/deck-html/contract.ts +252 -0
  13. package/lib/decks-state.ts +574 -31
  14. package/lib/document-materials/extract.ts +20 -0
  15. package/lib/edit/resolve-deck.ts +13 -2
  16. package/lib/inspect/open.ts +63 -0
  17. package/lib/inspect/prompt.ts +32 -0
  18. package/lib/inspect/request.ts +70 -0
  19. package/lib/inspect/requests.ts +86 -0
  20. package/lib/inspect/server.ts +1063 -0
  21. package/lib/inspect/slide-index.ts +12 -0
  22. package/lib/inspection-context/compile.ts +346 -0
  23. package/lib/inspection-context/match.ts +169 -0
  24. package/lib/inspection-context/project.ts +263 -0
  25. package/lib/inspection-context/result.ts +160 -0
  26. package/lib/qa/export-gate.ts +8 -1
  27. package/lib/refine/open.ts +70 -0
  28. package/lib/refine/server.ts +1581 -0
  29. package/lib/workspace-state/actions.ts +71 -0
  30. package/lib/workspace-state/compat.ts +10 -0
  31. package/lib/workspace-state/evidence-status.ts +267 -0
  32. package/lib/workspace-state/graph.ts +426 -0
  33. package/lib/workspace-state/render-targets.ts +182 -0
  34. package/lib/workspace-state/rendered-artifacts.ts +43 -0
  35. package/lib/workspace-state/repository.ts +43 -0
  36. package/lib/workspace-state/research-attachments.ts +130 -0
  37. package/lib/workspace-state/review-snapshots.ts +127 -0
  38. package/lib/workspace-state/types.ts +119 -0
  39. package/package.json +1 -1
  40. package/plugin.ts +48 -1
  41. package/skill/SKILL.md +10 -5
  42. package/tools/decks.ts +61 -2
  43. package/tools/inspection-context.ts +22 -0
  44. package/tools/inspection-result.ts +63 -0
  45. package/tools/pdf.ts +9 -1
  46. package/tools/pptx.ts +10 -0
  47. package/tools/research-save.ts +15 -0
  48. package/tools/workspace-scan.ts +15 -0
@@ -0,0 +1,63 @@
1
+ import { tool } from "@opencode-ai/plugin"
2
+ import { completeInspectRequest } from "../lib/inspect/requests"
3
+ import type { InspectionResult } from "../lib/inspection-context/result"
4
+
5
+ const evidenceSourceItemSchema = tool.schema.object({
6
+ source: tool.schema.string().describe("Human-readable source label."),
7
+ sourcePath: tool.schema.string().optional(),
8
+ findingsFile: tool.schema.string().optional(),
9
+ location: tool.schema.string().optional(),
10
+ page: tool.schema.string().optional(),
11
+ url: tool.schema.string().optional(),
12
+ quote: tool.schema.string().optional(),
13
+ caveat: tool.schema.string().optional(),
14
+ })
15
+
16
+ export default tool({
17
+ description:
18
+ "Submit the final structured Evidence Inspector result for a pending /revela inspect request. " +
19
+ "Use only when responding to an inspection prompt. This updates the local browser inspector; it does not mutate DECKS.json or deck files.",
20
+ args: {
21
+ requestId: tool.schema.string().describe("Pending inspection request id from the inspection prompt."),
22
+ result: tool.schema.object({
23
+ version: tool.schema.number().describe("Must be 1."),
24
+ status: tool.schema.enum(["success", "no_match"]),
25
+ selectedText: tool.schema.string().optional(),
26
+ slide: tool.schema.object({
27
+ index: tool.schema.number(),
28
+ title: tool.schema.string(),
29
+ }).optional(),
30
+ matchConfidence: tool.schema.enum(["none", "low", "medium", "high"]),
31
+ cards: tool.schema.object({
32
+ purpose: tool.schema.object({
33
+ status: tool.schema.enum(["clear", "weak", "misplaced", "unknown"]),
34
+ role: tool.schema.string().optional(),
35
+ rationale: tool.schema.string(),
36
+ whyItMatters: tool.schema.string(),
37
+ }),
38
+ source: tool.schema.object({
39
+ status: tool.schema.enum(["supported", "weak", "unsupported", "not_needed", "unknown"]),
40
+ matchedClaim: tool.schema.string().optional(),
41
+ sources: tool.schema.array(evidenceSourceItemSchema),
42
+ warnings: tool.schema.array(tool.schema.string()),
43
+ gaps: tool.schema.array(tool.schema.string()),
44
+ caveats: tool.schema.array(tool.schema.string()),
45
+ rationale: tool.schema.string(),
46
+ }),
47
+ }),
48
+ stale: tool.schema.object({
49
+ stale: tool.schema.boolean(),
50
+ reason: tool.schema.string().optional(),
51
+ }).optional(),
52
+ }).describe("Final structured inspector result to render in the browser."),
53
+ },
54
+ async execute(args) {
55
+ try {
56
+ if (args.result.version !== 1) throw new Error("Inspection result version must be 1.")
57
+ const request = completeInspectRequest(args.requestId, args.result as InspectionResult)
58
+ return JSON.stringify({ ok: true, requestId: request.requestId, status: request.status })
59
+ } catch (e: any) {
60
+ return JSON.stringify({ ok: false, error: e.message || String(e) })
61
+ }
62
+ },
63
+ })
package/tools/pdf.ts CHANGED
@@ -9,6 +9,7 @@ import { existsSync } from "fs"
9
9
  import { resolve } from "path"
10
10
  import { exportToPdf } from "../lib/pdf/export"
11
11
  import { assertExportQAPassed } from "../lib/qa/export-gate"
12
+ import { recordRenderedArtifact, workspaceRelative } from "../lib/workspace-state/rendered-artifacts"
12
13
 
13
14
  export default tool({
14
15
  description:
@@ -35,8 +36,15 @@ export default tool({
35
36
  }
36
37
 
37
38
  try {
38
- await assertExportQAPassed(filePath)
39
+ const root = directory || process.cwd()
40
+ await assertExportQAPassed(filePath, { workspaceRoot: root })
39
41
  const result = await exportToPdf(filePath)
42
+ recordRenderedArtifact(root, {
43
+ sourceHtmlPath: workspaceRelative(resolve(root), filePath),
44
+ outputPath: result.outputPath,
45
+ type: "pdf",
46
+ actor: "revela-pdf",
47
+ })
40
48
  return JSON.stringify({ ok: true, ...result }, null, 2)
41
49
  } catch (e: any) {
42
50
  return JSON.stringify({ ok: false, error: e?.message ?? String(e) })
package/tools/pptx.ts CHANGED
@@ -7,7 +7,9 @@
7
7
  import { tool } from "@opencode-ai/plugin"
8
8
  import { existsSync } from "fs"
9
9
  import { resolve } from "path"
10
+ import { assertDeckHtmlContractValid } from "../lib/deck-html/contract"
10
11
  import { exportToPptx } from "../lib/pptx/export"
12
+ import { recordRenderedArtifact, workspaceRelative } from "../lib/workspace-state/rendered-artifacts"
11
13
 
12
14
  export default tool({
13
15
  description:
@@ -42,12 +44,20 @@ export default tool({
42
44
  const progress: string[] = []
43
45
 
44
46
  try {
47
+ const root = directory || process.cwd()
48
+ assertDeckHtmlContractValid(root, filePath)
45
49
  const result = await exportToPptx(filePath, {
46
50
  speakerNotes: normalizeSpeakerNotes(speakerNotes),
47
51
  onProgress: (event) => {
48
52
  progress.push(event.message)
49
53
  },
50
54
  })
55
+ recordRenderedArtifact(root, {
56
+ sourceHtmlPath: workspaceRelative(resolve(root), filePath),
57
+ outputPath: result.outputPath,
58
+ type: "pptx",
59
+ actor: "revela-pptx",
60
+ })
51
61
  return JSON.stringify({ ok: true, ...result, progress }, null, 2)
52
62
  } catch (e: any) {
53
63
  return JSON.stringify({ ok: false, error: e?.message ?? String(e), progress })
@@ -1,6 +1,8 @@
1
1
  import { tool } from "@opencode-ai/plugin"
2
2
  import { mkdirSync, writeFileSync } from "fs"
3
3
  import { join } from "path"
4
+ import { hasDecksState, readDecksState, writeDecksState } from "../lib/decks-state"
5
+ import { recordWorkspaceAction } from "../lib/workspace-state/actions"
4
6
 
5
7
  /**
6
8
  * Format today's date as YYYY-MM-DD
@@ -88,6 +90,19 @@ export default tool({
88
90
 
89
91
  writeFileSync(filePath, fileContent, "utf-8")
90
92
 
93
+ if (hasDecksState(workspaceDir)) {
94
+ const state = readDecksState(workspaceDir)
95
+ recordWorkspaceAction(state, {
96
+ type: "research.findings_saved",
97
+ actor: "revela-research-save",
98
+ inputs: { topic: topicKey, axis: fileKey, sourceCount: args.sources?.length ?? 0 },
99
+ outputs: { path: relPath, sources: args.sources ?? [] },
100
+ summary: `Saved research findings for ${topicKey}/${fileKey}.`,
101
+ nodeIds: [`finding:${relPath}`],
102
+ })
103
+ writeDecksState(workspaceDir, state)
104
+ }
105
+
91
106
  return JSON.stringify({ ok: true, path: relPath })
92
107
  } catch (e: any) {
93
108
  return JSON.stringify({ error: e.message || String(e) })
@@ -3,6 +3,8 @@ import { readdirSync, statSync, existsSync } from "fs"
3
3
  import { join, relative, extname, resolve, sep, isAbsolute } from "path"
4
4
  import { sourceMaterialMetadata } from "../lib/source-materials"
5
5
  import type { SourceMaterial } from "../lib/decks-state"
6
+ import { hasDecksState, readDecksState, writeDecksState } from "../lib/decks-state"
7
+ import { recordWorkspaceAction } from "../lib/workspace-state/actions"
6
8
 
7
9
  const DOC_EXTENSIONS = new Set([
8
10
  ".pdf", ".docx", ".doc", ".xlsx", ".xls",
@@ -145,6 +147,19 @@ export default tool({
145
147
  const results: FileEntry[] = []
146
148
  scanDir(scanRoot, workspaceDir, results, maxDepth, 0)
147
149
 
150
+ if (hasDecksState(workspaceDir)) {
151
+ const state = readDecksState(workspaceDir)
152
+ recordWorkspaceAction(state, {
153
+ type: "workspace.scanned",
154
+ actor: "revela-workspace-scan",
155
+ inputs: { path: args.path, maxDepth },
156
+ outputs: { found: results.length, paths: results.map((file) => file.path) },
157
+ summary: `Scanned workspace documents and found ${results.length} file${results.length === 1 ? "" : "s"}.`,
158
+ nodeIds: results.map((file) => `source:${file.sourceMaterial.path}`),
159
+ })
160
+ writeDecksState(workspaceDir, state)
161
+ }
162
+
148
163
  if (results.length === 0) {
149
164
  return JSON.stringify({
150
165
  found: 0,