@cyber-dash-tech/revela 0.4.6 → 0.5.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 +82 -2
- package/README.zh-CN.md +85 -2
- package/lib/agents/research-prompt.ts +37 -10
- package/lib/commands/help.ts +3 -0
- package/lib/commands/init.ts +68 -0
- package/lib/commands/remember.ts +46 -0
- package/lib/commands/review.ts +68 -0
- package/lib/decks-memory.ts +509 -0
- package/lib/decks-state.ts +452 -0
- package/package.json +1 -1
- package/plugin.ts +220 -14
- package/skill/SKILL.md +123 -153
- package/tools/decks.ts +158 -0
package/tools/decks.ts
ADDED
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { tool } from "@opencode-ai/plugin"
|
|
2
|
+
import {
|
|
3
|
+
createDeckSpec,
|
|
4
|
+
DECKS_STATE_FILE,
|
|
5
|
+
readOrCreateDecksState,
|
|
6
|
+
reviewDeckState,
|
|
7
|
+
upsertDeck,
|
|
8
|
+
upsertSlides,
|
|
9
|
+
writeDecksState,
|
|
10
|
+
type DeckSpec,
|
|
11
|
+
type RequiredInputs,
|
|
12
|
+
type ResearchAxis,
|
|
13
|
+
type SlideSpec,
|
|
14
|
+
} from "../lib/decks-state"
|
|
15
|
+
|
|
16
|
+
export default tool({
|
|
17
|
+
description:
|
|
18
|
+
`Read and update ${DECKS_STATE_FILE}, Revela's workspace deck state file. ` +
|
|
19
|
+
"Use this tool instead of writing or patching the state file directly. " +
|
|
20
|
+
"It stores active deck specs, per-slide content/layout/components, and computes write readiness.",
|
|
21
|
+
args: {
|
|
22
|
+
action: tool.schema
|
|
23
|
+
.enum(["read", "init", "upsertDeck", "upsertSlides", "review", "remember"])
|
|
24
|
+
.describe("Action to perform on DECKS.json."),
|
|
25
|
+
slug: tool.schema.string().optional().describe("Deck slug for read/upsert/review actions."),
|
|
26
|
+
summary: tool.schema.boolean().optional().describe("For read: return a compact summary instead of full state."),
|
|
27
|
+
goal: tool.schema.string().optional().describe("For upsertDeck: deck goal."),
|
|
28
|
+
audience: tool.schema.string().optional().describe("For upsertDeck: deck audience."),
|
|
29
|
+
language: tool.schema.string().optional().describe("For upsertDeck: deck language."),
|
|
30
|
+
slideCount: tool.schema.number().optional().describe("For upsertDeck: expected slide count."),
|
|
31
|
+
outputPath: tool.schema.string().optional().describe("For upsertDeck: target output path, normally decks/{slug}.html."),
|
|
32
|
+
design: tool.schema.string().optional().describe("For upsertDeck: active design name."),
|
|
33
|
+
domain: tool.schema.string().optional().describe("For upsertDeck: active domain name."),
|
|
34
|
+
memory: tool.schema.string().optional().describe("For remember: explicit user or workflow preference to store."),
|
|
35
|
+
preferenceType: tool.schema.enum(["user", "workflow"]).optional().describe("For remember: which preference list to update."),
|
|
36
|
+
requiredInputs: tool.schema.object({
|
|
37
|
+
topicClarified: tool.schema.boolean().optional(),
|
|
38
|
+
audienceClarified: tool.schema.boolean().optional(),
|
|
39
|
+
slideCountDecided: tool.schema.boolean().optional(),
|
|
40
|
+
languageDecided: tool.schema.boolean().optional(),
|
|
41
|
+
visualStyleSelected: tool.schema.boolean().optional(),
|
|
42
|
+
sourceMaterialsIdentified: tool.schema.boolean().optional(),
|
|
43
|
+
researchNeedAssessed: tool.schema.boolean().optional(),
|
|
44
|
+
researchFindingsRead: tool.schema.boolean().optional(),
|
|
45
|
+
slidePlanConfirmed: tool.schema.boolean().optional(),
|
|
46
|
+
designLayoutsFetched: tool.schema.boolean().optional(),
|
|
47
|
+
}).optional().describe("For upsertDeck: checklist state. Only set true for explicit completed prerequisites."),
|
|
48
|
+
researchPlan: tool.schema.array(tool.schema.object({
|
|
49
|
+
axis: tool.schema.string().describe("Research axis name."),
|
|
50
|
+
needed: tool.schema.boolean().describe("Whether this research axis is needed for the deck."),
|
|
51
|
+
status: tool.schema.enum(["pending", "in_progress", "done", "read", "skipped"]).describe("Research status."),
|
|
52
|
+
findingsFile: tool.schema.string().optional().describe("Findings file path if available."),
|
|
53
|
+
notes: tool.schema.string().optional().describe("Short notes."),
|
|
54
|
+
})).optional().describe("For upsertDeck: research plan."),
|
|
55
|
+
slides: tool.schema.array(tool.schema.object({
|
|
56
|
+
index: tool.schema.number().describe("1-based slide index."),
|
|
57
|
+
title: tool.schema.string().describe("Slide title."),
|
|
58
|
+
purpose: tool.schema.string().optional().describe("Narrative purpose of this slide."),
|
|
59
|
+
layout: tool.schema.string().describe("Design layout name."),
|
|
60
|
+
qa: tool.schema.boolean().optional().describe("Whether the layout requires full QA."),
|
|
61
|
+
components: tool.schema.array(tool.schema.string()).describe("Design components used by this slide."),
|
|
62
|
+
content: tool.schema.object({
|
|
63
|
+
headline: tool.schema.string().optional(),
|
|
64
|
+
body: tool.schema.array(tool.schema.string()).optional(),
|
|
65
|
+
bullets: tool.schema.array(tool.schema.string()).optional(),
|
|
66
|
+
speakerNotes: tool.schema.string().optional(),
|
|
67
|
+
}).describe("Structured slide content."),
|
|
68
|
+
evidence: tool.schema.array(tool.schema.object({
|
|
69
|
+
source: tool.schema.string().describe("Source file, URL, or research note."),
|
|
70
|
+
quote: tool.schema.string().optional(),
|
|
71
|
+
page: tool.schema.string().optional(),
|
|
72
|
+
url: tool.schema.string().optional(),
|
|
73
|
+
})).optional().describe("Evidence references for this slide."),
|
|
74
|
+
visuals: tool.schema.array(tool.schema.object({
|
|
75
|
+
id: tool.schema.string().optional(),
|
|
76
|
+
purpose: tool.schema.string().optional(),
|
|
77
|
+
brief: tool.schema.string().describe("Visual brief."),
|
|
78
|
+
assetPath: tool.schema.string().optional(),
|
|
79
|
+
})).optional().describe("Visual needs or assets for this slide."),
|
|
80
|
+
status: tool.schema.enum(["planned", "ready", "written", "qa_passed", "qa_failed"]).optional().describe("Slide production status."),
|
|
81
|
+
notes: tool.schema.string().optional().describe("Implementation notes for this slide."),
|
|
82
|
+
})).optional().describe("For upsertSlides: complete or partial slide specs."),
|
|
83
|
+
},
|
|
84
|
+
async execute(args, context) {
|
|
85
|
+
try {
|
|
86
|
+
const workspaceRoot = context.directory ?? process.cwd()
|
|
87
|
+
const state = readOrCreateDecksState(workspaceRoot)
|
|
88
|
+
|
|
89
|
+
if (args.action === "init") {
|
|
90
|
+
writeDecksState(workspaceRoot, state)
|
|
91
|
+
return JSON.stringify({ ok: true, path: DECKS_STATE_FILE, state }, null, 2)
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (args.action === "read") {
|
|
95
|
+
const slug = args.slug || state.activeDeck
|
|
96
|
+
if (args.summary) {
|
|
97
|
+
const deck = slug ? state.decks[slug] : undefined
|
|
98
|
+
return JSON.stringify({ ok: true, path: DECKS_STATE_FILE, activeDeck: state.activeDeck, deck }, null, 2)
|
|
99
|
+
}
|
|
100
|
+
return JSON.stringify({ ok: true, path: DECKS_STATE_FILE, state }, null, 2)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (args.action === "upsertDeck") {
|
|
104
|
+
if (!args.slug) return JSON.stringify({ ok: false, error: "slug is required for upsertDeck" })
|
|
105
|
+
const existing = state.decks[args.slug]
|
|
106
|
+
const deckInput: Partial<DeckSpec> & { slug: string } = {
|
|
107
|
+
...existing,
|
|
108
|
+
slug: args.slug,
|
|
109
|
+
goal: args.goal ?? existing?.goal ?? "",
|
|
110
|
+
audience: args.audience ?? existing?.audience,
|
|
111
|
+
language: args.language ?? existing?.language,
|
|
112
|
+
slideCount: args.slideCount ?? existing?.slideCount,
|
|
113
|
+
outputPath: args.outputPath ?? existing?.outputPath,
|
|
114
|
+
theme: {
|
|
115
|
+
design: args.design ?? existing?.theme?.design,
|
|
116
|
+
domain: args.domain ?? existing?.theme?.domain,
|
|
117
|
+
},
|
|
118
|
+
requiredInputs: {
|
|
119
|
+
...(existing?.requiredInputs ?? {}),
|
|
120
|
+
...((args.requiredInputs ?? {}) as Partial<RequiredInputs>),
|
|
121
|
+
} as RequiredInputs,
|
|
122
|
+
researchPlan: (args.researchPlan as ResearchAxis[] | undefined) ?? existing?.researchPlan,
|
|
123
|
+
}
|
|
124
|
+
const next = upsertDeck(state, deckInput)
|
|
125
|
+
writeDecksState(workspaceRoot, next)
|
|
126
|
+
return JSON.stringify({ ok: true, path: DECKS_STATE_FILE, deck: next.activeDeck ? next.decks[next.activeDeck] : undefined }, null, 2)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (args.action === "upsertSlides") {
|
|
130
|
+
if (!args.slug) return JSON.stringify({ ok: false, error: "slug is required for upsertSlides" })
|
|
131
|
+
if (!args.slides) return JSON.stringify({ ok: false, error: "slides are required for upsertSlides" })
|
|
132
|
+
const next = upsertSlides(state, args.slug, args.slides as SlideSpec[])
|
|
133
|
+
writeDecksState(workspaceRoot, next)
|
|
134
|
+
return JSON.stringify({ ok: true, path: DECKS_STATE_FILE, deck: next.activeDeck ? next.decks[next.activeDeck] : undefined }, null, 2)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (args.action === "review") {
|
|
138
|
+
const reviewed = reviewDeckState(state, args.slug)
|
|
139
|
+
writeDecksState(workspaceRoot, reviewed.state)
|
|
140
|
+
return JSON.stringify({ ok: true, path: DECKS_STATE_FILE, result: reviewed.result }, null, 2)
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (args.action === "remember") {
|
|
144
|
+
const memory = args.memory?.trim()
|
|
145
|
+
if (!memory) return JSON.stringify({ ok: false, error: "memory is required for remember" })
|
|
146
|
+
const preferenceType = args.preferenceType ?? "user"
|
|
147
|
+
const list = state.workspace.preferences[preferenceType]
|
|
148
|
+
if (!list.some((entry) => entry.trim().toLowerCase() === memory.toLowerCase())) list.push(memory)
|
|
149
|
+
writeDecksState(workspaceRoot, state)
|
|
150
|
+
return JSON.stringify({ ok: true, path: DECKS_STATE_FILE, preferenceType, memory }, null, 2)
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return JSON.stringify({ ok: false, error: `Unsupported action: ${args.action}` })
|
|
154
|
+
} catch (e: any) {
|
|
155
|
+
return JSON.stringify({ ok: false, error: e.message || String(e) })
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
})
|