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
package/src/db.ts DELETED
@@ -1,411 +0,0 @@
1
- import fs from "node:fs"
2
- import path from "node:path"
3
- import { Database } from "bun:sqlite"
4
- import type { StashEntry } from "./metadata"
5
- import type { EmbeddingVector } from "./embedder"
6
-
7
- // ── Types ───────────────────────────────────────────────────────────────────
8
-
9
- export interface DbIndexedEntry {
10
- id: number
11
- entryKey: string
12
- dirPath: string
13
- filePath: string
14
- stashDir: string
15
- entry: StashEntry
16
- searchText: string
17
- }
18
-
19
- export interface DbSearchResult {
20
- id: number
21
- filePath: string
22
- entry: StashEntry
23
- searchText: string
24
- bm25Score: number
25
- }
26
-
27
- export interface DbVecResult {
28
- id: number
29
- distance: number
30
- }
31
-
32
- // ── Constants ───────────────────────────────────────────────────────────────
33
-
34
- export const DB_VERSION = 5
35
- export const EMBEDDING_DIM = 384
36
-
37
- // ── Path ────────────────────────────────────────────────────────────────────
38
-
39
- export function getDbPath(): string {
40
- const cacheDir =
41
- process.env.XDG_CACHE_HOME ||
42
- path.join(process.env.HOME || process.env.USERPROFILE || "", ".cache")
43
- return path.join(cacheDir, "agentikit", "index.db")
44
- }
45
-
46
- // ── Database lifecycle ──────────────────────────────────────────────────────
47
-
48
- export function openDatabase(dbPath?: string, options?: { embeddingDim?: number }): Database {
49
- const resolvedPath = dbPath ?? getDbPath()
50
- const dir = path.dirname(resolvedPath)
51
- if (!fs.existsSync(dir)) {
52
- fs.mkdirSync(dir, { recursive: true })
53
- }
54
-
55
- const db = new Database(resolvedPath)
56
- db.exec("PRAGMA journal_mode = WAL")
57
- db.exec("PRAGMA foreign_keys = ON")
58
-
59
- // Try to load sqlite-vec extension
60
- loadVecExtension(db)
61
-
62
- ensureSchema(db, options?.embeddingDim ?? EMBEDDING_DIM)
63
- return db
64
- }
65
-
66
- export function closeDatabase(db: Database): void {
67
- db.close()
68
- }
69
-
70
- // ── sqlite-vec extension ────────────────────────────────────────────────────
71
-
72
- let vecAvailable = false
73
-
74
- function loadVecExtension(db: Database): void {
75
- try {
76
- const sqliteVec = require("sqlite-vec")
77
- sqliteVec.load(db)
78
- vecAvailable = true
79
- } catch {
80
- console.warn("sqlite-vec extension not available, embeddings will be skipped")
81
- vecAvailable = false
82
- }
83
- }
84
-
85
- export function isVecAvailable(): boolean {
86
- return vecAvailable
87
- }
88
-
89
- // ── Schema ──────────────────────────────────────────────────────────────────
90
-
91
- function ensureSchema(db: Database, embeddingDim: number): void {
92
- // Create meta table first so we can check version
93
- db.exec(`
94
- CREATE TABLE IF NOT EXISTS index_meta (
95
- key TEXT PRIMARY KEY,
96
- value TEXT NOT NULL
97
- );
98
- `)
99
-
100
- // Check stored version — if it differs from DB_VERSION, drop and recreate all tables
101
- const storedVersion = getMeta(db, "version")
102
- if (storedVersion && storedVersion !== String(DB_VERSION)) {
103
- db.exec("DROP TABLE IF EXISTS entries_vec")
104
- db.exec("DROP TABLE IF EXISTS entries_fts")
105
- db.exec("DROP INDEX IF EXISTS idx_entries_dir")
106
- db.exec("DROP INDEX IF EXISTS idx_entries_type")
107
- db.exec("DROP TABLE IF EXISTS entries")
108
- db.exec("DELETE FROM index_meta")
109
- }
110
-
111
- db.exec(`
112
- CREATE TABLE IF NOT EXISTS entries (
113
- id INTEGER PRIMARY KEY AUTOINCREMENT,
114
- entry_key TEXT NOT NULL UNIQUE,
115
- dir_path TEXT NOT NULL,
116
- file_path TEXT NOT NULL,
117
- stash_dir TEXT NOT NULL,
118
- entry_json TEXT NOT NULL,
119
- search_text TEXT NOT NULL,
120
- entry_type TEXT NOT NULL
121
- );
122
-
123
- CREATE INDEX IF NOT EXISTS idx_entries_dir ON entries(dir_path);
124
- CREATE INDEX IF NOT EXISTS idx_entries_type ON entries(entry_type);
125
- `)
126
-
127
- // FTS5 table — standalone with explicit entry_id for joining
128
- const ftsExists = db
129
- .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='entries_fts'")
130
- .get()
131
- if (!ftsExists) {
132
- db.exec(`
133
- CREATE VIRTUAL TABLE entries_fts USING fts5(
134
- entry_id UNINDEXED,
135
- search_text,
136
- tokenize='porter unicode61'
137
- );
138
- `)
139
- }
140
-
141
- // sqlite-vec table
142
- if (vecAvailable) {
143
- // Check if stored embedding dimension differs from configured one
144
- const storedDim = getMeta(db, "embeddingDim")
145
- if (storedDim && storedDim !== String(embeddingDim)) {
146
- try { db.exec("DROP TABLE IF EXISTS entries_vec") } catch { /* ignore */ }
147
- }
148
-
149
- const vecExists = db
150
- .prepare("SELECT name FROM sqlite_master WHERE type='table' AND name='entries_vec'")
151
- .get()
152
- if (!vecExists) {
153
- db.exec(`
154
- CREATE VIRTUAL TABLE entries_vec USING vec0(
155
- id INTEGER PRIMARY KEY,
156
- embedding FLOAT[${embeddingDim}]
157
- );
158
- `)
159
- }
160
- setMeta(db, "embeddingDim", String(embeddingDim))
161
- }
162
-
163
- // Set version if not present
164
- const version = getMeta(db, "version")
165
- if (!version) {
166
- setMeta(db, "version", String(DB_VERSION))
167
- }
168
- }
169
-
170
- // ── Meta helpers ────────────────────────────────────────────────────────────
171
-
172
- export function getMeta(db: Database, key: string): string | undefined {
173
- const row = db.prepare("SELECT value FROM index_meta WHERE key = ?").get(key) as
174
- | { value: string }
175
- | undefined
176
- return row?.value
177
- }
178
-
179
- export function setMeta(db: Database, key: string, value: string): void {
180
- db.prepare("INSERT OR REPLACE INTO index_meta (key, value) VALUES (?, ?)").run(key, value)
181
- }
182
-
183
- // ── Entry operations ────────────────────────────────────────────────────────
184
-
185
- export function upsertEntry(
186
- db: Database,
187
- entryKey: string,
188
- dirPath: string,
189
- filePath: string,
190
- stashDir: string,
191
- entry: StashEntry,
192
- searchText: string,
193
- ): number {
194
- const stmt = db.prepare(`
195
- INSERT INTO entries (entry_key, dir_path, file_path, stash_dir, entry_json, search_text, entry_type)
196
- VALUES (?, ?, ?, ?, ?, ?, ?)
197
- ON CONFLICT(entry_key) DO UPDATE SET
198
- dir_path = excluded.dir_path,
199
- file_path = excluded.file_path,
200
- stash_dir = excluded.stash_dir,
201
- entry_json = excluded.entry_json,
202
- search_text = excluded.search_text,
203
- entry_type = excluded.entry_type
204
- `)
205
- stmt.run(entryKey, dirPath, filePath, stashDir, JSON.stringify(entry), searchText, entry.type)
206
- // Fetch the row id explicitly since last_insert_rowid() is unreliable for ON CONFLICT DO UPDATE
207
- const row = db.prepare("SELECT id FROM entries WHERE entry_key = ?").get(entryKey) as { id: number }
208
- return row.id
209
- }
210
-
211
- export function deleteEntriesByDir(db: Database, dirPath: string): void {
212
- if (vecAvailable) {
213
- const ids = db
214
- .prepare("SELECT id FROM entries WHERE dir_path = ?")
215
- .all(dirPath) as Array<{ id: number }>
216
- for (const { id } of ids) {
217
- try {
218
- db.prepare("DELETE FROM entries_vec WHERE id = ?").run(id)
219
- } catch { /* ignore if vec table missing */ }
220
- }
221
- }
222
- db.prepare("DELETE FROM entries WHERE dir_path = ?").run(dirPath)
223
- }
224
-
225
- export function rebuildFts(db: Database): void {
226
- db.exec("DELETE FROM entries_fts")
227
- db.exec("INSERT INTO entries_fts (entry_id, search_text) SELECT CAST(id AS TEXT), search_text FROM entries")
228
- }
229
-
230
- // ── Vector operations ───────────────────────────────────────────────────────
231
-
232
- export function upsertEmbedding(
233
- db: Database,
234
- entryId: number,
235
- embedding: EmbeddingVector,
236
- ): void {
237
- if (!vecAvailable) return
238
- const buf = float32Buffer(embedding)
239
- try {
240
- db.prepare("DELETE FROM entries_vec WHERE id = ?").run(entryId)
241
- } catch { /* ignore */ }
242
- db.prepare("INSERT INTO entries_vec (id, embedding) VALUES (?, ?)").run(entryId, buf)
243
- }
244
-
245
- export function searchVec(
246
- db: Database,
247
- queryEmbedding: EmbeddingVector,
248
- k: number,
249
- ): DbVecResult[] {
250
- if (!vecAvailable) return []
251
- const buf = float32Buffer(queryEmbedding)
252
- try {
253
- return db
254
- .prepare("SELECT id, distance FROM entries_vec WHERE embedding MATCH ? AND k = ?")
255
- .all(buf, k) as DbVecResult[]
256
- } catch {
257
- return []
258
- }
259
- }
260
-
261
- function float32Buffer(vec: number[]): Buffer {
262
- const f32 = new Float32Array(vec)
263
- return Buffer.from(f32.buffer)
264
- }
265
-
266
- // ── FTS5 search ─────────────────────────────────────────────────────────────
267
-
268
- export function searchFts(
269
- db: Database,
270
- query: string,
271
- limit: number,
272
- entryType?: string,
273
- ): DbSearchResult[] {
274
- const ftsQuery = sanitizeFtsQuery(query)
275
- if (!ftsQuery) return []
276
-
277
- let sql: string
278
- let params: unknown[]
279
-
280
- if (entryType && entryType !== "any") {
281
- sql = `
282
- SELECT e.id, e.file_path AS filePath, e.entry_json, e.search_text AS searchText,
283
- bm25(entries_fts) AS bm25Score
284
- FROM entries_fts f
285
- JOIN entries e ON e.id = CAST(f.entry_id AS INTEGER)
286
- WHERE entries_fts MATCH ?
287
- AND e.entry_type = ?
288
- ORDER BY bm25Score
289
- LIMIT ?
290
- `
291
- params = [ftsQuery, entryType, limit]
292
- } else {
293
- sql = `
294
- SELECT e.id, e.file_path AS filePath, e.entry_json, e.search_text AS searchText,
295
- bm25(entries_fts) AS bm25Score
296
- FROM entries_fts f
297
- JOIN entries e ON e.id = CAST(f.entry_id AS INTEGER)
298
- WHERE entries_fts MATCH ?
299
- ORDER BY bm25Score
300
- LIMIT ?
301
- `
302
- params = [ftsQuery, limit]
303
- }
304
-
305
- try {
306
- const rows = db.prepare(sql).all(...(params as import("bun:sqlite").SQLQueryBindings[])) as Array<{
307
- id: number
308
- filePath: string
309
- entry_json: string
310
- searchText: string
311
- bm25Score: number
312
- }>
313
-
314
- return rows.map((row) => ({
315
- id: row.id,
316
- filePath: row.filePath,
317
- entry: JSON.parse(row.entry_json) as StashEntry,
318
- searchText: row.searchText,
319
- bm25Score: row.bm25Score,
320
- }))
321
- } catch {
322
- return []
323
- }
324
- }
325
-
326
- function sanitizeFtsQuery(query: string): string {
327
- const tokens = query
328
- .replace(/[^a-zA-Z0-9\s]/g, " ")
329
- .split(/\s+/)
330
- .filter((t) => t.length > 1)
331
- if (tokens.length === 0) return ""
332
- // Use unquoted tokens so the porter stemmer can normalize word forms
333
- return tokens.join(" OR ")
334
- }
335
-
336
- // ── All entries ─────────────────────────────────────────────────────────────
337
-
338
- export function getAllEntries(
339
- db: Database,
340
- entryType?: string,
341
- ): DbIndexedEntry[] {
342
- let sql: string
343
- let params: unknown[]
344
-
345
- if (entryType && entryType !== "any") {
346
- sql = "SELECT id, entry_key, dir_path, file_path, stash_dir, entry_json, search_text FROM entries WHERE entry_type = ?"
347
- params = [entryType]
348
- } else {
349
- sql = "SELECT id, entry_key, dir_path, file_path, stash_dir, entry_json, search_text FROM entries"
350
- params = []
351
- }
352
-
353
- const rows = db.prepare(sql).all(...(params as import("bun:sqlite").SQLQueryBindings[])) as Array<{
354
- id: number
355
- entry_key: string
356
- dir_path: string
357
- file_path: string
358
- stash_dir: string
359
- entry_json: string
360
- search_text: string
361
- }>
362
-
363
- return rows.map((row) => ({
364
- id: row.id,
365
- entryKey: row.entry_key,
366
- dirPath: row.dir_path,
367
- filePath: row.file_path,
368
- stashDir: row.stash_dir,
369
- entry: JSON.parse(row.entry_json) as StashEntry,
370
- searchText: row.search_text,
371
- }))
372
- }
373
-
374
- export function getEntryCount(db: Database): number {
375
- const row = db.prepare("SELECT COUNT(*) AS cnt FROM entries").get() as { cnt: number }
376
- return row.cnt
377
- }
378
-
379
- export function getEntryById(db: Database, id: number): { filePath: string; entry: StashEntry } | undefined {
380
- const row = db
381
- .prepare("SELECT file_path, entry_json FROM entries WHERE id = ?")
382
- .get(id) as { file_path: string; entry_json: string } | undefined
383
- if (!row) return undefined
384
- return { filePath: row.file_path, entry: JSON.parse(row.entry_json) as StashEntry }
385
- }
386
-
387
- export function getEntriesByDir(db: Database, dirPath: string): DbIndexedEntry[] {
388
- const rows = db
389
- .prepare(
390
- "SELECT id, entry_key, dir_path, file_path, stash_dir, entry_json, search_text FROM entries WHERE dir_path = ?",
391
- )
392
- .all(dirPath) as Array<{
393
- id: number
394
- entry_key: string
395
- dir_path: string
396
- file_path: string
397
- stash_dir: string
398
- entry_json: string
399
- search_text: string
400
- }>
401
-
402
- return rows.map((row) => ({
403
- id: row.id,
404
- entryKey: row.entry_key,
405
- dirPath: row.dir_path,
406
- filePath: row.file_path,
407
- stashDir: row.stash_dir,
408
- entry: JSON.parse(row.entry_json) as StashEntry,
409
- searchText: row.search_text,
410
- }))
411
- }
package/src/embedder.ts DELETED
@@ -1,128 +0,0 @@
1
- import type { EmbeddingConnectionConfig } from "./config"
2
- import { fetchWithTimeout } from "./common"
3
-
4
- // ── Types ───────────────────────────────────────────────────────────────────
5
-
6
- export type EmbeddingVector = number[]
7
-
8
- // ── Singleton local embedder ────────────────────────────────────────────────
9
-
10
- interface TransformerPipeline {
11
- (text: string, options: { pooling: string; normalize: boolean }): Promise<{ data: Float32Array }>
12
- }
13
-
14
- let localEmbedder: TransformerPipeline | undefined
15
-
16
- async function getLocalEmbedder(): Promise<TransformerPipeline> {
17
- if (!localEmbedder) {
18
- let pipeline: unknown
19
- try {
20
- const mod = await import("@xenova/transformers")
21
- pipeline = mod.pipeline as unknown
22
- } catch {
23
- throw new Error(
24
- "Semantic search requires @xenova/transformers. Install it with: npm install @xenova/transformers",
25
- )
26
- }
27
- const pipelineFn = pipeline as (task: string, model: string) => Promise<TransformerPipeline>
28
- localEmbedder = await pipelineFn("feature-extraction", "Xenova/all-MiniLM-L6-v2")
29
- }
30
- return localEmbedder!
31
- }
32
-
33
- async function embedLocal(text: string): Promise<EmbeddingVector> {
34
- const model = await getLocalEmbedder()
35
- const result = await model(text, { pooling: "mean", normalize: true })
36
- return Array.from(result.data) as number[]
37
- }
38
-
39
- // ── OpenAI-compatible remote embedder ───────────────────────────────────────
40
-
41
- async function embedRemote(
42
- text: string,
43
- config: EmbeddingConnectionConfig,
44
- ): Promise<EmbeddingVector> {
45
- const headers: Record<string, string> = { "Content-Type": "application/json" }
46
- if (config.apiKey) {
47
- headers["Authorization"] = `Bearer ${config.apiKey}`
48
- }
49
-
50
- const body: { input: string; model: string; dimensions?: number } = {
51
- input: text,
52
- model: config.model,
53
- }
54
- if (config.dimension) {
55
- body.dimensions = config.dimension
56
- }
57
-
58
- const response = await fetchWithTimeout(config.endpoint, {
59
- method: "POST",
60
- headers,
61
- body: JSON.stringify(body),
62
- })
63
-
64
- if (!response.ok) {
65
- const body = await response.text().catch(() => "")
66
- throw new Error(`Embedding request failed (${response.status}): ${body}`)
67
- }
68
-
69
- const json = (await response.json()) as {
70
- data: Array<{ embedding: number[] }>
71
- }
72
-
73
- if (!json.data?.[0]?.embedding) {
74
- throw new Error("Unexpected embedding response format: missing data[0].embedding")
75
- }
76
-
77
- return json.data[0].embedding
78
- }
79
-
80
- // ── Public API ──────────────────────────────────────────────────────────────
81
-
82
- /**
83
- * Generate an embedding for the given text.
84
- * If embeddingConfig is provided, uses the configured OpenAI-compatible endpoint.
85
- * Otherwise falls back to local @xenova/transformers.
86
- */
87
- export async function embed(
88
- text: string,
89
- embeddingConfig?: EmbeddingConnectionConfig,
90
- ): Promise<EmbeddingVector> {
91
- if (embeddingConfig) {
92
- return embedRemote(text, embeddingConfig)
93
- }
94
- return embedLocal(text)
95
- }
96
-
97
- // ── Similarity ──────────────────────────────────────────────────────────────
98
-
99
- export function cosineSimilarity(a: EmbeddingVector, b: EmbeddingVector): number {
100
- const len = Math.min(a.length, b.length)
101
- if (len === 0) return 0
102
- let dot = 0
103
- for (let i = 0; i < len; i++) {
104
- dot += a[i] * b[i]
105
- }
106
- return dot
107
- }
108
-
109
- // ── Availability check ──────────────────────────────────────────────────────
110
-
111
- export async function isEmbeddingAvailable(
112
- embeddingConfig?: EmbeddingConnectionConfig,
113
- ): Promise<boolean> {
114
- if (embeddingConfig) {
115
- try {
116
- await embedRemote("test", embeddingConfig)
117
- return true
118
- } catch {
119
- return false
120
- }
121
- }
122
- try {
123
- await getLocalEmbedder()
124
- return true
125
- } catch {
126
- return false
127
- }
128
- }
@@ -1,95 +0,0 @@
1
- /**
2
- * Shared frontmatter parsing utilities.
3
- *
4
- * Provides a single, canonical YAML-subset frontmatter parser used by both
5
- * the stash open logic and the metadata generator.
6
- */
7
-
8
- /**
9
- * Parse YAML-subset frontmatter from a Markdown (or similar) string.
10
- *
11
- * Returns the parsed key-value data and the remaining body content.
12
- */
13
- export function parseFrontmatter(raw: string): {
14
- data: Record<string, unknown>
15
- content: string
16
- frontmatter: string | null
17
- bodyStartLine: number
18
- } {
19
- const parsedBlock = parseFrontmatterBlock(raw)
20
- if (!parsedBlock) {
21
- return { data: {}, content: raw, frontmatter: null, bodyStartLine: 1 }
22
- }
23
-
24
- const data: Record<string, unknown> = {}
25
- let currentKey: string | null = null
26
- let nested: Record<string, unknown> | null = null
27
-
28
- for (const line of parsedBlock.frontmatter.split(/\r?\n/)) {
29
- const indented = line.match(/^ (\w[\w-]*):\s*(.+)$/)
30
- if (indented && currentKey && nested) {
31
- nested[indented[1]] = parseYamlScalar(indented[2].trim())
32
- continue
33
- }
34
-
35
- const top = line.match(/^(\w[\w-]*):\s*(.*)$/)
36
- if (!top) {
37
- continue
38
- }
39
-
40
- currentKey = top[1]
41
- const value = top[2].trim()
42
- if (value === "") {
43
- nested = {}
44
- data[currentKey] = nested
45
- } else {
46
- nested = null
47
- data[currentKey] = parseYamlScalar(value)
48
- }
49
- }
50
- return {
51
- data,
52
- content: parsedBlock.content,
53
- frontmatter: parsedBlock.frontmatter,
54
- bodyStartLine: parsedBlock.bodyStartLine,
55
- }
56
- }
57
-
58
- export function parseFrontmatterBlock(
59
- raw: string,
60
- ): { frontmatter: string; content: string; bodyStartLine: number } | null {
61
- const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n?([\s\S]*)$/)
62
- if (!match) return null
63
- return {
64
- frontmatter: match[1],
65
- content: match[2],
66
- bodyStartLine: countLines(raw.slice(0, match[0].length - match[2].length)) + 1,
67
- }
68
- }
69
-
70
- function countLines(text: string): number {
71
- if (text.length === 0) return 0
72
- return text.split(/\r?\n/).length - 1
73
- }
74
-
75
- /**
76
- * Parse a simple YAML scalar value (string, boolean, or number).
77
- */
78
- export function parseYamlScalar(value: string): unknown {
79
- if (value === "") return ""
80
- if (value === "true") return true
81
- if (value === "false") return false
82
- const asNumber = Number(value)
83
- if (!Number.isNaN(asNumber)) return asNumber
84
- if ((value.startsWith('"') && value.endsWith('"')) || (value.startsWith("'") && value.endsWith("'"))) {
85
- return value.slice(1, -1)
86
- }
87
- return value
88
- }
89
-
90
- /**
91
- * Coerce an unknown value to a trimmed string, or return undefined if empty/non-string.
92
- */
93
- export function toStringOrUndefined(value: unknown): string | undefined {
94
- return typeof value === "string" && value.trim() ? value : undefined
95
- }
package/src/github.ts DELETED
@@ -1,21 +0,0 @@
1
- export const GITHUB_API_BASE = "https://api.github.com"
2
-
3
- export function githubHeaders(): HeadersInit {
4
- const token = process.env.GITHUB_TOKEN?.trim()
5
- const headers: Record<string, string> = {
6
- Accept: "application/vnd.github+json",
7
- "User-Agent": "agentikit-registry",
8
- }
9
- if (token) headers.Authorization = `Bearer ${token}`
10
- return headers
11
- }
12
-
13
- export function asRecord(value: unknown): Record<string, unknown> {
14
- return typeof value === "object" && value !== null && !Array.isArray(value)
15
- ? value as Record<string, unknown>
16
- : {}
17
- }
18
-
19
- export function asString(value: unknown): string | undefined {
20
- return typeof value === "string" && value ? value : undefined
21
- }
@@ -1,32 +0,0 @@
1
- import { parseFrontmatter, toStringOrUndefined } from "../frontmatter"
2
- import { isMarkdownFile, markdownCanonicalName, markdownAssetPath } from "./markdown-helpers"
3
- import type { AssetTypeHandler, ShowInput } from "../asset-type-handler"
4
- import type { ShowResponse } from "../stash-types"
5
-
6
- export const agentHandler: AssetTypeHandler = {
7
- typeName: "agent",
8
- stashDir: "agents",
9
-
10
- isRelevantFile: isMarkdownFile,
11
- toCanonicalName: markdownCanonicalName,
12
- toAssetPath: markdownAssetPath,
13
-
14
- buildShowResponse(input: ShowInput): ShowResponse {
15
- const parsedMd = parseFrontmatter(input.content)
16
- return {
17
- type: "agent",
18
- name: input.name,
19
- path: input.path,
20
- description: toStringOrUndefined(parsedMd.data.description),
21
- prompt: "Dispatching prompt must include the agent's full prompt content verbatim; summaries are non-compliant. \n\n"
22
- + parsedMd.content,
23
- toolPolicy: parsedMd.data.tools,
24
- modelHint: parsedMd.data.model,
25
- }
26
- },
27
-
28
- defaultUsageGuide: [
29
- "Read the .md file and dispatch an agent using the content of the file. Use modelHint/toolPolicy when present to run the agent with compatible settings.",
30
- "Use with `akm show <openRef>` to get the full prompt payload.",
31
- ],
32
- }