@skillrecordings/cli 0.1.0 ā 0.2.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/bin/skill.mjs +27 -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
package/src/commands/kb-sync.ts
DELETED
|
@@ -1,498 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* KB Sync CLI Command
|
|
3
|
-
*
|
|
4
|
-
* Syncs FAQ sources to Upstash Vector + Redis knowledge base.
|
|
5
|
-
* Supports single app sync, all apps sync, and stats viewing.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import { createHash } from 'node:crypto'
|
|
9
|
-
import {
|
|
10
|
-
type IngestResult,
|
|
11
|
-
PRODUCT_SOURCES,
|
|
12
|
-
ingest,
|
|
13
|
-
listProductSources,
|
|
14
|
-
} from '@skillrecordings/core/knowledge/ingest'
|
|
15
|
-
import {
|
|
16
|
-
type KnowledgeArticle,
|
|
17
|
-
getKnowledgeNamespace,
|
|
18
|
-
getKnowledgeRedisKey,
|
|
19
|
-
} from '@skillrecordings/core/knowledge/types'
|
|
20
|
-
import { getRedis } from '@skillrecordings/core/redis/client'
|
|
21
|
-
import {
|
|
22
|
-
getVectorIndex,
|
|
23
|
-
upsertVector,
|
|
24
|
-
} from '@skillrecordings/core/vector/client'
|
|
25
|
-
import type { Command } from 'commander'
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Hash content to enable idempotent updates
|
|
29
|
-
*/
|
|
30
|
-
function hashContent(content: string): string {
|
|
31
|
-
return createHash('sha256').update(content).digest('hex').slice(0, 16)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Fetch content from a URL
|
|
36
|
-
*/
|
|
37
|
-
async function fetchContent(url: string): Promise<string> {
|
|
38
|
-
// Handle local file:// URLs
|
|
39
|
-
if (url.startsWith('file://')) {
|
|
40
|
-
const fs = await import('node:fs/promises')
|
|
41
|
-
const filePath = url.replace('file://', '')
|
|
42
|
-
return fs.readFile(filePath, 'utf-8')
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const response = await fetch(url)
|
|
46
|
-
if (!response.ok) {
|
|
47
|
-
throw new Error(
|
|
48
|
-
`Failed to fetch ${url}: ${response.status} ${response.statusText}`
|
|
49
|
-
)
|
|
50
|
-
}
|
|
51
|
-
return response.text()
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* Get stored hash for an article from Redis
|
|
56
|
-
*/
|
|
57
|
-
async function getStoredHash(
|
|
58
|
-
id: string,
|
|
59
|
-
namespace: string
|
|
60
|
-
): Promise<string | null> {
|
|
61
|
-
const redis = getRedis()
|
|
62
|
-
const key = getKnowledgeRedisKey(id, namespace)
|
|
63
|
-
const data = await redis.hget(key, 'content_hash')
|
|
64
|
-
return data as string | null
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Store article in Redis with content hash
|
|
69
|
-
*/
|
|
70
|
-
async function storeArticle(
|
|
71
|
-
article: KnowledgeArticle,
|
|
72
|
-
contentHash: string
|
|
73
|
-
): Promise<void> {
|
|
74
|
-
const redis = getRedis()
|
|
75
|
-
const namespace = getKnowledgeNamespace(article.appId)
|
|
76
|
-
const key = getKnowledgeRedisKey(article.id, namespace)
|
|
77
|
-
|
|
78
|
-
await redis.hset(key, {
|
|
79
|
-
id: article.id,
|
|
80
|
-
title: article.title,
|
|
81
|
-
question: article.question,
|
|
82
|
-
answer: article.answer,
|
|
83
|
-
appId: article.appId,
|
|
84
|
-
metadata: JSON.stringify(article.metadata),
|
|
85
|
-
content_hash: contentHash,
|
|
86
|
-
})
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Store article in vector index
|
|
91
|
-
*/
|
|
92
|
-
async function storeVector(article: KnowledgeArticle): Promise<void> {
|
|
93
|
-
const searchableText = `${article.title}\n\n${article.question}`
|
|
94
|
-
await upsertVector({
|
|
95
|
-
id: article.id,
|
|
96
|
-
data: searchableText,
|
|
97
|
-
metadata: {
|
|
98
|
-
type: 'knowledge',
|
|
99
|
-
appId: article.appId,
|
|
100
|
-
category: article.metadata.category as any,
|
|
101
|
-
source: article.metadata.source as any,
|
|
102
|
-
trustScore: article.metadata.trust_score ?? 1.0,
|
|
103
|
-
},
|
|
104
|
-
})
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* Sync result for a single app
|
|
109
|
-
*/
|
|
110
|
-
interface SyncResult {
|
|
111
|
-
appId: string
|
|
112
|
-
total: number
|
|
113
|
-
added: number
|
|
114
|
-
updated: number
|
|
115
|
-
unchanged: number
|
|
116
|
-
errors: string[]
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
/**
|
|
120
|
-
* Sync a single app's knowledge base
|
|
121
|
-
*/
|
|
122
|
-
async function syncApp(appId: string, dryRun: boolean): Promise<SyncResult> {
|
|
123
|
-
const result: SyncResult = {
|
|
124
|
-
appId,
|
|
125
|
-
total: 0,
|
|
126
|
-
added: 0,
|
|
127
|
-
updated: 0,
|
|
128
|
-
unchanged: 0,
|
|
129
|
-
errors: [],
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
const source = PRODUCT_SOURCES[appId]
|
|
133
|
-
if (!source) {
|
|
134
|
-
result.errors.push(`Unknown app: ${appId}`)
|
|
135
|
-
return result
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
if (!source.enabled) {
|
|
139
|
-
result.errors.push(`App ${appId} is not enabled for sync`)
|
|
140
|
-
return result
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
if (!source.sourceUrls || source.sourceUrls.length === 0) {
|
|
144
|
-
result.errors.push(`No source URLs configured for ${appId}`)
|
|
145
|
-
return result
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const namespace = getKnowledgeNamespace(appId)
|
|
149
|
-
|
|
150
|
-
// Fetch content from all source URLs
|
|
151
|
-
const allContent: string[] = []
|
|
152
|
-
for (const url of source.sourceUrls) {
|
|
153
|
-
try {
|
|
154
|
-
const content = await fetchContent(url)
|
|
155
|
-
allContent.push(content)
|
|
156
|
-
} catch (error) {
|
|
157
|
-
result.errors.push(
|
|
158
|
-
`Failed to fetch ${url}: ${error instanceof Error ? error.message : String(error)}`
|
|
159
|
-
)
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
if (allContent.length === 0) {
|
|
164
|
-
result.errors.push(`No content fetched for ${appId}`)
|
|
165
|
-
return result
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
// Parse content based on format
|
|
169
|
-
let ingestResult: IngestResult
|
|
170
|
-
try {
|
|
171
|
-
// For single URL, pass content directly; for multiple, pass as array
|
|
172
|
-
const content =
|
|
173
|
-
allContent.length === 1
|
|
174
|
-
? allContent[0]
|
|
175
|
-
: allContent.map((c, i) => ({
|
|
176
|
-
filePath: source.sourceUrls![i],
|
|
177
|
-
content: c,
|
|
178
|
-
}))
|
|
179
|
-
|
|
180
|
-
ingestResult = await ingest({
|
|
181
|
-
productId: appId,
|
|
182
|
-
content: content as any,
|
|
183
|
-
format: source.format,
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
if (ingestResult.errors.length > 0) {
|
|
187
|
-
for (const error of ingestResult.errors) {
|
|
188
|
-
result.errors.push(error.message)
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
} catch (error) {
|
|
192
|
-
result.errors.push(
|
|
193
|
-
`Parse error: ${error instanceof Error ? error.message : String(error)}`
|
|
194
|
-
)
|
|
195
|
-
return result
|
|
196
|
-
}
|
|
197
|
-
|
|
198
|
-
result.total = ingestResult.articles.length
|
|
199
|
-
|
|
200
|
-
// Process each article
|
|
201
|
-
for (const article of ingestResult.articles) {
|
|
202
|
-
const contentHash = hashContent(
|
|
203
|
-
`${article.title}|${article.question}|${article.answer}`
|
|
204
|
-
)
|
|
205
|
-
const storedHash = await getStoredHash(article.id, namespace)
|
|
206
|
-
|
|
207
|
-
if (storedHash === contentHash) {
|
|
208
|
-
result.unchanged++
|
|
209
|
-
continue
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
if (dryRun) {
|
|
213
|
-
if (storedHash) {
|
|
214
|
-
result.updated++
|
|
215
|
-
} else {
|
|
216
|
-
result.added++
|
|
217
|
-
}
|
|
218
|
-
continue
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// Store in Redis and Vector
|
|
222
|
-
try {
|
|
223
|
-
await storeArticle(article, contentHash)
|
|
224
|
-
await storeVector(article)
|
|
225
|
-
|
|
226
|
-
if (storedHash) {
|
|
227
|
-
result.updated++
|
|
228
|
-
} else {
|
|
229
|
-
result.added++
|
|
230
|
-
}
|
|
231
|
-
} catch (error) {
|
|
232
|
-
result.errors.push(
|
|
233
|
-
`Failed to store ${article.id}: ${error instanceof Error ? error.message : String(error)}`
|
|
234
|
-
)
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
return result
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Format duration in human-readable form
|
|
243
|
-
*/
|
|
244
|
-
function formatDuration(ms: number): string {
|
|
245
|
-
if (ms < 1000) return `${ms}ms`
|
|
246
|
-
if (ms < 60000) return `${(ms / 1000).toFixed(1)}s`
|
|
247
|
-
return `${(ms / 60000).toFixed(1)}m`
|
|
248
|
-
}
|
|
249
|
-
|
|
250
|
-
/**
|
|
251
|
-
* Sync knowledge base from FAQ sources
|
|
252
|
-
*/
|
|
253
|
-
export async function sync(options: {
|
|
254
|
-
app?: string
|
|
255
|
-
all?: boolean
|
|
256
|
-
dryRun?: boolean
|
|
257
|
-
json?: boolean
|
|
258
|
-
}): Promise<void> {
|
|
259
|
-
const startTime = Date.now()
|
|
260
|
-
|
|
261
|
-
try {
|
|
262
|
-
// Determine which apps to sync
|
|
263
|
-
let appsToSync: string[] = []
|
|
264
|
-
|
|
265
|
-
if (options.all) {
|
|
266
|
-
appsToSync = listProductSources()
|
|
267
|
-
.filter((s) => s.enabled)
|
|
268
|
-
.map((s) => s.appId)
|
|
269
|
-
} else if (options.app) {
|
|
270
|
-
appsToSync = [options.app]
|
|
271
|
-
} else {
|
|
272
|
-
if (options.json) {
|
|
273
|
-
console.error(JSON.stringify({ error: 'Must specify --app or --all' }))
|
|
274
|
-
} else {
|
|
275
|
-
console.error('Error: Must specify --app <appId> or --all')
|
|
276
|
-
}
|
|
277
|
-
process.exit(1)
|
|
278
|
-
}
|
|
279
|
-
|
|
280
|
-
if (appsToSync.length === 0) {
|
|
281
|
-
if (options.json) {
|
|
282
|
-
console.error(JSON.stringify({ error: 'No apps enabled for sync' }))
|
|
283
|
-
} else {
|
|
284
|
-
console.error('No apps enabled for sync')
|
|
285
|
-
}
|
|
286
|
-
process.exit(1)
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
const results: SyncResult[] = []
|
|
290
|
-
|
|
291
|
-
for (const appId of appsToSync) {
|
|
292
|
-
if (!options.json) {
|
|
293
|
-
console.log(
|
|
294
|
-
`\n${options.dryRun ? '[DRY RUN] ' : ''}Syncing ${appId}...`
|
|
295
|
-
)
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
const result = await syncApp(appId, options.dryRun ?? false)
|
|
299
|
-
results.push(result)
|
|
300
|
-
|
|
301
|
-
if (!options.json) {
|
|
302
|
-
if (result.errors.length > 0) {
|
|
303
|
-
console.log(` ā ļø Errors:`)
|
|
304
|
-
for (const error of result.errors) {
|
|
305
|
-
console.log(` - ${error}`)
|
|
306
|
-
}
|
|
307
|
-
}
|
|
308
|
-
console.log(` š Total: ${result.total}`)
|
|
309
|
-
console.log(` ā
Added: ${result.added}`)
|
|
310
|
-
console.log(` š Updated: ${result.updated}`)
|
|
311
|
-
console.log(` āļø Unchanged: ${result.unchanged}`)
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
const elapsed = Date.now() - startTime
|
|
316
|
-
|
|
317
|
-
// Summary
|
|
318
|
-
const summary = {
|
|
319
|
-
dryRun: options.dryRun ?? false,
|
|
320
|
-
duration: formatDuration(elapsed),
|
|
321
|
-
apps: results.length,
|
|
322
|
-
total: results.reduce((sum, r) => sum + r.total, 0),
|
|
323
|
-
added: results.reduce((sum, r) => sum + r.added, 0),
|
|
324
|
-
updated: results.reduce((sum, r) => sum + r.updated, 0),
|
|
325
|
-
unchanged: results.reduce((sum, r) => sum + r.unchanged, 0),
|
|
326
|
-
errors: results.reduce((sum, r) => sum + r.errors.length, 0),
|
|
327
|
-
results,
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
if (options.json) {
|
|
331
|
-
console.log(JSON.stringify(summary, null, 2))
|
|
332
|
-
} else {
|
|
333
|
-
console.log('\n' + 'ā'.repeat(50))
|
|
334
|
-
console.log(
|
|
335
|
-
`${options.dryRun ? '[DRY RUN] ' : ''}Sync complete in ${summary.duration}`
|
|
336
|
-
)
|
|
337
|
-
console.log(
|
|
338
|
-
`Apps: ${summary.apps} | Total: ${summary.total} | Added: ${summary.added} | Updated: ${summary.updated} | Unchanged: ${summary.unchanged}`
|
|
339
|
-
)
|
|
340
|
-
if (summary.errors > 0) {
|
|
341
|
-
console.log(`ā ļø ${summary.errors} errors occurred`)
|
|
342
|
-
}
|
|
343
|
-
}
|
|
344
|
-
} catch (error) {
|
|
345
|
-
if (options.json) {
|
|
346
|
-
console.error(
|
|
347
|
-
JSON.stringify({
|
|
348
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
349
|
-
})
|
|
350
|
-
)
|
|
351
|
-
} else {
|
|
352
|
-
console.error(
|
|
353
|
-
'Error:',
|
|
354
|
-
error instanceof Error ? error.message : 'Unknown error'
|
|
355
|
-
)
|
|
356
|
-
}
|
|
357
|
-
process.exit(1)
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
|
|
361
|
-
/**
|
|
362
|
-
* Display knowledge base statistics
|
|
363
|
-
*/
|
|
364
|
-
export async function stats(options: {
|
|
365
|
-
app?: string
|
|
366
|
-
json?: boolean
|
|
367
|
-
}): Promise<void> {
|
|
368
|
-
try {
|
|
369
|
-
const sources = listProductSources()
|
|
370
|
-
const redis = getRedis()
|
|
371
|
-
|
|
372
|
-
const appStats: Array<{
|
|
373
|
-
appId: string
|
|
374
|
-
enabled: boolean
|
|
375
|
-
format: string
|
|
376
|
-
articleCount: number
|
|
377
|
-
}> = []
|
|
378
|
-
|
|
379
|
-
for (const source of sources) {
|
|
380
|
-
if (options.app && source.appId !== options.app) {
|
|
381
|
-
continue
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
const namespace = getKnowledgeNamespace(source.appId)
|
|
385
|
-
const pattern = `${namespace}:article:*`
|
|
386
|
-
|
|
387
|
-
// Count articles using SCAN
|
|
388
|
-
let cursor = 0
|
|
389
|
-
let count = 0
|
|
390
|
-
do {
|
|
391
|
-
const [nextCursor, keys] = await redis.scan(cursor, {
|
|
392
|
-
match: pattern,
|
|
393
|
-
count: 100,
|
|
394
|
-
})
|
|
395
|
-
cursor = Number(nextCursor)
|
|
396
|
-
count += keys.length
|
|
397
|
-
} while (cursor !== 0)
|
|
398
|
-
|
|
399
|
-
appStats.push({
|
|
400
|
-
appId: source.appId,
|
|
401
|
-
enabled: source.enabled ?? false,
|
|
402
|
-
format: source.format,
|
|
403
|
-
articleCount: count,
|
|
404
|
-
})
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
if (options.json) {
|
|
408
|
-
console.log(JSON.stringify(appStats, null, 2))
|
|
409
|
-
return
|
|
410
|
-
}
|
|
411
|
-
|
|
412
|
-
console.log('\nKnowledge Base Statistics')
|
|
413
|
-
console.log('ā'.repeat(60))
|
|
414
|
-
|
|
415
|
-
const total = appStats.reduce((sum, s) => sum + s.articleCount, 0)
|
|
416
|
-
|
|
417
|
-
for (const stat of appStats) {
|
|
418
|
-
const status = stat.enabled ? 'ā
' : 'āøļø'
|
|
419
|
-
console.log(`\n${status} ${stat.appId} (${stat.format})`)
|
|
420
|
-
console.log(` Articles: ${stat.articleCount}`)
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
console.log('\n' + 'ā'.repeat(60))
|
|
424
|
-
console.log(`Total articles: ${total}`)
|
|
425
|
-
console.log('')
|
|
426
|
-
} catch (error) {
|
|
427
|
-
if (options.json) {
|
|
428
|
-
console.error(
|
|
429
|
-
JSON.stringify({
|
|
430
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
431
|
-
})
|
|
432
|
-
)
|
|
433
|
-
} else {
|
|
434
|
-
console.error(
|
|
435
|
-
'Error:',
|
|
436
|
-
error instanceof Error ? error.message : 'Unknown error'
|
|
437
|
-
)
|
|
438
|
-
}
|
|
439
|
-
process.exit(1)
|
|
440
|
-
}
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
/**
|
|
444
|
-
* List configured product sources
|
|
445
|
-
*/
|
|
446
|
-
export async function list(options: { json?: boolean }): Promise<void> {
|
|
447
|
-
const sources = listProductSources()
|
|
448
|
-
|
|
449
|
-
if (options.json) {
|
|
450
|
-
console.log(JSON.stringify(sources, null, 2))
|
|
451
|
-
return
|
|
452
|
-
}
|
|
453
|
-
|
|
454
|
-
console.log('\nConfigured Knowledge Sources')
|
|
455
|
-
console.log('ā'.repeat(60))
|
|
456
|
-
|
|
457
|
-
for (const source of sources) {
|
|
458
|
-
const status = source.enabled ? 'ā
' : 'āøļø'
|
|
459
|
-
console.log(`\n${status} ${source.appId}`)
|
|
460
|
-
console.log(` Format: ${source.format}`)
|
|
461
|
-
console.log(` Source: ${source.defaultSource || 'docs'}`)
|
|
462
|
-
console.log(` Category: ${source.defaultCategory || 'general'}`)
|
|
463
|
-
if (source.sourceUrls && source.sourceUrls.length > 0) {
|
|
464
|
-
console.log(` URLs:`)
|
|
465
|
-
for (const url of source.sourceUrls) {
|
|
466
|
-
console.log(` - ${url}`)
|
|
467
|
-
}
|
|
468
|
-
}
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
console.log('')
|
|
472
|
-
}
|
|
473
|
-
|
|
474
|
-
/**
|
|
475
|
-
* Register kb commands with Commander
|
|
476
|
-
*/
|
|
477
|
-
export function registerKbCommands(program: Command): void {
|
|
478
|
-
const kb = program.command('kb').description('Manage knowledge base content')
|
|
479
|
-
|
|
480
|
-
kb.command('sync')
|
|
481
|
-
.description('Sync FAQ sources to knowledge base')
|
|
482
|
-
.option('--app <appId>', 'Sync specific app')
|
|
483
|
-
.option('--all', 'Sync all enabled apps')
|
|
484
|
-
.option('--dry-run', 'Show what would be synced without making changes')
|
|
485
|
-
.option('--json', 'Output as JSON')
|
|
486
|
-
.action(sync)
|
|
487
|
-
|
|
488
|
-
kb.command('stats')
|
|
489
|
-
.description('Show knowledge base statistics')
|
|
490
|
-
.option('--app <appId>', 'Filter by app')
|
|
491
|
-
.option('--json', 'Output as JSON')
|
|
492
|
-
.action(stats)
|
|
493
|
-
|
|
494
|
-
kb.command('list')
|
|
495
|
-
.description('List configured knowledge sources')
|
|
496
|
-
.option('--json', 'Output as JSON')
|
|
497
|
-
.action(list)
|
|
498
|
-
}
|
|
@@ -1,135 +0,0 @@
|
|
|
1
|
-
import { calculateConfidence } from '@skillrecordings/memory/decay'
|
|
2
|
-
import { MemoryService } from '@skillrecordings/memory/memory'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Pad string to fixed width
|
|
6
|
-
*/
|
|
7
|
-
function pad(str: string, width: number): string {
|
|
8
|
-
return str.padEnd(width).slice(0, width)
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Format confidence score as percentage
|
|
13
|
-
*/
|
|
14
|
-
function formatConfidence(confidence: number): string {
|
|
15
|
-
return `${(confidence * 100).toFixed(0)}%`
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Find memories by semantic search
|
|
20
|
-
*/
|
|
21
|
-
export async function find(
|
|
22
|
-
query: string,
|
|
23
|
-
options: {
|
|
24
|
-
limit?: string
|
|
25
|
-
collection?: string
|
|
26
|
-
app?: string
|
|
27
|
-
minConfidence?: string
|
|
28
|
-
json?: boolean
|
|
29
|
-
}
|
|
30
|
-
): Promise<void> {
|
|
31
|
-
try {
|
|
32
|
-
const limit = options.limit ? parseInt(options.limit, 10) : 10
|
|
33
|
-
const threshold = options.minConfidence
|
|
34
|
-
? parseFloat(options.minConfidence)
|
|
35
|
-
: 0.5
|
|
36
|
-
|
|
37
|
-
if (limit < 1 || limit > 100) {
|
|
38
|
-
console.error('Error: --limit must be between 1 and 100')
|
|
39
|
-
process.exit(1)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
if (threshold < 0 || threshold > 1) {
|
|
43
|
-
console.error('Error: --min-confidence must be between 0 and 1')
|
|
44
|
-
process.exit(1)
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
const results = await MemoryService.find(query, {
|
|
48
|
-
collection: options.collection || 'learnings',
|
|
49
|
-
limit,
|
|
50
|
-
threshold,
|
|
51
|
-
app_slug: options.app,
|
|
52
|
-
})
|
|
53
|
-
|
|
54
|
-
if (options.json) {
|
|
55
|
-
console.log(JSON.stringify(results, null, 2))
|
|
56
|
-
return
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
if (results.length === 0) {
|
|
60
|
-
console.log('No memories found.')
|
|
61
|
-
return
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
console.log(`\nFound ${results.length} memories:\n`)
|
|
65
|
-
console.log(
|
|
66
|
-
pad('ID', 36) +
|
|
67
|
-
' ' +
|
|
68
|
-
pad('SCORE', 8) +
|
|
69
|
-
' ' +
|
|
70
|
-
pad('CONF', 6) +
|
|
71
|
-
' ' +
|
|
72
|
-
pad('AGE', 8) +
|
|
73
|
-
' ' +
|
|
74
|
-
'CONTENT'
|
|
75
|
-
)
|
|
76
|
-
console.log('-'.repeat(100))
|
|
77
|
-
|
|
78
|
-
for (const result of results) {
|
|
79
|
-
const confidence = calculateConfidence(result.memory)
|
|
80
|
-
const ageDays = Math.floor(result.age_days)
|
|
81
|
-
const ageStr =
|
|
82
|
-
ageDays === 0 ? 'today' : ageDays === 1 ? '1 day' : `${ageDays} days`
|
|
83
|
-
|
|
84
|
-
const contentPreview =
|
|
85
|
-
result.memory.content.length > 40
|
|
86
|
-
? result.memory.content.slice(0, 37) + '...'
|
|
87
|
-
: result.memory.content
|
|
88
|
-
|
|
89
|
-
console.log(
|
|
90
|
-
pad(result.memory.id, 36) +
|
|
91
|
-
' ' +
|
|
92
|
-
pad(result.score.toFixed(2), 8) +
|
|
93
|
-
' ' +
|
|
94
|
-
pad(formatConfidence(confidence), 6) +
|
|
95
|
-
' ' +
|
|
96
|
-
pad(ageStr, 8) +
|
|
97
|
-
' ' +
|
|
98
|
-
contentPreview
|
|
99
|
-
)
|
|
100
|
-
|
|
101
|
-
if (
|
|
102
|
-
result.memory.metadata.tags &&
|
|
103
|
-
result.memory.metadata.tags.length > 0
|
|
104
|
-
) {
|
|
105
|
-
console.log(
|
|
106
|
-
pad('', 36) +
|
|
107
|
-
' ' +
|
|
108
|
-
pad('', 8) +
|
|
109
|
-
' ' +
|
|
110
|
-
pad('', 6) +
|
|
111
|
-
' ' +
|
|
112
|
-
pad('', 8) +
|
|
113
|
-
' ' +
|
|
114
|
-
`Tags: ${result.memory.metadata.tags.join(', ')}`
|
|
115
|
-
)
|
|
116
|
-
}
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
console.log('')
|
|
120
|
-
} catch (error) {
|
|
121
|
-
if (options.json) {
|
|
122
|
-
console.error(
|
|
123
|
-
JSON.stringify({
|
|
124
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
125
|
-
})
|
|
126
|
-
)
|
|
127
|
-
} else {
|
|
128
|
-
console.error(
|
|
129
|
-
'Error:',
|
|
130
|
-
error instanceof Error ? error.message : 'Unknown error'
|
|
131
|
-
)
|
|
132
|
-
}
|
|
133
|
-
process.exit(1)
|
|
134
|
-
}
|
|
135
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
import { calculateConfidence } from '@skillrecordings/memory/decay'
|
|
2
|
-
import { MemoryService } from '@skillrecordings/memory/memory'
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Get a specific memory by ID
|
|
6
|
-
*/
|
|
7
|
-
export async function get(
|
|
8
|
-
id: string,
|
|
9
|
-
options: {
|
|
10
|
-
collection?: string
|
|
11
|
-
json?: boolean
|
|
12
|
-
}
|
|
13
|
-
): Promise<void> {
|
|
14
|
-
try {
|
|
15
|
-
const memory = await MemoryService.get(
|
|
16
|
-
id,
|
|
17
|
-
options.collection || 'learnings'
|
|
18
|
-
)
|
|
19
|
-
|
|
20
|
-
if (!memory) {
|
|
21
|
-
if (options.json) {
|
|
22
|
-
console.error(JSON.stringify({ error: 'Memory not found' }))
|
|
23
|
-
} else {
|
|
24
|
-
console.error('Error: Memory not found')
|
|
25
|
-
}
|
|
26
|
-
process.exit(1)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
if (options.json) {
|
|
30
|
-
console.log(JSON.stringify(memory, null, 2))
|
|
31
|
-
return
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
const confidence = calculateConfidence(memory)
|
|
35
|
-
const createdAt = new Date(memory.metadata.created_at)
|
|
36
|
-
const lastValidated = memory.metadata.last_validated_at
|
|
37
|
-
? new Date(memory.metadata.last_validated_at)
|
|
38
|
-
: null
|
|
39
|
-
|
|
40
|
-
console.log('\nš Memory Details:')
|
|
41
|
-
console.log(` ID: ${memory.id}`)
|
|
42
|
-
console.log(` Collection: ${memory.metadata.collection}`)
|
|
43
|
-
console.log(` Source: ${memory.metadata.source}`)
|
|
44
|
-
console.log(` Confidence: ${(confidence * 100).toFixed(0)}%`)
|
|
45
|
-
console.log(` Created: ${createdAt.toLocaleString()}`)
|
|
46
|
-
if (lastValidated) {
|
|
47
|
-
console.log(` Validated: ${lastValidated.toLocaleString()}`)
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
if (memory.metadata.app_slug) {
|
|
51
|
-
console.log(` App: ${memory.metadata.app_slug}`)
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
if (memory.metadata.tags && memory.metadata.tags.length > 0) {
|
|
55
|
-
console.log(` Tags: ${memory.metadata.tags.join(', ')}`)
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
console.log('\nš Content:')
|
|
59
|
-
console.log(` ${memory.content}\n`)
|
|
60
|
-
|
|
61
|
-
if (memory.metadata.votes) {
|
|
62
|
-
const { upvotes, downvotes, citations, success_rate } =
|
|
63
|
-
memory.metadata.votes
|
|
64
|
-
if (upvotes > 0 || downvotes > 0 || citations > 0) {
|
|
65
|
-
console.log('š Votes:')
|
|
66
|
-
console.log(` Upvotes: ${upvotes}`)
|
|
67
|
-
console.log(` Downvotes: ${downvotes}`)
|
|
68
|
-
console.log(` Citations: ${citations}`)
|
|
69
|
-
console.log(` Success Rate: ${(success_rate * 100).toFixed(0)}%\n`)
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
} catch (error) {
|
|
73
|
-
if (options.json) {
|
|
74
|
-
console.error(
|
|
75
|
-
JSON.stringify({
|
|
76
|
-
error: error instanceof Error ? error.message : 'Unknown error',
|
|
77
|
-
})
|
|
78
|
-
)
|
|
79
|
-
} else {
|
|
80
|
-
console.error(
|
|
81
|
-
'Error:',
|
|
82
|
-
error instanceof Error ? error.message : 'Unknown error'
|
|
83
|
-
)
|
|
84
|
-
}
|
|
85
|
-
process.exit(1)
|
|
86
|
-
}
|
|
87
|
-
}
|