@skillrecordings/cli 0.1.0 → 0.2.1
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/bin/skill.mjs +21 -0
- package/dist/chunk-2NCCVTEE.js +22342 -0
- package/dist/chunk-2NCCVTEE.js.map +1 -0
- package/dist/chunk-3E3GYSZR.js +7071 -0
- package/dist/chunk-3E3GYSZR.js.map +1 -0
- package/dist/chunk-F4EM72IH.js +86 -0
- package/dist/chunk-F4EM72IH.js.map +1 -0
- package/dist/chunk-FGP7KUQW.js +432 -0
- package/dist/chunk-FGP7KUQW.js.map +1 -0
- package/dist/chunk-H3D6VCME.js +55 -0
- package/dist/chunk-H3D6VCME.js.map +1 -0
- package/dist/chunk-HK3PEWFD.js +208 -0
- package/dist/chunk-HK3PEWFD.js.map +1 -0
- package/dist/chunk-KEV3QKXP.js +4495 -0
- package/dist/chunk-KEV3QKXP.js.map +1 -0
- package/dist/chunk-MG37YDAK.js +882 -0
- package/dist/chunk-MG37YDAK.js.map +1 -0
- package/dist/chunk-MLNDSBZ4.js +482 -0
- package/dist/chunk-MLNDSBZ4.js.map +1 -0
- package/dist/chunk-N2WIV2JV.js +22 -0
- package/dist/chunk-N2WIV2JV.js.map +1 -0
- package/dist/chunk-PWWRCN5W.js +2067 -0
- package/dist/chunk-PWWRCN5W.js.map +1 -0
- package/dist/chunk-SKHBM3XP.js +7746 -0
- package/dist/chunk-SKHBM3XP.js.map +1 -0
- package/dist/chunk-WFANXVQG.js +64 -0
- package/dist/chunk-WFANXVQG.js.map +1 -0
- package/dist/chunk-WYKL32C3.js +275 -0
- package/dist/chunk-WYKL32C3.js.map +1 -0
- package/dist/chunk-ZNF7XD2S.js +134 -0
- package/dist/chunk-ZNF7XD2S.js.map +1 -0
- package/dist/config-AUAIYDSI.js +20 -0
- package/dist/config-AUAIYDSI.js.map +1 -0
- package/dist/fileFromPath-XN7LXIBI.js +134 -0
- package/dist/fileFromPath-XN7LXIBI.js.map +1 -0
- package/dist/getMachineId-bsd-KW2E7VK3.js +42 -0
- package/dist/getMachineId-bsd-KW2E7VK3.js.map +1 -0
- package/dist/getMachineId-darwin-ROXJUJX5.js +42 -0
- package/dist/getMachineId-darwin-ROXJUJX5.js.map +1 -0
- package/dist/getMachineId-linux-KVZEHQSU.js +34 -0
- package/dist/getMachineId-linux-KVZEHQSU.js.map +1 -0
- package/dist/getMachineId-unsupported-PPRILPPA.js +25 -0
- package/dist/getMachineId-unsupported-PPRILPPA.js.map +1 -0
- package/dist/getMachineId-win-IIF36LEJ.js +44 -0
- package/dist/getMachineId-win-IIF36LEJ.js.map +1 -0
- package/dist/index.js +112703 -0
- package/dist/index.js.map +1 -0
- package/dist/lib-R6DEEJCP.js +7623 -0
- package/dist/lib-R6DEEJCP.js.map +1 -0
- package/dist/pipeline-IAVVAKTU.js +120 -0
- package/dist/pipeline-IAVVAKTU.js.map +1 -0
- package/dist/query-NTP5NVXN.js +25 -0
- package/dist/query-NTP5NVXN.js.map +1 -0
- package/dist/routing-BAEPFB7V.js +390 -0
- package/dist/routing-BAEPFB7V.js.map +1 -0
- package/dist/stripe-lookup-charge-EPRUMZDL.js +56 -0
- package/dist/stripe-lookup-charge-EPRUMZDL.js.map +1 -0
- package/dist/stripe-payment-history-SJPKA63N.js +67 -0
- package/dist/stripe-payment-history-SJPKA63N.js.map +1 -0
- package/dist/stripe-subscription-status-L4Z65GB3.js +58 -0
- package/dist/stripe-subscription-status-L4Z65GB3.js.map +1 -0
- package/dist/stripe-verify-refund-FZDKCIUQ.js +54 -0
- package/dist/stripe-verify-refund-FZDKCIUQ.js.map +1 -0
- package/dist/support-memory-WSG7SDKG.js +10 -0
- package/dist/support-memory-WSG7SDKG.js.map +1 -0
- package/package.json +10 -7
- package/.env.encrypted +0 -0
- package/CHANGELOG.md +0 -35
- package/data/tt-archive-dataset.json +0 -1
- package/data/validate-test-dataset.json +0 -97
- package/docs/CLI-AUTH.md +0 -504
- package/preload.ts +0 -18
- package/src/__tests__/init.test.ts +0 -74
- package/src/alignment-test.ts +0 -64
- package/src/check-apps.ts +0 -16
- package/src/commands/auth/decrypt.ts +0 -123
- package/src/commands/auth/encrypt.ts +0 -81
- package/src/commands/auth/index.ts +0 -50
- package/src/commands/auth/keygen.ts +0 -41
- package/src/commands/auth/status.ts +0 -164
- package/src/commands/axiom/forensic.ts +0 -868
- package/src/commands/axiom/index.ts +0 -697
- package/src/commands/build-dataset.ts +0 -311
- package/src/commands/db-status.ts +0 -47
- package/src/commands/deploys.ts +0 -219
- package/src/commands/eval-local/compare.ts +0 -171
- package/src/commands/eval-local/health.ts +0 -212
- package/src/commands/eval-local/index.ts +0 -76
- package/src/commands/eval-local/real-tools.ts +0 -416
- package/src/commands/eval-local/run.ts +0 -1168
- package/src/commands/eval-local/score-production.ts +0 -256
- package/src/commands/eval-local/seed.ts +0 -276
- package/src/commands/eval-pipeline/index.ts +0 -53
- package/src/commands/eval-pipeline/real-tools.ts +0 -492
- package/src/commands/eval-pipeline/run.ts +0 -1316
- package/src/commands/eval-pipeline/seed.ts +0 -395
- package/src/commands/eval-prompt.ts +0 -496
- package/src/commands/eval.test.ts +0 -253
- package/src/commands/eval.ts +0 -108
- package/src/commands/faq-classify.ts +0 -460
- package/src/commands/faq-cluster.ts +0 -135
- package/src/commands/faq-extract.ts +0 -249
- package/src/commands/faq-mine.ts +0 -432
- package/src/commands/faq-review.ts +0 -426
- package/src/commands/front/index.ts +0 -351
- package/src/commands/front/pull-conversations.ts +0 -275
- package/src/commands/front/tags.ts +0 -825
- package/src/commands/front-cache.ts +0 -1277
- package/src/commands/front-stats.ts +0 -75
- package/src/commands/health.test.ts +0 -82
- package/src/commands/health.ts +0 -362
- package/src/commands/init.test.ts +0 -89
- package/src/commands/init.ts +0 -106
- package/src/commands/inngest/client.ts +0 -294
- package/src/commands/inngest/events.ts +0 -296
- package/src/commands/inngest/investigate.ts +0 -382
- package/src/commands/inngest/runs.ts +0 -149
- package/src/commands/inngest/signal.ts +0 -143
- package/src/commands/kb-sync.ts +0 -498
- package/src/commands/memory/find.ts +0 -135
- package/src/commands/memory/get.ts +0 -87
- package/src/commands/memory/index.ts +0 -97
- package/src/commands/memory/stats.ts +0 -163
- package/src/commands/memory/store.ts +0 -49
- package/src/commands/memory/vote.ts +0 -159
- package/src/commands/pipeline.ts +0 -127
- package/src/commands/responses.ts +0 -856
- package/src/commands/tools.ts +0 -293
- package/src/commands/wizard.ts +0 -319
- package/src/index.ts +0 -172
- package/src/lib/crypto.ts +0 -56
- package/src/lib/env-loader.ts +0 -206
- package/src/lib/onepassword.ts +0 -137
- package/src/test-agent-local.ts +0 -115
- package/tsconfig.json +0 -11
- package/vitest.config.ts +0 -10
|
@@ -1,426 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FAQ Review CLI Command
|
|
3
|
-
*
|
|
4
|
-
* Interactive CLI for human curation of FAQ candidates.
|
|
5
|
-
* Approve, edit, reject, or skip candidates before publishing to KB.
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* skill faq-review --app total-typescript
|
|
9
|
-
* skill faq-review --app epic-react --stats
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import { spawnSync } from 'node:child_process'
|
|
13
|
-
import { existsSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs'
|
|
14
|
-
import { tmpdir } from 'node:os'
|
|
15
|
-
import { join } from 'node:path'
|
|
16
|
-
import { confirm, select } from '@inquirer/prompts'
|
|
17
|
-
import {
|
|
18
|
-
type StoredFaqCandidate,
|
|
19
|
-
approveCandidate,
|
|
20
|
-
getPendingCandidates,
|
|
21
|
-
getQueueStats,
|
|
22
|
-
rejectCandidate,
|
|
23
|
-
} from '@skillrecordings/core/faq/review'
|
|
24
|
-
import type { Command } from 'commander'
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Color codes for terminal output
|
|
28
|
-
*/
|
|
29
|
-
const COLORS = {
|
|
30
|
-
reset: '\x1b[0m',
|
|
31
|
-
green: '\x1b[32m',
|
|
32
|
-
yellow: '\x1b[33m',
|
|
33
|
-
red: '\x1b[31m',
|
|
34
|
-
blue: '\x1b[34m',
|
|
35
|
-
cyan: '\x1b[36m',
|
|
36
|
-
dim: '\x1b[2m',
|
|
37
|
-
bold: '\x1b[1m',
|
|
38
|
-
} as const
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Word wrap text to specified width
|
|
42
|
-
*/
|
|
43
|
-
function wordWrap(text: string, width: number): string {
|
|
44
|
-
const words = text.split(' ')
|
|
45
|
-
const lines: string[] = []
|
|
46
|
-
let currentLine = ''
|
|
47
|
-
|
|
48
|
-
for (const word of words) {
|
|
49
|
-
if (currentLine.length + word.length + 1 <= width) {
|
|
50
|
-
currentLine += (currentLine ? ' ' : '') + word
|
|
51
|
-
} else {
|
|
52
|
-
if (currentLine) lines.push(currentLine)
|
|
53
|
-
currentLine = word
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
if (currentLine) lines.push(currentLine)
|
|
58
|
-
return lines.join('\n')
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Display a single FAQ candidate
|
|
63
|
-
*/
|
|
64
|
-
function displayCandidate(
|
|
65
|
-
candidate: StoredFaqCandidate,
|
|
66
|
-
index: number,
|
|
67
|
-
total: number
|
|
68
|
-
): void {
|
|
69
|
-
console.log('\n' + '═'.repeat(70))
|
|
70
|
-
console.log(
|
|
71
|
-
`${COLORS.bold}FAQ Candidate ${index + 1} of ${total}${COLORS.reset}`
|
|
72
|
-
)
|
|
73
|
-
console.log('═'.repeat(70))
|
|
74
|
-
|
|
75
|
-
// Metadata row
|
|
76
|
-
const confPct = (candidate.confidence * 100).toFixed(0)
|
|
77
|
-
const unchangedPct = (candidate.unchangedRate * 100).toFixed(0)
|
|
78
|
-
console.log(
|
|
79
|
-
`${COLORS.dim}Confidence: ${confPct}% | Cluster: ${candidate.clusterSize} convos | Unchanged: ${unchangedPct}%${COLORS.reset}`
|
|
80
|
-
)
|
|
81
|
-
|
|
82
|
-
if (candidate.suggestedCategory) {
|
|
83
|
-
console.log(
|
|
84
|
-
`${COLORS.dim}Category: ${candidate.suggestedCategory}${COLORS.reset}`
|
|
85
|
-
)
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
if (candidate.tags.length > 0) {
|
|
89
|
-
console.log(
|
|
90
|
-
`${COLORS.dim}Tags: ${candidate.tags.slice(0, 5).join(', ')}${COLORS.reset}`
|
|
91
|
-
)
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
// Question
|
|
95
|
-
console.log(`\n${COLORS.bold}${COLORS.cyan}Question:${COLORS.reset}`)
|
|
96
|
-
console.log(wordWrap(candidate.question, 68))
|
|
97
|
-
|
|
98
|
-
// Answer
|
|
99
|
-
console.log(`\n${COLORS.bold}${COLORS.green}Answer:${COLORS.reset}`)
|
|
100
|
-
console.log(wordWrap(candidate.answer, 68))
|
|
101
|
-
|
|
102
|
-
console.log('\n' + '-'.repeat(70))
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
/**
|
|
106
|
-
* Get editor command
|
|
107
|
-
*/
|
|
108
|
-
function getEditor(): string {
|
|
109
|
-
return process.env.EDITOR || process.env.VISUAL || 'nano'
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
/**
|
|
113
|
-
* Open content in editor and return edited content
|
|
114
|
-
*/
|
|
115
|
-
function editInEditor(
|
|
116
|
-
question: string,
|
|
117
|
-
answer: string
|
|
118
|
-
): { question: string; answer: string } | null {
|
|
119
|
-
const editor = getEditor()
|
|
120
|
-
const tmpFile = join(tmpdir(), `faq-edit-${Date.now()}.md`)
|
|
121
|
-
|
|
122
|
-
// Write content to temp file
|
|
123
|
-
const content = `# FAQ Edit
|
|
124
|
-
|
|
125
|
-
## Question
|
|
126
|
-
${question}
|
|
127
|
-
|
|
128
|
-
## Answer
|
|
129
|
-
${answer}
|
|
130
|
-
|
|
131
|
-
<!--
|
|
132
|
-
Edit the question and answer above.
|
|
133
|
-
Save and close the editor when done.
|
|
134
|
-
The sections are separated by "## Question" and "## Answer" headers.
|
|
135
|
-
-->
|
|
136
|
-
`
|
|
137
|
-
|
|
138
|
-
writeFileSync(tmpFile, content, 'utf-8')
|
|
139
|
-
|
|
140
|
-
try {
|
|
141
|
-
// Open editor (blocking)
|
|
142
|
-
const result = spawnSync(editor, [tmpFile], {
|
|
143
|
-
stdio: 'inherit',
|
|
144
|
-
shell: true,
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
if (result.status !== 0) {
|
|
148
|
-
console.log(`${COLORS.red}Editor exited with error${COLORS.reset}`)
|
|
149
|
-
return null
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// Read edited content
|
|
153
|
-
const edited = readFileSync(tmpFile, 'utf-8')
|
|
154
|
-
|
|
155
|
-
// Parse sections
|
|
156
|
-
const questionMatch = edited.match(
|
|
157
|
-
/## Question\s*\n([\s\S]*?)(?=\n## Answer|$)/
|
|
158
|
-
)
|
|
159
|
-
const answerMatch = edited.match(/## Answer\s*\n([\s\S]*?)(?=\n<!--|$)/)
|
|
160
|
-
|
|
161
|
-
const editedQuestion = questionMatch?.[1]?.trim()
|
|
162
|
-
const editedAnswer = answerMatch?.[1]?.trim()
|
|
163
|
-
|
|
164
|
-
if (!editedQuestion || !editedAnswer) {
|
|
165
|
-
console.log(
|
|
166
|
-
`${COLORS.red}Could not parse edited content. Please keep the ## headers.${COLORS.reset}`
|
|
167
|
-
)
|
|
168
|
-
return null
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
return {
|
|
172
|
-
question: editedQuestion,
|
|
173
|
-
answer: editedAnswer,
|
|
174
|
-
}
|
|
175
|
-
} finally {
|
|
176
|
-
// Clean up temp file
|
|
177
|
-
if (existsSync(tmpFile)) {
|
|
178
|
-
unlinkSync(tmpFile)
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* Run interactive review session
|
|
185
|
-
*/
|
|
186
|
-
async function runReviewSession(options: {
|
|
187
|
-
app: string
|
|
188
|
-
}): Promise<void> {
|
|
189
|
-
console.log(`\n${COLORS.bold}📋 FAQ Review Session${COLORS.reset}`)
|
|
190
|
-
console.log(`App: ${options.app}`)
|
|
191
|
-
console.log('Loading candidates...\n')
|
|
192
|
-
|
|
193
|
-
// Load pending candidates
|
|
194
|
-
const candidates = await getPendingCandidates(options.app, 100)
|
|
195
|
-
|
|
196
|
-
if (candidates.length === 0) {
|
|
197
|
-
console.log(
|
|
198
|
-
`${COLORS.yellow}No pending FAQ candidates found.${COLORS.reset}`
|
|
199
|
-
)
|
|
200
|
-
console.log(
|
|
201
|
-
`Run ${COLORS.cyan}skill faq-mine --app ${options.app} --since 30d${COLORS.reset} to generate candidates.`
|
|
202
|
-
)
|
|
203
|
-
return
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
console.log(
|
|
207
|
-
`Found ${COLORS.bold}${candidates.length}${COLORS.reset} pending candidates.`
|
|
208
|
-
)
|
|
209
|
-
|
|
210
|
-
// Session stats
|
|
211
|
-
let approved = 0
|
|
212
|
-
let rejected = 0
|
|
213
|
-
let skipped = 0
|
|
214
|
-
let edited = 0
|
|
215
|
-
|
|
216
|
-
// Review loop
|
|
217
|
-
for (let i = 0; i < candidates.length; i++) {
|
|
218
|
-
const candidate = candidates[i]!
|
|
219
|
-
|
|
220
|
-
displayCandidate(candidate, i, candidates.length)
|
|
221
|
-
|
|
222
|
-
// Get action
|
|
223
|
-
const action = await select({
|
|
224
|
-
message: 'Action:',
|
|
225
|
-
choices: [
|
|
226
|
-
{
|
|
227
|
-
name: `${COLORS.green}[A]pprove${COLORS.reset} - Publish to KB`,
|
|
228
|
-
value: 'approve',
|
|
229
|
-
},
|
|
230
|
-
{
|
|
231
|
-
name: `${COLORS.blue}[E]dit${COLORS.reset} - Edit then publish`,
|
|
232
|
-
value: 'edit',
|
|
233
|
-
},
|
|
234
|
-
{
|
|
235
|
-
name: `${COLORS.red}[R]eject${COLORS.reset} - Won't resurface`,
|
|
236
|
-
value: 'reject',
|
|
237
|
-
},
|
|
238
|
-
{
|
|
239
|
-
name: `${COLORS.dim}[S]kip${COLORS.reset} - Review later`,
|
|
240
|
-
value: 'skip',
|
|
241
|
-
},
|
|
242
|
-
{
|
|
243
|
-
name: `${COLORS.dim}[Q]uit${COLORS.reset} - End session`,
|
|
244
|
-
value: 'quit',
|
|
245
|
-
},
|
|
246
|
-
],
|
|
247
|
-
})
|
|
248
|
-
|
|
249
|
-
if (action === 'quit') {
|
|
250
|
-
break
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
if (action === 'skip') {
|
|
254
|
-
skipped++
|
|
255
|
-
console.log(`${COLORS.dim}⏭ Skipped${COLORS.reset}`)
|
|
256
|
-
continue
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
if (action === 'reject') {
|
|
260
|
-
const result = await rejectCandidate(
|
|
261
|
-
candidate.id,
|
|
262
|
-
'Rejected via CLI review'
|
|
263
|
-
)
|
|
264
|
-
if (result.success) {
|
|
265
|
-
rejected++
|
|
266
|
-
console.log(`${COLORS.red}✗ Rejected - won't resurface${COLORS.reset}`)
|
|
267
|
-
} else {
|
|
268
|
-
console.log(
|
|
269
|
-
`${COLORS.red}✗ Failed to reject: ${result.error}${COLORS.reset}`
|
|
270
|
-
)
|
|
271
|
-
}
|
|
272
|
-
continue
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
// approve or edit
|
|
276
|
-
let finalQuestion = candidate.question
|
|
277
|
-
let finalAnswer = candidate.answer
|
|
278
|
-
let wasEdited = false
|
|
279
|
-
|
|
280
|
-
if (action === 'edit') {
|
|
281
|
-
console.log(`\nOpening ${getEditor()}...`)
|
|
282
|
-
const editResult = editInEditor(candidate.question, candidate.answer)
|
|
283
|
-
|
|
284
|
-
if (!editResult) {
|
|
285
|
-
console.log('Edit cancelled. Skipping candidate.')
|
|
286
|
-
skipped++
|
|
287
|
-
continue
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
finalQuestion = editResult.question
|
|
291
|
-
finalAnswer = editResult.answer
|
|
292
|
-
wasEdited =
|
|
293
|
-
finalQuestion !== candidate.question || finalAnswer !== candidate.answer
|
|
294
|
-
|
|
295
|
-
if (wasEdited) {
|
|
296
|
-
console.log(`\n${COLORS.yellow}Content was edited.${COLORS.reset}`)
|
|
297
|
-
console.log(`\n${COLORS.bold}New Question:${COLORS.reset}`)
|
|
298
|
-
console.log(wordWrap(finalQuestion, 68))
|
|
299
|
-
console.log(`\n${COLORS.bold}New Answer:${COLORS.reset}`)
|
|
300
|
-
console.log(wordWrap(finalAnswer, 68))
|
|
301
|
-
|
|
302
|
-
const confirmPublish = await confirm({
|
|
303
|
-
message: 'Publish edited FAQ?',
|
|
304
|
-
default: true,
|
|
305
|
-
})
|
|
306
|
-
|
|
307
|
-
if (!confirmPublish) {
|
|
308
|
-
skipped++
|
|
309
|
-
continue
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
}
|
|
313
|
-
|
|
314
|
-
// Publish (approve or edit)
|
|
315
|
-
const result = await approveCandidate(candidate.id, {
|
|
316
|
-
question: wasEdited ? finalQuestion : undefined,
|
|
317
|
-
answer: wasEdited ? finalAnswer : undefined,
|
|
318
|
-
editNotes: wasEdited ? 'Edited during CLI review' : undefined,
|
|
319
|
-
})
|
|
320
|
-
|
|
321
|
-
if (result.success) {
|
|
322
|
-
approved++
|
|
323
|
-
if (wasEdited) edited++
|
|
324
|
-
console.log(
|
|
325
|
-
`${COLORS.green}✓ Published as ${result.articleId}${COLORS.reset}`
|
|
326
|
-
)
|
|
327
|
-
} else {
|
|
328
|
-
console.log(
|
|
329
|
-
`${COLORS.red}✗ Failed to publish: ${result.error}${COLORS.reset}`
|
|
330
|
-
)
|
|
331
|
-
}
|
|
332
|
-
}
|
|
333
|
-
|
|
334
|
-
// Session summary
|
|
335
|
-
console.log('\n' + '═'.repeat(70))
|
|
336
|
-
console.log(`${COLORS.bold}📊 Session Summary${COLORS.reset}`)
|
|
337
|
-
console.log('═'.repeat(70))
|
|
338
|
-
console.log(
|
|
339
|
-
`${COLORS.green}Approved: ${approved}${edited > 0 ? ` (${edited} edited)` : ''}${COLORS.reset}`
|
|
340
|
-
)
|
|
341
|
-
console.log(`${COLORS.red}Rejected: ${rejected}${COLORS.reset}`)
|
|
342
|
-
console.log(`${COLORS.dim}Skipped: ${skipped}${COLORS.reset}`)
|
|
343
|
-
console.log('')
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
/**
|
|
347
|
-
* Display review statistics
|
|
348
|
-
*/
|
|
349
|
-
async function showStats(appId: string, json: boolean): Promise<void> {
|
|
350
|
-
const stats = await getQueueStats(appId)
|
|
351
|
-
|
|
352
|
-
if (json) {
|
|
353
|
-
console.log(JSON.stringify(stats, null, 2))
|
|
354
|
-
return
|
|
355
|
-
}
|
|
356
|
-
|
|
357
|
-
console.log(
|
|
358
|
-
`\n${COLORS.bold}📊 Review Queue Statistics for ${appId}${COLORS.reset}`
|
|
359
|
-
)
|
|
360
|
-
console.log('─'.repeat(40))
|
|
361
|
-
console.log(`Pending: ${stats.pending}`)
|
|
362
|
-
console.log(
|
|
363
|
-
`${COLORS.green}Approved: ${stats.approved}${COLORS.reset}`
|
|
364
|
-
)
|
|
365
|
-
console.log(`${COLORS.red}Rejected: ${stats.rejected}${COLORS.reset}`)
|
|
366
|
-
console.log(`Total: ${stats.total}`)
|
|
367
|
-
|
|
368
|
-
if (stats.total > 0 && stats.approved + stats.rejected > 0) {
|
|
369
|
-
const approvalRate = (
|
|
370
|
-
(stats.approved / (stats.approved + stats.rejected)) *
|
|
371
|
-
100
|
|
372
|
-
).toFixed(1)
|
|
373
|
-
console.log(`\nApproval rate: ${approvalRate}%`)
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
console.log('')
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
/**
|
|
380
|
-
* Main command handler
|
|
381
|
-
*/
|
|
382
|
-
async function faqReview(options: {
|
|
383
|
-
app: string
|
|
384
|
-
stats?: boolean
|
|
385
|
-
json?: boolean
|
|
386
|
-
}): Promise<void> {
|
|
387
|
-
if (!options.app) {
|
|
388
|
-
console.error('Error: --app is required')
|
|
389
|
-
process.exit(1)
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
try {
|
|
393
|
-
if (options.stats) {
|
|
394
|
-
await showStats(options.app, options.json ?? false)
|
|
395
|
-
} else {
|
|
396
|
-
await runReviewSession({
|
|
397
|
-
app: options.app,
|
|
398
|
-
})
|
|
399
|
-
}
|
|
400
|
-
} catch (error) {
|
|
401
|
-
if ((error as any)?.name === 'ExitPromptError') {
|
|
402
|
-
// User pressed Ctrl+C
|
|
403
|
-
console.log('\n\nReview session cancelled.')
|
|
404
|
-
process.exit(0)
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
console.error(
|
|
408
|
-
'Error:',
|
|
409
|
-
error instanceof Error ? error.message : String(error)
|
|
410
|
-
)
|
|
411
|
-
process.exit(1)
|
|
412
|
-
}
|
|
413
|
-
}
|
|
414
|
-
|
|
415
|
-
/**
|
|
416
|
-
* Register FAQ review commands with Commander
|
|
417
|
-
*/
|
|
418
|
-
export function registerFaqReviewCommands(program: Command): void {
|
|
419
|
-
program
|
|
420
|
-
.command('faq-review')
|
|
421
|
-
.description('Interactive review of FAQ candidates')
|
|
422
|
-
.requiredOption('-a, --app <slug>', 'App slug to review (required)')
|
|
423
|
-
.option('--stats', 'Show review statistics instead of interactive review')
|
|
424
|
-
.option('--json', 'Output stats as JSON (use with --stats)')
|
|
425
|
-
.action(faqReview)
|
|
426
|
-
}
|