@cyber-dash-tech/revela 0.18.3 → 0.18.5
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 +7 -5
- package/README.zh-CN.md +7 -5
- package/lib/commands/refine.ts +1 -1
- package/lib/decks-state.ts +1 -0
- package/lib/document-materials/extract.ts +25 -20
- package/lib/material-intake.ts +9 -4
- package/lib/narrative-vault/constants.ts +1 -1
- package/lib/narrative-vault/paths.ts +7 -2
- package/lib/refine/comment-requests.ts +1 -1
- package/lib/refine/prompt-bridge.ts +94 -25
- package/lib/refine/review-comments.ts +203 -0
- package/lib/refine/server.ts +1073 -216
- package/lib/runtime/index.ts +3 -2
- package/lib/workspace-meta.ts +32 -0
- package/package.json +1 -1
- package/plugin.ts +4 -3
- package/plugins/revela/.mcp.json +1 -1
- package/plugins/revela/hooks/revela_guard.ts +2 -2
- package/plugins/revela/mcp/revela-server.ts +1 -1
- package/plugins/revela/skills/revela-export/SKILL.md +30 -1
- package/plugins/revela/skills/revela-helper/SKILL.md +48 -0
- package/plugins/revela/skills/revela-make-deck/SKILL.md +93 -15
- package/plugins/revela/skills/revela-research/SKILL.md +57 -15
- package/plugins/revela/skills/{revela-review-deck → revela-review}/SKILL.md +28 -7
- package/tools/workspace-scan.ts +1 -1
- package/plugins/revela/skills/revela-design/SKILL.md +0 -46
- package/plugins/revela/skills/revela-domain/SKILL.md +0 -30
- package/plugins/revela/skills/revela-init/SKILL.md +0 -31
- package/plugins/revela/skills/revela-upgrade/SKILL.md +0 -33
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs"
|
|
2
|
+
import { join } from "path"
|
|
3
|
+
import { randomBytes } from "crypto"
|
|
4
|
+
import { workspaceMetaPath } from "../workspace-meta"
|
|
5
|
+
|
|
6
|
+
export type ReviewCommentStatus = "open" | "queued" | "applying" | "applied" | "failed"
|
|
7
|
+
|
|
8
|
+
export interface ReviewCommentRecord {
|
|
9
|
+
id: string
|
|
10
|
+
deckFile: string
|
|
11
|
+
slideIndex: number
|
|
12
|
+
deckVersion: string
|
|
13
|
+
comment: string
|
|
14
|
+
elements: any[]
|
|
15
|
+
asset?: any
|
|
16
|
+
drop?: any
|
|
17
|
+
status: ReviewCommentStatus
|
|
18
|
+
createdAt: string
|
|
19
|
+
updatedAt: string
|
|
20
|
+
lastApplyRequestId?: string
|
|
21
|
+
lastApplyError?: string
|
|
22
|
+
lastApplyRaw?: string
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface ReviewCommentCreateInput {
|
|
26
|
+
deckFile: string
|
|
27
|
+
deckVersion: string
|
|
28
|
+
comment: string
|
|
29
|
+
elements: any[]
|
|
30
|
+
asset?: any
|
|
31
|
+
drop?: any
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function createReviewComment(workspaceRoot: string, input: ReviewCommentCreateInput): ReviewCommentRecord {
|
|
35
|
+
const comment = input.comment.trim()
|
|
36
|
+
if (!comment) throw new Error("Comment is required")
|
|
37
|
+
const elements = Array.isArray(input.elements) ? input.elements : []
|
|
38
|
+
const slideIndex = deriveSlideIndex(elements)
|
|
39
|
+
const now = new Date().toISOString()
|
|
40
|
+
const record: ReviewCommentRecord = {
|
|
41
|
+
id: randomBytes(10).toString("base64url"),
|
|
42
|
+
deckFile: normalizeDeckFile(input.deckFile),
|
|
43
|
+
slideIndex,
|
|
44
|
+
deckVersion: input.deckVersion,
|
|
45
|
+
comment,
|
|
46
|
+
elements,
|
|
47
|
+
...(input.asset ? { asset: input.asset } : {}),
|
|
48
|
+
...(input.drop ? { drop: input.drop } : {}),
|
|
49
|
+
status: "open",
|
|
50
|
+
createdAt: now,
|
|
51
|
+
updatedAt: now,
|
|
52
|
+
}
|
|
53
|
+
writeReviewComment(workspaceRoot, record)
|
|
54
|
+
return record
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export function listReviewComments(workspaceRoot: string, deckFile: string): ReviewCommentRecord[] {
|
|
58
|
+
const file = normalizeDeckFile(deckFile)
|
|
59
|
+
const dir = reviewCommentsDir(workspaceRoot)
|
|
60
|
+
if (!existsSync(dir)) return []
|
|
61
|
+
const registry = readRegistry(workspaceRoot)
|
|
62
|
+
const records = registry.commentIds
|
|
63
|
+
.map((id) => readReviewComment(workspaceRoot, id))
|
|
64
|
+
.filter((record): record is ReviewCommentRecord => Boolean(record))
|
|
65
|
+
return records
|
|
66
|
+
.filter((record) => record.deckFile === file)
|
|
67
|
+
.sort((a, b) => a.slideIndex - b.slideIndex || a.createdAt.localeCompare(b.createdAt))
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function readReviewComment(workspaceRoot: string, id: string): ReviewCommentRecord | undefined {
|
|
71
|
+
const safeId = normalizeId(id)
|
|
72
|
+
if (!safeId) return undefined
|
|
73
|
+
const path = reviewCommentPath(workspaceRoot, safeId)
|
|
74
|
+
if (!existsSync(path)) return undefined
|
|
75
|
+
return JSON.parse(readFileSync(path, "utf-8")) as ReviewCommentRecord
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function markReviewCommentApplying(workspaceRoot: string, id: string, requestId: string): ReviewCommentRecord | undefined {
|
|
79
|
+
return updateReviewComment(workspaceRoot, id, (record) => ({
|
|
80
|
+
...record,
|
|
81
|
+
status: "applying",
|
|
82
|
+
lastApplyRequestId: requestId,
|
|
83
|
+
lastApplyError: undefined,
|
|
84
|
+
lastApplyRaw: undefined,
|
|
85
|
+
}))
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export function markReviewCommentQueued(workspaceRoot: string, id: string): ReviewCommentRecord | undefined {
|
|
89
|
+
return updateReviewComment(workspaceRoot, id, (record) => ({
|
|
90
|
+
...record,
|
|
91
|
+
status: "queued",
|
|
92
|
+
lastApplyError: undefined,
|
|
93
|
+
lastApplyRaw: undefined,
|
|
94
|
+
}))
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export function markReviewCommentApplied(workspaceRoot: string, id: string): ReviewCommentRecord | undefined {
|
|
98
|
+
return updateReviewComment(workspaceRoot, id, (record) => ({
|
|
99
|
+
...record,
|
|
100
|
+
status: "applied",
|
|
101
|
+
lastApplyError: undefined,
|
|
102
|
+
lastApplyRaw: undefined,
|
|
103
|
+
}))
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function markReviewCommentFailed(workspaceRoot: string, id: string, error: string, raw?: string): ReviewCommentRecord | undefined {
|
|
107
|
+
return updateReviewComment(workspaceRoot, id, (record) => ({
|
|
108
|
+
...record,
|
|
109
|
+
status: "failed",
|
|
110
|
+
lastApplyError: error,
|
|
111
|
+
...(raw ? { lastApplyRaw: boundedTail(raw) } : {}),
|
|
112
|
+
}))
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function markReviewCommentStopped(workspaceRoot: string, id: string): ReviewCommentRecord | undefined {
|
|
116
|
+
return markReviewCommentFailed(workspaceRoot, id, "Stopped by user.", "Stopped by user.")
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export function deleteReviewComment(workspaceRoot: string, id: string): boolean {
|
|
120
|
+
const safeId = normalizeId(id)
|
|
121
|
+
if (!safeId) return false
|
|
122
|
+
const path = reviewCommentPath(workspaceRoot, safeId)
|
|
123
|
+
if (!existsSync(path)) return false
|
|
124
|
+
unlinkSync(path)
|
|
125
|
+
const registry = readRegistry(workspaceRoot)
|
|
126
|
+
writeRegistry(workspaceRoot, {
|
|
127
|
+
version: 1,
|
|
128
|
+
commentIds: registry.commentIds.filter((item) => item !== safeId),
|
|
129
|
+
})
|
|
130
|
+
return true
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function updateReviewComment(
|
|
134
|
+
workspaceRoot: string,
|
|
135
|
+
id: string,
|
|
136
|
+
update: (record: ReviewCommentRecord) => ReviewCommentRecord,
|
|
137
|
+
): ReviewCommentRecord | undefined {
|
|
138
|
+
const record = readReviewComment(workspaceRoot, id)
|
|
139
|
+
if (!record) return undefined
|
|
140
|
+
const next = { ...update(record), updatedAt: new Date().toISOString() }
|
|
141
|
+
writeReviewComment(workspaceRoot, next)
|
|
142
|
+
return next
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function writeReviewComment(workspaceRoot: string, record: ReviewCommentRecord): void {
|
|
146
|
+
const registry = readRegistry(workspaceRoot)
|
|
147
|
+
const dir = reviewCommentsDir(workspaceRoot)
|
|
148
|
+
mkdirSync(dir, { recursive: true })
|
|
149
|
+
writeFileSync(reviewCommentPath(workspaceRoot, record.id), `${JSON.stringify(record, null, 2)}\n`, "utf-8")
|
|
150
|
+
if (!registry.commentIds.includes(record.id)) {
|
|
151
|
+
registry.commentIds.push(record.id)
|
|
152
|
+
writeRegistry(workspaceRoot, registry)
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function readRegistry(workspaceRoot: string): { version: 1; commentIds: string[] } {
|
|
157
|
+
const path = join(reviewCommentsDir(workspaceRoot), "registry.json")
|
|
158
|
+
if (!existsSync(path)) return { version: 1, commentIds: [] }
|
|
159
|
+
const parsed = JSON.parse(readFileSync(path, "utf-8")) as { version?: number; commentIds?: unknown[] }
|
|
160
|
+
return {
|
|
161
|
+
version: 1,
|
|
162
|
+
commentIds: Array.isArray(parsed.commentIds) ? parsed.commentIds.filter((item): item is string => typeof item === "string") : [],
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function writeRegistry(workspaceRoot: string, registry: { version: 1; commentIds: string[] }): void {
|
|
167
|
+
const dir = reviewCommentsDir(workspaceRoot)
|
|
168
|
+
mkdirSync(dir, { recursive: true })
|
|
169
|
+
writeFileSync(join(dir, "registry.json"), `${JSON.stringify(registry, null, 2)}\n`, "utf-8")
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function reviewCommentsDir(workspaceRoot: string): string {
|
|
173
|
+
return workspaceMetaPath(workspaceRoot, "review-comments")
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function reviewCommentPath(workspaceRoot: string, id: string): string {
|
|
177
|
+
return join(reviewCommentsDir(workspaceRoot), `${id}.json`)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function normalizeDeckFile(file: string): string {
|
|
181
|
+
return file.replace(/\\/g, "/").replace(/^\.\//, "")
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function normalizeId(id: string): string {
|
|
185
|
+
const trimmed = id.trim()
|
|
186
|
+
return /^[A-Za-z0-9_-]+$/.test(trimmed) ? trimmed : ""
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function deriveSlideIndex(elements: any[]): number {
|
|
190
|
+
const indexes = new Set<number>()
|
|
191
|
+
for (const element of elements) {
|
|
192
|
+
const value = element?.slideIndex
|
|
193
|
+
if (Number.isInteger(value) && value > 0) indexes.add(value)
|
|
194
|
+
}
|
|
195
|
+
if (indexes.size === 0) throw new Error("Comment must reference one slide. Ctrl/Cmd-click an element on the target slide first.")
|
|
196
|
+
if (indexes.size > 1) throw new Error("Comment references multiple slides. Split this into per-slide comments before applying fixes.")
|
|
197
|
+
return [...indexes][0]
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function boundedTail(text: string, limit = 4096): string {
|
|
201
|
+
if (text.length <= limit) return text
|
|
202
|
+
return text.slice(text.length - limit)
|
|
203
|
+
}
|