agentikit 0.0.13 → 0.0.15
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/LICENSE +385 -0
- package/README.md +187 -110
- package/dist/{src/asset-spec.js → asset-spec.js} +11 -2
- package/dist/{src/asset-type-handler.js → asset-type-handler.js} +4 -3
- package/dist/cli.js +709 -0
- package/dist/common.js +192 -0
- package/dist/{src/config-cli.js → config-cli.js} +36 -30
- package/dist/{src/config.js → config.js} +95 -25
- package/dist/{src/db.js → db.js} +123 -51
- package/dist/{src/embedder.js → embedder.js} +57 -2
- package/dist/errors.js +28 -0
- package/dist/file-context.js +188 -0
- package/dist/{src/frontmatter.js → frontmatter.js} +1 -1
- package/dist/{src/github.js → github.js} +1 -3
- package/dist/handlers/agent-handler.js +19 -0
- package/dist/handlers/command-handler.js +20 -0
- package/dist/handlers/handler-bridge.js +51 -0
- package/dist/handlers/index.js +19 -0
- package/dist/handlers/knowledge-handler.js +32 -0
- package/dist/handlers/script-handler.js +42 -0
- package/dist/{src/handlers → handlers}/skill-handler.js +5 -6
- package/dist/{src/handlers → handlers}/tool-handler.js +8 -24
- package/dist/{src/indexer.js → indexer.js} +50 -26
- package/dist/init.js +43 -0
- package/dist/{src/llm.js → llm.js} +6 -11
- package/dist/lockfile.js +60 -0
- package/dist/matchers.js +163 -0
- package/dist/{src/metadata.js → metadata.js} +36 -16
- package/dist/{src/origin-resolve.js → origin-resolve.js} +10 -9
- package/dist/paths.js +83 -0
- package/dist/{src/registry-install.js → registry-install.js} +151 -19
- package/dist/{src/registry-resolve.js → registry-resolve.js} +190 -26
- package/dist/{src/registry-search.js → registry-search.js} +13 -21
- package/dist/renderers.js +286 -0
- package/dist/{src/ripgrep-install.js → ripgrep-install.js} +8 -27
- package/dist/{src/ripgrep-resolve.js → ripgrep-resolve.js} +21 -11
- package/dist/ripgrep.js +2 -0
- package/dist/self-update.js +226 -0
- package/dist/{src/stash-add.js → stash-add.js} +14 -4
- package/dist/stash-clone.js +115 -0
- package/dist/{src/stash-ref.js → stash-ref.js} +10 -9
- package/dist/{src/stash-registry.js → stash-registry.js} +21 -46
- package/dist/{src/stash-resolve.js → stash-resolve.js} +10 -9
- package/dist/{src/stash-search.js → stash-search.js} +89 -74
- package/dist/stash-show.js +74 -0
- package/dist/stash-source.js +127 -0
- package/dist/submit.js +557 -0
- package/dist/{src/tool-runner.js → tool-runner.js} +1 -5
- package/dist/{src/walker.js → walker.js} +38 -0
- package/dist/warn.js +20 -0
- package/package.json +13 -18
- package/dist/index.d.ts +0 -28
- package/dist/index.js +0 -15
- package/dist/src/asset-spec.d.ts +0 -16
- package/dist/src/asset-type-handler.d.ts +0 -27
- package/dist/src/cli.d.ts +0 -2
- package/dist/src/cli.js +0 -399
- package/dist/src/common.d.ts +0 -13
- package/dist/src/common.js +0 -60
- package/dist/src/config-cli.d.ts +0 -9
- package/dist/src/config.d.ts +0 -50
- package/dist/src/db.d.ts +0 -46
- package/dist/src/embedder.d.ts +0 -10
- package/dist/src/frontmatter.d.ts +0 -30
- package/dist/src/github.d.ts +0 -4
- package/dist/src/handlers/agent-handler.d.ts +0 -2
- package/dist/src/handlers/agent-handler.js +0 -26
- package/dist/src/handlers/command-handler.d.ts +0 -2
- package/dist/src/handlers/command-handler.js +0 -23
- package/dist/src/handlers/index.d.ts +0 -6
- package/dist/src/handlers/index.js +0 -23
- package/dist/src/handlers/knowledge-handler.d.ts +0 -2
- package/dist/src/handlers/knowledge-handler.js +0 -56
- package/dist/src/handlers/markdown-helpers.d.ts +0 -7
- package/dist/src/handlers/script-handler.d.ts +0 -2
- package/dist/src/handlers/script-handler.js +0 -78
- package/dist/src/handlers/skill-handler.d.ts +0 -2
- package/dist/src/handlers/tool-handler.d.ts +0 -2
- package/dist/src/indexer.d.ts +0 -22
- package/dist/src/init.d.ts +0 -19
- package/dist/src/init.js +0 -99
- package/dist/src/llm.d.ts +0 -15
- package/dist/src/markdown.d.ts +0 -18
- package/dist/src/metadata.d.ts +0 -41
- package/dist/src/origin-resolve.d.ts +0 -19
- package/dist/src/registry-install.d.ts +0 -11
- package/dist/src/registry-resolve.d.ts +0 -3
- package/dist/src/registry-search.d.ts +0 -27
- package/dist/src/registry-types.d.ts +0 -62
- package/dist/src/ripgrep-install.d.ts +0 -12
- package/dist/src/ripgrep-resolve.d.ts +0 -13
- package/dist/src/ripgrep.d.ts +0 -3
- package/dist/src/ripgrep.js +0 -2
- package/dist/src/stash-add.d.ts +0 -4
- package/dist/src/stash-clone.d.ts +0 -22
- package/dist/src/stash-clone.js +0 -83
- package/dist/src/stash-ref.d.ts +0 -31
- package/dist/src/stash-registry.d.ts +0 -18
- package/dist/src/stash-resolve.d.ts +0 -2
- package/dist/src/stash-search.d.ts +0 -8
- package/dist/src/stash-show.d.ts +0 -5
- package/dist/src/stash-show.js +0 -46
- package/dist/src/stash-source.d.ts +0 -24
- package/dist/src/stash-source.js +0 -81
- package/dist/src/stash-types.d.ts +0 -227
- package/dist/src/stash.d.ts +0 -16
- package/dist/src/stash.js +0 -9
- package/dist/src/tool-runner.d.ts +0 -35
- package/dist/src/walker.d.ts +0 -19
- package/src/asset-spec.ts +0 -85
- package/src/asset-type-handler.ts +0 -77
- package/src/cli.ts +0 -427
- package/src/common.ts +0 -76
- package/src/config-cli.ts +0 -499
- package/src/config.ts +0 -305
- package/src/db.ts +0 -411
- package/src/embedder.ts +0 -128
- package/src/frontmatter.ts +0 -95
- package/src/github.ts +0 -21
- package/src/handlers/agent-handler.ts +0 -32
- package/src/handlers/command-handler.ts +0 -29
- package/src/handlers/index.ts +0 -25
- package/src/handlers/knowledge-handler.ts +0 -62
- package/src/handlers/markdown-helpers.ts +0 -19
- package/src/handlers/script-handler.ts +0 -92
- package/src/handlers/skill-handler.ts +0 -37
- package/src/handlers/tool-handler.ts +0 -71
- package/src/indexer.ts +0 -392
- package/src/init.ts +0 -114
- package/src/llm.ts +0 -125
- package/src/markdown.ts +0 -106
- package/src/metadata.ts +0 -333
- package/src/origin-resolve.ts +0 -67
- package/src/registry-install.ts +0 -361
- package/src/registry-resolve.ts +0 -341
- package/src/registry-search.ts +0 -335
- package/src/registry-types.ts +0 -72
- package/src/ripgrep-install.ts +0 -200
- package/src/ripgrep-resolve.ts +0 -72
- package/src/ripgrep.ts +0 -3
- package/src/stash-add.ts +0 -63
- package/src/stash-clone.ts +0 -127
- package/src/stash-ref.ts +0 -99
- package/src/stash-registry.ts +0 -259
- package/src/stash-resolve.ts +0 -50
- package/src/stash-search.ts +0 -613
- package/src/stash-show.ts +0 -55
- package/src/stash-source.ts +0 -103
- package/src/stash-types.ts +0 -231
- package/src/stash.ts +0 -39
- package/src/tool-runner.ts +0 -142
- package/src/walker.ts +0 -53
- /package/dist/{src/handlers → handlers}/markdown-helpers.js +0 -0
- /package/dist/{src/markdown.js → markdown.js} +0 -0
- /package/dist/{src/registry-types.js → registry-types.js} +0 -0
- /package/dist/{src/stash-types.js → stash-types.js} +0 -0
package/src/stash-search.ts
DELETED
|
@@ -1,613 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs"
|
|
2
|
-
import path from "node:path"
|
|
3
|
-
import { type AgentikitAssetType } from "./common"
|
|
4
|
-
import { ASSET_TYPES, TYPE_DIRS, deriveCanonicalAssetName } from "./asset-spec"
|
|
5
|
-
import { buildSearchText } from "./indexer"
|
|
6
|
-
import { walkStash } from "./walker"
|
|
7
|
-
import { makeAssetRef } from "./stash-ref"
|
|
8
|
-
import type {
|
|
9
|
-
AgentikitSearchType,
|
|
10
|
-
LocalSearchHit,
|
|
11
|
-
RegistrySearchResultHit,
|
|
12
|
-
SearchHit,
|
|
13
|
-
SearchResponse,
|
|
14
|
-
SearchSource,
|
|
15
|
-
SearchUsageMode,
|
|
16
|
-
} from "./stash-types"
|
|
17
|
-
import { loadConfig } from "./config"
|
|
18
|
-
import { searchRegistry } from "./registry-search"
|
|
19
|
-
import {
|
|
20
|
-
openDatabase,
|
|
21
|
-
closeDatabase,
|
|
22
|
-
getDbPath,
|
|
23
|
-
getMeta,
|
|
24
|
-
searchFts,
|
|
25
|
-
searchVec,
|
|
26
|
-
getAllEntries,
|
|
27
|
-
getEntryCount,
|
|
28
|
-
getEntryById,
|
|
29
|
-
isVecAvailable,
|
|
30
|
-
type DbSearchResult,
|
|
31
|
-
} from "./db"
|
|
32
|
-
import { tryGetHandler } from "./asset-type-handler"
|
|
33
|
-
import { type StashSource, resolveStashSources, findSourceForPath } from "./stash-source"
|
|
34
|
-
|
|
35
|
-
type IndexedAsset = {
|
|
36
|
-
type: AgentikitAssetType
|
|
37
|
-
name: string
|
|
38
|
-
path: string
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
const DEFAULT_LIMIT = 20
|
|
42
|
-
|
|
43
|
-
export async function agentikitSearch(input: {
|
|
44
|
-
query: string
|
|
45
|
-
type?: AgentikitSearchType
|
|
46
|
-
limit?: number
|
|
47
|
-
usage?: SearchUsageMode
|
|
48
|
-
source?: SearchSource
|
|
49
|
-
}): Promise<SearchResponse> {
|
|
50
|
-
const t0 = Date.now()
|
|
51
|
-
const query = input.query.trim()
|
|
52
|
-
const normalizedQuery = query.toLowerCase()
|
|
53
|
-
const searchType = input.type ?? "any"
|
|
54
|
-
const limit = normalizeLimit(input.limit)
|
|
55
|
-
const usageMode = parseSearchUsageMode(input.usage)
|
|
56
|
-
const source = parseSearchSource(input.source)
|
|
57
|
-
const sources = resolveStashSources()
|
|
58
|
-
const stashDir = sources[0].path
|
|
59
|
-
const localResult = source === "registry"
|
|
60
|
-
? undefined
|
|
61
|
-
: await searchLocal({
|
|
62
|
-
query: normalizedQuery,
|
|
63
|
-
searchType,
|
|
64
|
-
limit,
|
|
65
|
-
usageMode,
|
|
66
|
-
stashDir,
|
|
67
|
-
sources,
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
const config = loadConfig()
|
|
71
|
-
const registryResult = source === "local"
|
|
72
|
-
? undefined
|
|
73
|
-
: await searchRegistry(query, { limit, registryUrls: config.registryUrls })
|
|
74
|
-
|
|
75
|
-
if (source === "local") {
|
|
76
|
-
return {
|
|
77
|
-
stashDir,
|
|
78
|
-
source,
|
|
79
|
-
hits: localResult?.hits ?? [],
|
|
80
|
-
usageGuide: localResult?.usageGuide,
|
|
81
|
-
tip: localResult?.tip,
|
|
82
|
-
timing: { totalMs: Date.now() - t0, rankMs: localResult?.rankMs, embedMs: localResult?.embedMs },
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const registryHits = (registryResult?.hits ?? []).map((hit): RegistrySearchResultHit => {
|
|
87
|
-
const installRef = hit.source === "npm" ? `npm:${hit.ref}` : `github:${hit.ref}`
|
|
88
|
-
return {
|
|
89
|
-
hitSource: "registry",
|
|
90
|
-
type: "registry",
|
|
91
|
-
name: hit.title,
|
|
92
|
-
id: hit.id,
|
|
93
|
-
registrySource: hit.source,
|
|
94
|
-
ref: hit.ref,
|
|
95
|
-
description: hit.description,
|
|
96
|
-
homepage: hit.homepage,
|
|
97
|
-
score: hit.score,
|
|
98
|
-
metadata: hit.metadata,
|
|
99
|
-
curated: hit.curated,
|
|
100
|
-
installRef,
|
|
101
|
-
installCmd: `akm add ${installRef}`,
|
|
102
|
-
}
|
|
103
|
-
})
|
|
104
|
-
|
|
105
|
-
if (source === "registry") {
|
|
106
|
-
const hits = registryHits.slice(0, limit)
|
|
107
|
-
return {
|
|
108
|
-
stashDir,
|
|
109
|
-
source,
|
|
110
|
-
hits,
|
|
111
|
-
tip: hits.length === 0 ? "No matching registry entries were found." : undefined,
|
|
112
|
-
warnings: registryResult?.warnings.length ? registryResult.warnings : undefined,
|
|
113
|
-
timing: { totalMs: Date.now() - t0 },
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
const mergedHits = mergeSearchHits(localResult?.hits ?? [], registryHits, limit)
|
|
118
|
-
|
|
119
|
-
return {
|
|
120
|
-
stashDir,
|
|
121
|
-
source,
|
|
122
|
-
hits: mergedHits,
|
|
123
|
-
usageGuide: localResult?.usageGuide,
|
|
124
|
-
tip: mergedHits.length === 0 ? "No matching stash assets or registry entries were found." : undefined,
|
|
125
|
-
warnings: registryResult?.warnings.length ? registryResult.warnings : undefined,
|
|
126
|
-
timing: { totalMs: Date.now() - t0 },
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
async function searchLocal(input: {
|
|
131
|
-
query: string
|
|
132
|
-
searchType: AgentikitSearchType
|
|
133
|
-
limit: number
|
|
134
|
-
usageMode: SearchUsageMode
|
|
135
|
-
stashDir: string
|
|
136
|
-
sources: StashSource[]
|
|
137
|
-
}): Promise<{ hits: LocalSearchHit[]; usageGuide?: Partial<Record<AgentikitAssetType, string[]>>; tip?: string; embedMs?: number; rankMs?: number }> {
|
|
138
|
-
const { query, searchType, limit, usageMode, stashDir, sources } = input
|
|
139
|
-
const config = loadConfig()
|
|
140
|
-
const allStashDirs = sources.map((s) => s.path)
|
|
141
|
-
|
|
142
|
-
// Try to open the database
|
|
143
|
-
const dbPath = getDbPath()
|
|
144
|
-
try {
|
|
145
|
-
if (fs.existsSync(dbPath)) {
|
|
146
|
-
const embeddingDim = config.embedding?.dimension
|
|
147
|
-
const db = openDatabase(dbPath, embeddingDim ? { embeddingDim } : undefined)
|
|
148
|
-
try {
|
|
149
|
-
const entryCount = getEntryCount(db)
|
|
150
|
-
const storedStashDir = getMeta(db, "stashDir")
|
|
151
|
-
if (entryCount > 0 && storedStashDir === stashDir) {
|
|
152
|
-
const { hits, usageGuide, embedMs, rankMs } = await searchDatabase(db, query, searchType, limit, stashDir, allStashDirs, config, usageMode, sources)
|
|
153
|
-
return {
|
|
154
|
-
hits,
|
|
155
|
-
usageGuide,
|
|
156
|
-
tip: hits.length === 0 ? "No matching stash assets were found. Try running 'akm index' to rebuild." : undefined,
|
|
157
|
-
embedMs,
|
|
158
|
-
rankMs,
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
} finally {
|
|
162
|
-
closeDatabase(db)
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
} catch (error) {
|
|
166
|
-
console.warn("Search index unavailable, falling back to substring search:", error instanceof Error ? error.message : String(error))
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
const hits = allStashDirs
|
|
170
|
-
.flatMap((dir) => substringSearch(query, searchType, limit, dir, sources))
|
|
171
|
-
.slice(0, limit)
|
|
172
|
-
const usageGuide = shouldIncludeUsageGuide(usageMode) ? buildUsageGuide(hits.map((hit) => hit.type), searchType) : undefined
|
|
173
|
-
return {
|
|
174
|
-
hits,
|
|
175
|
-
usageGuide,
|
|
176
|
-
tip: hits.length === 0 ? "No matching stash assets were found. Try running 'akm index' to rebuild." : undefined,
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// ── Database search ─────────────────────────────────────────────────────────
|
|
181
|
-
|
|
182
|
-
async function searchDatabase(
|
|
183
|
-
db: import("bun:sqlite").Database,
|
|
184
|
-
query: string,
|
|
185
|
-
searchType: AgentikitSearchType,
|
|
186
|
-
limit: number,
|
|
187
|
-
stashDir: string,
|
|
188
|
-
allStashDirs: string[],
|
|
189
|
-
config: import("./config").AgentikitConfig,
|
|
190
|
-
usageMode: SearchUsageMode,
|
|
191
|
-
sources: StashSource[],
|
|
192
|
-
): Promise<{ hits: LocalSearchHit[]; usageGuide?: Partial<Record<AgentikitAssetType, string[]>>; embedMs?: number; rankMs?: number }> {
|
|
193
|
-
// Empty query: return all entries
|
|
194
|
-
if (!query) {
|
|
195
|
-
const typeFilter = searchType === "any" ? undefined : searchType
|
|
196
|
-
const allEntries = getAllEntries(db, typeFilter)
|
|
197
|
-
const selected = allEntries.slice(0, limit)
|
|
198
|
-
const hits = selected.map((ie) =>
|
|
199
|
-
buildDbHit({
|
|
200
|
-
entry: ie.entry,
|
|
201
|
-
path: ie.filePath,
|
|
202
|
-
score: 1,
|
|
203
|
-
query,
|
|
204
|
-
rankingMode: "fts",
|
|
205
|
-
defaultStashDir: stashDir,
|
|
206
|
-
allStashDirs,
|
|
207
|
-
sources,
|
|
208
|
-
includeItemUsage: shouldIncludeItemUsage(usageMode),
|
|
209
|
-
}),
|
|
210
|
-
)
|
|
211
|
-
return {
|
|
212
|
-
hits,
|
|
213
|
-
usageGuide: shouldIncludeUsageGuide(usageMode)
|
|
214
|
-
? buildUsageGuideFromEntries(selected.map((e) => e.entry), searchType)
|
|
215
|
-
: undefined,
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
// Score using FTS5 (BM25) and optionally sqlite-vec
|
|
220
|
-
const tEmbed0 = Date.now()
|
|
221
|
-
const embeddingScores = await tryVecScores(db, query, limit * 3, config)
|
|
222
|
-
const embedMs = Date.now() - tEmbed0
|
|
223
|
-
|
|
224
|
-
const tRank0 = Date.now()
|
|
225
|
-
const typeFilter = searchType === "any" ? undefined : searchType
|
|
226
|
-
const ftsResults = searchFts(db, query, limit * 3, typeFilter)
|
|
227
|
-
|
|
228
|
-
// Build score map from FTS results (normalize BM25 scores)
|
|
229
|
-
const ftsScoreMap = new Map<number, { score: number; result: DbSearchResult }>()
|
|
230
|
-
for (const r of ftsResults) {
|
|
231
|
-
// BM25 returns negative scores (more negative = better match), normalize to 0-1
|
|
232
|
-
const absScore = Math.abs(r.bm25Score)
|
|
233
|
-
const normalized = absScore / (1 + absScore)
|
|
234
|
-
ftsScoreMap.set(r.id, { score: normalized, result: r })
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Blend scores
|
|
238
|
-
const scored: Array<{ id: number; entry: import("./metadata").StashEntry; filePath: string; score: number; rankingMode: "semantic" | "fts" }> = []
|
|
239
|
-
const seenIds = new Set<number>()
|
|
240
|
-
|
|
241
|
-
// Process FTS results
|
|
242
|
-
for (const [id, { score: ftsScore, result }] of ftsScoreMap) {
|
|
243
|
-
seenIds.add(id)
|
|
244
|
-
const embScore = embeddingScores?.get(id)
|
|
245
|
-
|
|
246
|
-
if (embScore !== undefined) {
|
|
247
|
-
const blended = embScore * 0.7 + ftsScore * 0.3
|
|
248
|
-
if (blended > 0) scored.push({ id, entry: result.entry, filePath: result.filePath, score: blended, rankingMode: "semantic" })
|
|
249
|
-
} else if (ftsScore > 0) {
|
|
250
|
-
scored.push({ id, entry: result.entry, filePath: result.filePath, score: ftsScore, rankingMode: "fts" })
|
|
251
|
-
}
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
// Add vec-only results not already in FTS results
|
|
255
|
-
if (embeddingScores) {
|
|
256
|
-
for (const [id, embScore] of embeddingScores) {
|
|
257
|
-
if (seenIds.has(id)) continue
|
|
258
|
-
const found = getEntryById(db, id)
|
|
259
|
-
if (found) {
|
|
260
|
-
if (typeFilter && found.entry.type !== typeFilter) continue
|
|
261
|
-
scored.push({
|
|
262
|
-
id,
|
|
263
|
-
entry: found.entry,
|
|
264
|
-
filePath: found.filePath,
|
|
265
|
-
score: embScore,
|
|
266
|
-
rankingMode: "semantic",
|
|
267
|
-
})
|
|
268
|
-
}
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// Apply boosts (tag, intent, name matches)
|
|
273
|
-
const queryTokens = query.toLowerCase().split(/\s+/).filter(Boolean)
|
|
274
|
-
for (const item of scored) {
|
|
275
|
-
const entry = item.entry
|
|
276
|
-
// Tag boost
|
|
277
|
-
if (entry.tags) {
|
|
278
|
-
for (const tag of entry.tags) {
|
|
279
|
-
if (queryTokens.some((t) => tag.toLowerCase() === t)) {
|
|
280
|
-
item.score += 0.15
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
// Intent boost
|
|
285
|
-
if (entry.intents) {
|
|
286
|
-
for (const intent of entry.intents) {
|
|
287
|
-
const intentLower = intent.toLowerCase()
|
|
288
|
-
for (const token of queryTokens) {
|
|
289
|
-
if (intentLower.includes(token)) {
|
|
290
|
-
item.score += 0.12
|
|
291
|
-
break
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
// Name boost
|
|
297
|
-
const nameLower = entry.name.toLowerCase().replace(/[-_]/g, " ")
|
|
298
|
-
if (queryTokens.some((t) => nameLower.includes(t))) {
|
|
299
|
-
item.score += 0.1
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
for (const item of scored) {
|
|
304
|
-
item.score = Math.min(item.score, 1.0)
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
scored.sort((a, b) => b.score - a.score)
|
|
308
|
-
const rankMs = Date.now() - tRank0
|
|
309
|
-
|
|
310
|
-
const selected = scored.slice(0, limit)
|
|
311
|
-
const hits = selected.map(({ entry, filePath, score, rankingMode }) =>
|
|
312
|
-
buildDbHit({
|
|
313
|
-
entry,
|
|
314
|
-
path: filePath,
|
|
315
|
-
score: Math.round(score * 1000) / 1000,
|
|
316
|
-
query,
|
|
317
|
-
rankingMode,
|
|
318
|
-
defaultStashDir: stashDir,
|
|
319
|
-
allStashDirs,
|
|
320
|
-
sources,
|
|
321
|
-
includeItemUsage: shouldIncludeItemUsage(usageMode),
|
|
322
|
-
}),
|
|
323
|
-
)
|
|
324
|
-
|
|
325
|
-
return {
|
|
326
|
-
embedMs,
|
|
327
|
-
rankMs,
|
|
328
|
-
hits,
|
|
329
|
-
usageGuide: shouldIncludeUsageGuide(usageMode)
|
|
330
|
-
? buildUsageGuideFromEntries(selected.map((item) => item.entry), searchType)
|
|
331
|
-
: undefined,
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
// ── Vector scorer ───────────────────────────────────────────────────────────
|
|
336
|
-
|
|
337
|
-
async function tryVecScores(
|
|
338
|
-
db: import("bun:sqlite").Database,
|
|
339
|
-
query: string,
|
|
340
|
-
k: number,
|
|
341
|
-
config: import("./config").AgentikitConfig,
|
|
342
|
-
): Promise<Map<number, number> | null> {
|
|
343
|
-
if (!config.semanticSearch || !isVecAvailable()) return null
|
|
344
|
-
const hasEmbeddings = getMeta(db, "hasEmbeddings")
|
|
345
|
-
if (hasEmbeddings !== "1") return null
|
|
346
|
-
|
|
347
|
-
try {
|
|
348
|
-
const { embed } = await import("./embedder.js")
|
|
349
|
-
const queryEmbedding = await embed(query, config.embedding)
|
|
350
|
-
const vecResults = searchVec(db, queryEmbedding, k)
|
|
351
|
-
|
|
352
|
-
const scores = new Map<number, number>()
|
|
353
|
-
for (const { id, distance } of vecResults) {
|
|
354
|
-
// Convert L2 distance to cosine similarity (vectors are normalized)
|
|
355
|
-
const cosineSim = 1 - (distance * distance) / 2
|
|
356
|
-
scores.set(id, Math.max(0, cosineSim))
|
|
357
|
-
}
|
|
358
|
-
return scores
|
|
359
|
-
} catch (error) {
|
|
360
|
-
console.warn("Vector search failed, skipping:", error instanceof Error ? error.message : String(error))
|
|
361
|
-
return null
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
// ── Substring fallback (no index) ───────────────────────────────────────────
|
|
366
|
-
|
|
367
|
-
function substringSearch(
|
|
368
|
-
query: string,
|
|
369
|
-
searchType: AgentikitSearchType,
|
|
370
|
-
limit: number,
|
|
371
|
-
stashDir: string,
|
|
372
|
-
sources: StashSource[],
|
|
373
|
-
): LocalSearchHit[] {
|
|
374
|
-
const assets = indexAssets(stashDir, searchType)
|
|
375
|
-
return assets
|
|
376
|
-
.filter((asset) => asset.name.toLowerCase().includes(query))
|
|
377
|
-
.sort(compareAssets)
|
|
378
|
-
.slice(0, limit)
|
|
379
|
-
.map((asset) => assetToSearchHit(asset, stashDir, sources))
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
// ── Hit building ────────────────────────────────────────────────────────────
|
|
383
|
-
|
|
384
|
-
function buildDbHit(input: {
|
|
385
|
-
entry: import("./metadata").StashEntry
|
|
386
|
-
path: string
|
|
387
|
-
score: number
|
|
388
|
-
query: string
|
|
389
|
-
rankingMode: "semantic" | "fts"
|
|
390
|
-
defaultStashDir: string
|
|
391
|
-
allStashDirs: string[]
|
|
392
|
-
sources: StashSource[]
|
|
393
|
-
includeItemUsage: boolean
|
|
394
|
-
}): LocalSearchHit {
|
|
395
|
-
const entryStashDir = findSourceForPath(input.path, input.sources)?.path ?? input.defaultStashDir
|
|
396
|
-
const typeRoot = path.join(entryStashDir, TYPE_DIRS[input.entry.type])
|
|
397
|
-
const openRefName = deriveCanonicalAssetName(input.entry.type, typeRoot, input.path)
|
|
398
|
-
?? input.entry.name
|
|
399
|
-
|
|
400
|
-
const qualityBoost = input.entry.generated === true ? 0 : 0.05
|
|
401
|
-
const confidenceBoost = typeof input.entry.confidence === "number" ? Math.min(0.05, Math.max(0, input.entry.confidence) * 0.05) : 0
|
|
402
|
-
const score = Math.min(Math.round((input.score + qualityBoost + confidenceBoost) * 1000) / 1000, 1.0)
|
|
403
|
-
|
|
404
|
-
const whyMatched = buildWhyMatched(input.entry, input.query, input.rankingMode, qualityBoost, confidenceBoost)
|
|
405
|
-
|
|
406
|
-
const source = findSourceForPath(input.path, input.sources)
|
|
407
|
-
|
|
408
|
-
const hit: LocalSearchHit = {
|
|
409
|
-
hitSource: "local",
|
|
410
|
-
type: input.entry.type,
|
|
411
|
-
name: input.entry.name,
|
|
412
|
-
path: input.path,
|
|
413
|
-
openRef: makeAssetRef(input.entry.type, openRefName, source?.registryId),
|
|
414
|
-
registryId: source?.registryId,
|
|
415
|
-
editable: source?.writable ?? false,
|
|
416
|
-
description: input.entry.description,
|
|
417
|
-
tags: input.entry.tags,
|
|
418
|
-
score,
|
|
419
|
-
whyMatched,
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
if (input.includeItemUsage && input.entry.usage && input.entry.usage.length > 0) {
|
|
423
|
-
hit.usage = input.entry.usage
|
|
424
|
-
}
|
|
425
|
-
|
|
426
|
-
const handler = tryGetHandler(input.entry.type)
|
|
427
|
-
if (handler?.enrichSearchHit) {
|
|
428
|
-
handler.enrichSearchHit(hit, entryStashDir)
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
return hit
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
function buildWhyMatched(
|
|
435
|
-
entry: import("./metadata").StashEntry,
|
|
436
|
-
query: string,
|
|
437
|
-
rankingMode: "semantic" | "fts",
|
|
438
|
-
qualityBoost: number,
|
|
439
|
-
confidenceBoost: number,
|
|
440
|
-
): string[] {
|
|
441
|
-
const reasons: string[] = [rankingMode === "semantic" ? "semantic similarity" : "fts bm25 relevance"]
|
|
442
|
-
const tokens = query.toLowerCase().split(/\s+/).filter(Boolean)
|
|
443
|
-
|
|
444
|
-
const name = entry.name.toLowerCase()
|
|
445
|
-
const tags = entry.tags?.join(" ").toLowerCase() ?? ""
|
|
446
|
-
const intents = entry.intents?.join(" ").toLowerCase() ?? ""
|
|
447
|
-
const aliases = entry.aliases?.join(" ").toLowerCase() ?? ""
|
|
448
|
-
|
|
449
|
-
if (tokens.some((t) => name.includes(t))) reasons.push("matched name tokens")
|
|
450
|
-
if (tokens.some((t) => tags.includes(t))) reasons.push("matched tags")
|
|
451
|
-
if (tokens.some((t) => intents.includes(t))) reasons.push("matched intents")
|
|
452
|
-
if (tokens.some((t) => aliases.includes(t))) reasons.push("matched aliases")
|
|
453
|
-
if (qualityBoost > 0) reasons.push("curated metadata boost")
|
|
454
|
-
if (confidenceBoost > 0) reasons.push("metadata confidence boost")
|
|
455
|
-
|
|
456
|
-
return reasons
|
|
457
|
-
}
|
|
458
|
-
|
|
459
|
-
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
460
|
-
|
|
461
|
-
function assetToSearchHit(asset: IndexedAsset, stashDir: string, sources: StashSource[]): LocalSearchHit {
|
|
462
|
-
const source = findSourceForPath(asset.path, sources)
|
|
463
|
-
const hit: LocalSearchHit = {
|
|
464
|
-
hitSource: "local",
|
|
465
|
-
type: asset.type,
|
|
466
|
-
name: asset.name,
|
|
467
|
-
path: asset.path,
|
|
468
|
-
openRef: makeAssetRef(asset.type, asset.name, source?.registryId),
|
|
469
|
-
registryId: source?.registryId,
|
|
470
|
-
editable: source?.writable ?? false,
|
|
471
|
-
}
|
|
472
|
-
const handler = tryGetHandler(asset.type)
|
|
473
|
-
if (handler?.enrichSearchHit) {
|
|
474
|
-
handler.enrichSearchHit(hit, stashDir)
|
|
475
|
-
}
|
|
476
|
-
return hit
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
function normalizeLimit(limit?: number): number {
|
|
480
|
-
if (typeof limit !== "number" || Number.isNaN(limit) || limit <= 0) {
|
|
481
|
-
return DEFAULT_LIMIT
|
|
482
|
-
}
|
|
483
|
-
return Math.min(Math.floor(limit), 200)
|
|
484
|
-
}
|
|
485
|
-
|
|
486
|
-
function parseSearchUsageMode(mode: SearchUsageMode | undefined): SearchUsageMode {
|
|
487
|
-
if (mode === "none" || mode === "both" || mode === "item" || mode === "guide") {
|
|
488
|
-
return mode
|
|
489
|
-
}
|
|
490
|
-
if (typeof mode === "undefined") return "both"
|
|
491
|
-
throw new Error(`Invalid usage mode: ${String(mode)}. Expected one of: none|both|item|guide`)
|
|
492
|
-
}
|
|
493
|
-
|
|
494
|
-
function parseSearchSource(source: SearchSource | undefined): SearchSource {
|
|
495
|
-
if (source === "local" || source === "registry" || source === "both") return source
|
|
496
|
-
if (typeof source === "undefined") return "local"
|
|
497
|
-
throw new Error(`Invalid search source: ${String(source)}. Expected one of: local|registry|both`)
|
|
498
|
-
}
|
|
499
|
-
|
|
500
|
-
function mergeSearchHits(localHits: LocalSearchHit[], registryHits: RegistrySearchResultHit[], limit: number): SearchHit[] {
|
|
501
|
-
const merged: SearchHit[] = []
|
|
502
|
-
let localIndex = 0
|
|
503
|
-
let registryIndex = 0
|
|
504
|
-
|
|
505
|
-
while (merged.length < limit && (localIndex < localHits.length || registryIndex < registryHits.length)) {
|
|
506
|
-
if (localIndex < localHits.length) {
|
|
507
|
-
merged.push(localHits[localIndex])
|
|
508
|
-
localIndex += 1
|
|
509
|
-
if (merged.length >= limit) break
|
|
510
|
-
}
|
|
511
|
-
if (registryIndex < registryHits.length) {
|
|
512
|
-
merged.push(registryHits[registryIndex])
|
|
513
|
-
registryIndex += 1
|
|
514
|
-
}
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
return merged
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
function shouldIncludeUsageGuide(mode: SearchUsageMode): boolean {
|
|
521
|
-
return mode === "both" || mode === "guide"
|
|
522
|
-
}
|
|
523
|
-
|
|
524
|
-
function shouldIncludeItemUsage(mode: SearchUsageMode): boolean {
|
|
525
|
-
return mode === "both" || mode === "item"
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
function buildUsageGuideFromEntries(
|
|
529
|
-
entries: import("./metadata").StashEntry[],
|
|
530
|
-
searchType: AgentikitSearchType,
|
|
531
|
-
): Partial<Record<AgentikitAssetType, string[]>> | undefined {
|
|
532
|
-
const types = entries.map((entry) => entry.type)
|
|
533
|
-
const fallbackGuide = buildUsageGuide(types, searchType)
|
|
534
|
-
const metadataByType = new Map<AgentikitAssetType, string[]>()
|
|
535
|
-
|
|
536
|
-
for (const entry of entries) {
|
|
537
|
-
if (!entry.usage || entry.usage.length === 0) continue
|
|
538
|
-
const current = metadataByType.get(entry.type) ?? []
|
|
539
|
-
for (const item of entry.usage) {
|
|
540
|
-
const trimmed = item.trim()
|
|
541
|
-
if (trimmed && !current.includes(trimmed)) current.push(trimmed)
|
|
542
|
-
}
|
|
543
|
-
if (current.length > 0) metadataByType.set(entry.type, current)
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
if (!fallbackGuide && metadataByType.size === 0) return undefined
|
|
547
|
-
|
|
548
|
-
const result: Partial<Record<AgentikitAssetType, string[]>> = {}
|
|
549
|
-
for (const assetType of resolveGuideTypes(types, searchType)) {
|
|
550
|
-
const lines: string[] = []
|
|
551
|
-
const metadataLines = metadataByType.get(assetType)
|
|
552
|
-
if (metadataLines && metadataLines.length > 0) {
|
|
553
|
-
lines.push(...metadataLines)
|
|
554
|
-
}
|
|
555
|
-
const fallbackLines = fallbackGuide?.[assetType]
|
|
556
|
-
if (fallbackLines && fallbackLines.length > 0) {
|
|
557
|
-
for (const line of fallbackLines) {
|
|
558
|
-
if (!lines.includes(line)) lines.push(line)
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
if (lines.length > 0) result[assetType] = lines
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
return Object.keys(result).length > 0 ? result : undefined
|
|
565
|
-
}
|
|
566
|
-
|
|
567
|
-
function buildUsageGuide(
|
|
568
|
-
hitTypes: AgentikitAssetType[],
|
|
569
|
-
searchType: AgentikitSearchType,
|
|
570
|
-
): Partial<Record<AgentikitAssetType, string[]>> | undefined {
|
|
571
|
-
const result: Partial<Record<AgentikitAssetType, string[]>> = {}
|
|
572
|
-
for (const assetType of resolveGuideTypes(hitTypes, searchType)) {
|
|
573
|
-
result[assetType] = usageGuideByType(assetType)
|
|
574
|
-
}
|
|
575
|
-
return Object.keys(result).length > 0 ? result : undefined
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
function resolveGuideTypes(hitTypes: AgentikitAssetType[], searchType: AgentikitSearchType): AgentikitAssetType[] {
|
|
579
|
-
if (searchType !== "any") return [searchType]
|
|
580
|
-
return Array.from(new Set(hitTypes))
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
function usageGuideByType(type: AgentikitAssetType): string[] {
|
|
584
|
-
const handler = tryGetHandler(type)
|
|
585
|
-
return handler?.defaultUsageGuide ?? []
|
|
586
|
-
}
|
|
587
|
-
|
|
588
|
-
function fileToAsset(assetType: AgentikitAssetType, root: string, file: string): IndexedAsset | undefined {
|
|
589
|
-
const name = deriveCanonicalAssetName(assetType, root, file)
|
|
590
|
-
if (!name) return undefined
|
|
591
|
-
return { type: assetType, name, path: file }
|
|
592
|
-
}
|
|
593
|
-
|
|
594
|
-
function indexAssets(stashDir: string, type: AgentikitSearchType): IndexedAsset[] {
|
|
595
|
-
const assets: IndexedAsset[] = []
|
|
596
|
-
const types = type === "any" ? ASSET_TYPES : [type]
|
|
597
|
-
for (const assetType of types) {
|
|
598
|
-
const root = path.join(stashDir, TYPE_DIRS[assetType])
|
|
599
|
-
const groups = walkStash(root, assetType)
|
|
600
|
-
for (const { files } of groups) {
|
|
601
|
-
for (const file of files) {
|
|
602
|
-
const asset = fileToAsset(assetType, root, file)
|
|
603
|
-
if (asset) assets.push(asset)
|
|
604
|
-
}
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
return assets
|
|
608
|
-
}
|
|
609
|
-
|
|
610
|
-
function compareAssets(a: IndexedAsset, b: IndexedAsset): number {
|
|
611
|
-
if (a.type !== b.type) return a.type.localeCompare(b.type)
|
|
612
|
-
return a.name.localeCompare(b.name)
|
|
613
|
-
}
|
package/src/stash-show.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
import fs from "node:fs"
|
|
2
|
-
import { parseAssetRef } from "./stash-ref"
|
|
3
|
-
import { resolveSourcesForOrigin } from "./origin-resolve"
|
|
4
|
-
import { resolveAssetPath } from "./stash-resolve"
|
|
5
|
-
import type { KnowledgeView, ShowResponse } from "./stash-types"
|
|
6
|
-
import { getHandler } from "./asset-type-handler"
|
|
7
|
-
import { resolveStashSources, findSourceForPath } from "./stash-source"
|
|
8
|
-
|
|
9
|
-
export async function agentikitShow(input: { ref: string; view?: KnowledgeView }): Promise<ShowResponse> {
|
|
10
|
-
const parsed = parseAssetRef(input.ref)
|
|
11
|
-
const allSources = resolveStashSources()
|
|
12
|
-
const searchSources = resolveSourcesForOrigin(parsed.origin, allSources)
|
|
13
|
-
|
|
14
|
-
const allStashDirs = searchSources.map((s) => s.path)
|
|
15
|
-
|
|
16
|
-
let assetPath: string | undefined
|
|
17
|
-
let lastError: Error | undefined
|
|
18
|
-
for (const dir of allStashDirs) {
|
|
19
|
-
try {
|
|
20
|
-
assetPath = resolveAssetPath(dir, parsed.type, parsed.name)
|
|
21
|
-
break
|
|
22
|
-
} catch (err) {
|
|
23
|
-
lastError = err instanceof Error ? err : new Error(String(err))
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
if (!assetPath && parsed.origin && searchSources.length === 0) {
|
|
28
|
-
const installCmd = `akm add ${parsed.origin}`
|
|
29
|
-
throw new Error(
|
|
30
|
-
`Stash asset not found for ref: ${parsed.type}:${parsed.name}. ` +
|
|
31
|
-
`Kit "${parsed.origin}" is not installed. Run: ${installCmd}`
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
if (!assetPath) {
|
|
36
|
-
throw lastError ?? new Error(`Stash asset not found for ref: ${parsed.type}:${parsed.name}`)
|
|
37
|
-
}
|
|
38
|
-
const content = fs.readFileSync(assetPath, "utf8")
|
|
39
|
-
|
|
40
|
-
const source = findSourceForPath(assetPath, allSources)
|
|
41
|
-
const handler = getHandler(parsed.type)
|
|
42
|
-
const response = handler.buildShowResponse({
|
|
43
|
-
name: parsed.name,
|
|
44
|
-
path: assetPath,
|
|
45
|
-
content,
|
|
46
|
-
view: input.view,
|
|
47
|
-
stashDirs: allStashDirs,
|
|
48
|
-
})
|
|
49
|
-
|
|
50
|
-
return {
|
|
51
|
-
...response,
|
|
52
|
-
registryId: source?.registryId,
|
|
53
|
-
editable: source?.writable ?? false,
|
|
54
|
-
}
|
|
55
|
-
}
|