agentikit 0.0.13 → 0.0.14

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.
Files changed (145) hide show
  1. package/LICENSE +385 -0
  2. package/README.md +180 -110
  3. package/dist/cli.js +671 -0
  4. package/dist/common.js +192 -0
  5. package/dist/{src/config-cli.js → config-cli.js} +14 -6
  6. package/dist/{src/config.js → config.js} +92 -24
  7. package/dist/{src/db.js → db.js} +109 -35
  8. package/dist/{src/embedder.js → embedder.js} +57 -2
  9. package/dist/file-context.js +158 -0
  10. package/dist/{src/handlers → handlers}/command-handler.js +2 -0
  11. package/dist/{src/handlers → handlers}/index.js +0 -6
  12. package/dist/{src/indexer.js → indexer.js} +34 -10
  13. package/dist/init.js +43 -0
  14. package/dist/lockfile.js +55 -0
  15. package/dist/matchers.js +157 -0
  16. package/dist/{src/metadata.js → metadata.js} +12 -1
  17. package/dist/{src/origin-resolve.js → origin-resolve.js} +10 -9
  18. package/dist/paths.js +82 -0
  19. package/dist/{src/registry-install.js → registry-install.js} +145 -17
  20. package/dist/{src/registry-resolve.js → registry-resolve.js} +178 -18
  21. package/dist/{src/registry-search.js → registry-search.js} +8 -16
  22. package/dist/renderers.js +276 -0
  23. package/dist/{src/ripgrep-install.js → ripgrep-install.js} +5 -5
  24. package/dist/{src/ripgrep-resolve.js → ripgrep-resolve.js} +21 -11
  25. package/dist/self-update.js +220 -0
  26. package/dist/{src/stash-add.js → stash-add.js} +11 -2
  27. package/dist/stash-clone.js +115 -0
  28. package/dist/{src/stash-registry.js → stash-registry.js} +15 -41
  29. package/dist/{src/stash-search.js → stash-search.js} +67 -55
  30. package/dist/{src/stash-show.js → stash-show.js} +30 -3
  31. package/dist/{src/stash-source.js → stash-source.js} +56 -9
  32. package/dist/submit.js +552 -0
  33. package/dist/{src/walker.js → walker.js} +38 -0
  34. package/package.json +7 -16
  35. package/dist/index.d.ts +0 -28
  36. package/dist/index.js +0 -15
  37. package/dist/src/asset-spec.d.ts +0 -16
  38. package/dist/src/asset-type-handler.d.ts +0 -27
  39. package/dist/src/cli.d.ts +0 -2
  40. package/dist/src/cli.js +0 -399
  41. package/dist/src/common.d.ts +0 -13
  42. package/dist/src/common.js +0 -60
  43. package/dist/src/config-cli.d.ts +0 -9
  44. package/dist/src/config.d.ts +0 -50
  45. package/dist/src/db.d.ts +0 -46
  46. package/dist/src/embedder.d.ts +0 -10
  47. package/dist/src/frontmatter.d.ts +0 -30
  48. package/dist/src/github.d.ts +0 -4
  49. package/dist/src/handlers/agent-handler.d.ts +0 -2
  50. package/dist/src/handlers/command-handler.d.ts +0 -2
  51. package/dist/src/handlers/index.d.ts +0 -6
  52. package/dist/src/handlers/knowledge-handler.d.ts +0 -2
  53. package/dist/src/handlers/markdown-helpers.d.ts +0 -7
  54. package/dist/src/handlers/script-handler.d.ts +0 -2
  55. package/dist/src/handlers/skill-handler.d.ts +0 -2
  56. package/dist/src/handlers/tool-handler.d.ts +0 -2
  57. package/dist/src/indexer.d.ts +0 -22
  58. package/dist/src/init.d.ts +0 -19
  59. package/dist/src/init.js +0 -99
  60. package/dist/src/llm.d.ts +0 -15
  61. package/dist/src/markdown.d.ts +0 -18
  62. package/dist/src/metadata.d.ts +0 -41
  63. package/dist/src/origin-resolve.d.ts +0 -19
  64. package/dist/src/registry-install.d.ts +0 -11
  65. package/dist/src/registry-resolve.d.ts +0 -3
  66. package/dist/src/registry-search.d.ts +0 -27
  67. package/dist/src/registry-types.d.ts +0 -62
  68. package/dist/src/ripgrep-install.d.ts +0 -12
  69. package/dist/src/ripgrep-resolve.d.ts +0 -13
  70. package/dist/src/ripgrep.d.ts +0 -3
  71. package/dist/src/stash-add.d.ts +0 -4
  72. package/dist/src/stash-clone.d.ts +0 -22
  73. package/dist/src/stash-clone.js +0 -83
  74. package/dist/src/stash-ref.d.ts +0 -31
  75. package/dist/src/stash-registry.d.ts +0 -18
  76. package/dist/src/stash-resolve.d.ts +0 -2
  77. package/dist/src/stash-search.d.ts +0 -8
  78. package/dist/src/stash-show.d.ts +0 -5
  79. package/dist/src/stash-source.d.ts +0 -24
  80. package/dist/src/stash-types.d.ts +0 -227
  81. package/dist/src/stash.d.ts +0 -16
  82. package/dist/src/stash.js +0 -9
  83. package/dist/src/tool-runner.d.ts +0 -35
  84. package/dist/src/walker.d.ts +0 -19
  85. package/src/asset-spec.ts +0 -85
  86. package/src/asset-type-handler.ts +0 -77
  87. package/src/cli.ts +0 -427
  88. package/src/common.ts +0 -76
  89. package/src/config-cli.ts +0 -499
  90. package/src/config.ts +0 -305
  91. package/src/db.ts +0 -411
  92. package/src/embedder.ts +0 -128
  93. package/src/frontmatter.ts +0 -95
  94. package/src/github.ts +0 -21
  95. package/src/handlers/agent-handler.ts +0 -32
  96. package/src/handlers/command-handler.ts +0 -29
  97. package/src/handlers/index.ts +0 -25
  98. package/src/handlers/knowledge-handler.ts +0 -62
  99. package/src/handlers/markdown-helpers.ts +0 -19
  100. package/src/handlers/script-handler.ts +0 -92
  101. package/src/handlers/skill-handler.ts +0 -37
  102. package/src/handlers/tool-handler.ts +0 -71
  103. package/src/indexer.ts +0 -392
  104. package/src/init.ts +0 -114
  105. package/src/llm.ts +0 -125
  106. package/src/markdown.ts +0 -106
  107. package/src/metadata.ts +0 -333
  108. package/src/origin-resolve.ts +0 -67
  109. package/src/registry-install.ts +0 -361
  110. package/src/registry-resolve.ts +0 -341
  111. package/src/registry-search.ts +0 -335
  112. package/src/registry-types.ts +0 -72
  113. package/src/ripgrep-install.ts +0 -200
  114. package/src/ripgrep-resolve.ts +0 -72
  115. package/src/ripgrep.ts +0 -3
  116. package/src/stash-add.ts +0 -63
  117. package/src/stash-clone.ts +0 -127
  118. package/src/stash-ref.ts +0 -99
  119. package/src/stash-registry.ts +0 -259
  120. package/src/stash-resolve.ts +0 -50
  121. package/src/stash-search.ts +0 -613
  122. package/src/stash-show.ts +0 -55
  123. package/src/stash-source.ts +0 -103
  124. package/src/stash-types.ts +0 -231
  125. package/src/stash.ts +0 -39
  126. package/src/tool-runner.ts +0 -142
  127. package/src/walker.ts +0 -53
  128. /package/dist/{src/asset-spec.js → asset-spec.js} +0 -0
  129. /package/dist/{src/asset-type-handler.js → asset-type-handler.js} +0 -0
  130. /package/dist/{src/frontmatter.js → frontmatter.js} +0 -0
  131. /package/dist/{src/github.js → github.js} +0 -0
  132. /package/dist/{src/handlers → handlers}/agent-handler.js +0 -0
  133. /package/dist/{src/handlers → handlers}/knowledge-handler.js +0 -0
  134. /package/dist/{src/handlers → handlers}/markdown-helpers.js +0 -0
  135. /package/dist/{src/handlers → handlers}/script-handler.js +0 -0
  136. /package/dist/{src/handlers → handlers}/skill-handler.js +0 -0
  137. /package/dist/{src/handlers → handlers}/tool-handler.js +0 -0
  138. /package/dist/{src/llm.js → llm.js} +0 -0
  139. /package/dist/{src/markdown.js → markdown.js} +0 -0
  140. /package/dist/{src/registry-types.js → registry-types.js} +0 -0
  141. /package/dist/{src/ripgrep.js → ripgrep.js} +0 -0
  142. /package/dist/{src/stash-ref.js → stash-ref.js} +0 -0
  143. /package/dist/{src/stash-resolve.js → stash-resolve.js} +0 -0
  144. /package/dist/{src/stash-types.js → stash-types.js} +0 -0
  145. /package/dist/{src/tool-runner.js → tool-runner.js} +0 -0
@@ -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
- }