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.
Files changed (156) hide show
  1. package/LICENSE +385 -0
  2. package/README.md +187 -110
  3. package/dist/{src/asset-spec.js → asset-spec.js} +11 -2
  4. package/dist/{src/asset-type-handler.js → asset-type-handler.js} +4 -3
  5. package/dist/cli.js +709 -0
  6. package/dist/common.js +192 -0
  7. package/dist/{src/config-cli.js → config-cli.js} +36 -30
  8. package/dist/{src/config.js → config.js} +95 -25
  9. package/dist/{src/db.js → db.js} +123 -51
  10. package/dist/{src/embedder.js → embedder.js} +57 -2
  11. package/dist/errors.js +28 -0
  12. package/dist/file-context.js +188 -0
  13. package/dist/{src/frontmatter.js → frontmatter.js} +1 -1
  14. package/dist/{src/github.js → github.js} +1 -3
  15. package/dist/handlers/agent-handler.js +19 -0
  16. package/dist/handlers/command-handler.js +20 -0
  17. package/dist/handlers/handler-bridge.js +51 -0
  18. package/dist/handlers/index.js +19 -0
  19. package/dist/handlers/knowledge-handler.js +32 -0
  20. package/dist/handlers/script-handler.js +42 -0
  21. package/dist/{src/handlers → handlers}/skill-handler.js +5 -6
  22. package/dist/{src/handlers → handlers}/tool-handler.js +8 -24
  23. package/dist/{src/indexer.js → indexer.js} +50 -26
  24. package/dist/init.js +43 -0
  25. package/dist/{src/llm.js → llm.js} +6 -11
  26. package/dist/lockfile.js +60 -0
  27. package/dist/matchers.js +163 -0
  28. package/dist/{src/metadata.js → metadata.js} +36 -16
  29. package/dist/{src/origin-resolve.js → origin-resolve.js} +10 -9
  30. package/dist/paths.js +83 -0
  31. package/dist/{src/registry-install.js → registry-install.js} +151 -19
  32. package/dist/{src/registry-resolve.js → registry-resolve.js} +190 -26
  33. package/dist/{src/registry-search.js → registry-search.js} +13 -21
  34. package/dist/renderers.js +286 -0
  35. package/dist/{src/ripgrep-install.js → ripgrep-install.js} +8 -27
  36. package/dist/{src/ripgrep-resolve.js → ripgrep-resolve.js} +21 -11
  37. package/dist/ripgrep.js +2 -0
  38. package/dist/self-update.js +226 -0
  39. package/dist/{src/stash-add.js → stash-add.js} +14 -4
  40. package/dist/stash-clone.js +115 -0
  41. package/dist/{src/stash-ref.js → stash-ref.js} +10 -9
  42. package/dist/{src/stash-registry.js → stash-registry.js} +21 -46
  43. package/dist/{src/stash-resolve.js → stash-resolve.js} +10 -9
  44. package/dist/{src/stash-search.js → stash-search.js} +89 -74
  45. package/dist/stash-show.js +74 -0
  46. package/dist/stash-source.js +127 -0
  47. package/dist/submit.js +557 -0
  48. package/dist/{src/tool-runner.js → tool-runner.js} +1 -5
  49. package/dist/{src/walker.js → walker.js} +38 -0
  50. package/dist/warn.js +20 -0
  51. package/package.json +13 -18
  52. package/dist/index.d.ts +0 -28
  53. package/dist/index.js +0 -15
  54. package/dist/src/asset-spec.d.ts +0 -16
  55. package/dist/src/asset-type-handler.d.ts +0 -27
  56. package/dist/src/cli.d.ts +0 -2
  57. package/dist/src/cli.js +0 -399
  58. package/dist/src/common.d.ts +0 -13
  59. package/dist/src/common.js +0 -60
  60. package/dist/src/config-cli.d.ts +0 -9
  61. package/dist/src/config.d.ts +0 -50
  62. package/dist/src/db.d.ts +0 -46
  63. package/dist/src/embedder.d.ts +0 -10
  64. package/dist/src/frontmatter.d.ts +0 -30
  65. package/dist/src/github.d.ts +0 -4
  66. package/dist/src/handlers/agent-handler.d.ts +0 -2
  67. package/dist/src/handlers/agent-handler.js +0 -26
  68. package/dist/src/handlers/command-handler.d.ts +0 -2
  69. package/dist/src/handlers/command-handler.js +0 -23
  70. package/dist/src/handlers/index.d.ts +0 -6
  71. package/dist/src/handlers/index.js +0 -23
  72. package/dist/src/handlers/knowledge-handler.d.ts +0 -2
  73. package/dist/src/handlers/knowledge-handler.js +0 -56
  74. package/dist/src/handlers/markdown-helpers.d.ts +0 -7
  75. package/dist/src/handlers/script-handler.d.ts +0 -2
  76. package/dist/src/handlers/script-handler.js +0 -78
  77. package/dist/src/handlers/skill-handler.d.ts +0 -2
  78. package/dist/src/handlers/tool-handler.d.ts +0 -2
  79. package/dist/src/indexer.d.ts +0 -22
  80. package/dist/src/init.d.ts +0 -19
  81. package/dist/src/init.js +0 -99
  82. package/dist/src/llm.d.ts +0 -15
  83. package/dist/src/markdown.d.ts +0 -18
  84. package/dist/src/metadata.d.ts +0 -41
  85. package/dist/src/origin-resolve.d.ts +0 -19
  86. package/dist/src/registry-install.d.ts +0 -11
  87. package/dist/src/registry-resolve.d.ts +0 -3
  88. package/dist/src/registry-search.d.ts +0 -27
  89. package/dist/src/registry-types.d.ts +0 -62
  90. package/dist/src/ripgrep-install.d.ts +0 -12
  91. package/dist/src/ripgrep-resolve.d.ts +0 -13
  92. package/dist/src/ripgrep.d.ts +0 -3
  93. package/dist/src/ripgrep.js +0 -2
  94. package/dist/src/stash-add.d.ts +0 -4
  95. package/dist/src/stash-clone.d.ts +0 -22
  96. package/dist/src/stash-clone.js +0 -83
  97. package/dist/src/stash-ref.d.ts +0 -31
  98. package/dist/src/stash-registry.d.ts +0 -18
  99. package/dist/src/stash-resolve.d.ts +0 -2
  100. package/dist/src/stash-search.d.ts +0 -8
  101. package/dist/src/stash-show.d.ts +0 -5
  102. package/dist/src/stash-show.js +0 -46
  103. package/dist/src/stash-source.d.ts +0 -24
  104. package/dist/src/stash-source.js +0 -81
  105. package/dist/src/stash-types.d.ts +0 -227
  106. package/dist/src/stash.d.ts +0 -16
  107. package/dist/src/stash.js +0 -9
  108. package/dist/src/tool-runner.d.ts +0 -35
  109. package/dist/src/walker.d.ts +0 -19
  110. package/src/asset-spec.ts +0 -85
  111. package/src/asset-type-handler.ts +0 -77
  112. package/src/cli.ts +0 -427
  113. package/src/common.ts +0 -76
  114. package/src/config-cli.ts +0 -499
  115. package/src/config.ts +0 -305
  116. package/src/db.ts +0 -411
  117. package/src/embedder.ts +0 -128
  118. package/src/frontmatter.ts +0 -95
  119. package/src/github.ts +0 -21
  120. package/src/handlers/agent-handler.ts +0 -32
  121. package/src/handlers/command-handler.ts +0 -29
  122. package/src/handlers/index.ts +0 -25
  123. package/src/handlers/knowledge-handler.ts +0 -62
  124. package/src/handlers/markdown-helpers.ts +0 -19
  125. package/src/handlers/script-handler.ts +0 -92
  126. package/src/handlers/skill-handler.ts +0 -37
  127. package/src/handlers/tool-handler.ts +0 -71
  128. package/src/indexer.ts +0 -392
  129. package/src/init.ts +0 -114
  130. package/src/llm.ts +0 -125
  131. package/src/markdown.ts +0 -106
  132. package/src/metadata.ts +0 -333
  133. package/src/origin-resolve.ts +0 -67
  134. package/src/registry-install.ts +0 -361
  135. package/src/registry-resolve.ts +0 -341
  136. package/src/registry-search.ts +0 -335
  137. package/src/registry-types.ts +0 -72
  138. package/src/ripgrep-install.ts +0 -200
  139. package/src/ripgrep-resolve.ts +0 -72
  140. package/src/ripgrep.ts +0 -3
  141. package/src/stash-add.ts +0 -63
  142. package/src/stash-clone.ts +0 -127
  143. package/src/stash-ref.ts +0 -99
  144. package/src/stash-registry.ts +0 -259
  145. package/src/stash-resolve.ts +0 -50
  146. package/src/stash-search.ts +0 -613
  147. package/src/stash-show.ts +0 -55
  148. package/src/stash-source.ts +0 -103
  149. package/src/stash-types.ts +0 -231
  150. package/src/stash.ts +0 -39
  151. package/src/tool-runner.ts +0 -142
  152. package/src/walker.ts +0 -53
  153. /package/dist/{src/handlers → handlers}/markdown-helpers.js +0 -0
  154. /package/dist/{src/markdown.js → markdown.js} +0 -0
  155. /package/dist/{src/registry-types.js → registry-types.js} +0 -0
  156. /package/dist/{src/stash-types.js → stash-types.js} +0 -0
package/src/indexer.ts DELETED
@@ -1,392 +0,0 @@
1
- import fs from "node:fs"
2
- import path from "node:path"
3
- import { type AgentikitAssetType, resolveStashDir } from "./common"
4
- import { ASSET_TYPES, TYPE_DIRS, deriveCanonicalAssetName } from "./asset-spec"
5
- import {
6
- type StashFile,
7
- type StashEntry,
8
- loadStashFile,
9
- writeStashFile,
10
- generateMetadata,
11
- } from "./metadata"
12
- import { walkStash } from "./walker"
13
- import type { LlmConnectionConfig } from "./config"
14
- import {
15
- openDatabase,
16
- closeDatabase,
17
- getDbPath,
18
- getMeta,
19
- setMeta,
20
- upsertEntry,
21
- deleteEntriesByDir,
22
- rebuildFts,
23
- upsertEmbedding,
24
- getEntriesByDir,
25
- getEntryCount,
26
- isVecAvailable,
27
- DB_VERSION,
28
- type DbIndexedEntry,
29
- } from "./db"
30
-
31
- // ── Types ───────────────────────────────────────────────────────────────────
32
-
33
- export interface IndexResponse {
34
- stashDir: string
35
- totalEntries: number
36
- generatedMetadata: number
37
- indexPath: string
38
- mode: "full" | "incremental"
39
- directoriesScanned: number
40
- directoriesSkipped: number
41
- /** Timing counters in milliseconds */
42
- timing?: { totalMs: number; walkMs: number; embedMs: number; ftsMs: number }
43
- }
44
-
45
- // ── Indexer ──────────────────────────────────────────────────────────────────
46
-
47
- export async function agentikitIndex(options?: { stashDir?: string; full?: boolean }): Promise<IndexResponse> {
48
- const stashDir = options?.stashDir || resolveStashDir()
49
-
50
- // Load config and resolve all stash sources
51
- const { loadConfig } = await import("./config.js")
52
- const config = loadConfig()
53
- const { resolveAllStashDirs } = await import("./stash-source.js")
54
- const allStashDirs = resolveAllStashDirs(stashDir)
55
-
56
- const t0 = Date.now()
57
-
58
- // Open database — pass embedding dimension from config if available
59
- const dbPath = getDbPath()
60
- const embeddingDim = config.embedding?.dimension
61
- const db = openDatabase(dbPath, embeddingDim ? { embeddingDim } : undefined)
62
-
63
- try {
64
- // Check if we should do incremental
65
- const prevStashDir = getMeta(db, "stashDir")
66
- const prevBuiltAt = getMeta(db, "builtAt")
67
- const isIncremental = !options?.full && prevStashDir === stashDir && !!prevBuiltAt
68
- const builtAtMs = isIncremental ? new Date(prevBuiltAt!).getTime() : 0
69
-
70
- if (options?.full || !isIncremental) {
71
- // Wipe all entries for full rebuild or stashDir change
72
- db.exec("DELETE FROM entries")
73
- db.exec("DELETE FROM entries_fts")
74
- if (isVecAvailable()) {
75
- try { db.exec("DELETE FROM entries_vec") } catch { /* ignore */ }
76
- }
77
- }
78
-
79
- const tWalkStart = Date.now()
80
-
81
- // Walk stash dirs and index entries
82
- const { scannedDirs, skippedDirs, generatedCount, dirsNeedingLlm } = indexEntries(db, allStashDirs, stashDir, isIncremental, builtAtMs)
83
-
84
- // Enhance entries with LLM if configured
85
- await enhanceDirsWithLlm(db, config, dirsNeedingLlm)
86
-
87
- const tWalkEnd = Date.now()
88
-
89
- // Rebuild FTS after all inserts
90
- rebuildFts(db)
91
- const tFtsEnd = Date.now()
92
-
93
- // Generate embeddings if semantic search is enabled
94
- const hasEmbeddings = await generateEmbeddingsForDb(db, config)
95
-
96
- const tEmbedEnd = Date.now()
97
-
98
- // Update metadata
99
- setMeta(db, "version", String(DB_VERSION))
100
- setMeta(db, "builtAt", new Date().toISOString())
101
- setMeta(db, "stashDir", stashDir)
102
- setMeta(db, "stashDirs", JSON.stringify(allStashDirs))
103
- setMeta(db, "hasEmbeddings", hasEmbeddings ? "1" : "0")
104
-
105
- const totalEntries = getEntryCount(db)
106
-
107
- const tEnd = Date.now()
108
-
109
- return {
110
- stashDir,
111
- totalEntries,
112
- generatedMetadata: generatedCount,
113
- indexPath: dbPath,
114
- mode: isIncremental ? "incremental" : "full",
115
- directoriesScanned: scannedDirs,
116
- directoriesSkipped: skippedDirs,
117
- timing: {
118
- totalMs: tEnd - t0,
119
- walkMs: tWalkEnd - tWalkStart,
120
- embedMs: tEmbedEnd - tFtsEnd,
121
- ftsMs: tFtsEnd - tWalkEnd,
122
- },
123
- }
124
- } finally {
125
- closeDatabase(db)
126
- }
127
- }
128
-
129
- // ── Extracted helpers for agentikitIndex ─────────────────────────────────────
130
-
131
- function indexEntries(
132
- db: import("bun:sqlite").Database,
133
- allStashDirs: string[],
134
- stashDir: string,
135
- isIncremental: boolean,
136
- builtAtMs: number,
137
- ): { scannedDirs: number; skippedDirs: number; generatedCount: number; dirsNeedingLlm: Array<{ dirPath: string; files: string[]; assetType: AgentikitAssetType; currentStashDir: string }> } {
138
- let scannedDirs = 0
139
- let skippedDirs = 0
140
- let generatedCount = 0
141
- const seenPaths = new Set<string>()
142
- const dirsNeedingLlm: Array<{ dirPath: string; files: string[]; assetType: AgentikitAssetType; currentStashDir: string }> = []
143
-
144
- const insertTransaction = db.transaction(() => {
145
- for (const currentStashDir of allStashDirs) {
146
- for (const assetType of ASSET_TYPES as AgentikitAssetType[]) {
147
- const typeRoot = path.join(currentStashDir, TYPE_DIRS[assetType])
148
- try {
149
- if (!fs.statSync(typeRoot).isDirectory()) continue
150
- } catch { continue }
151
-
152
- const dirGroups = walkStash(typeRoot, assetType)
153
-
154
- for (const { dirPath, files } of dirGroups) {
155
- if (seenPaths.has(path.resolve(dirPath))) continue
156
- seenPaths.add(path.resolve(dirPath))
157
-
158
- // Incremental: skip directories that haven't changed
159
- if (isIncremental) {
160
- const prevEntries = getEntriesByDir(db, dirPath)
161
- if (prevEntries.length > 0 && !isDirStale(dirPath, files, prevEntries, builtAtMs)) {
162
- skippedDirs++
163
- continue
164
- }
165
- }
166
-
167
- scannedDirs++
168
-
169
- // Delete old entries for this dir (will be re-inserted)
170
- deleteEntriesByDir(db, dirPath)
171
-
172
- // Try loading existing .stash.json (user metadata overrides)
173
- let stash = loadStashFile(dirPath)
174
-
175
- if (stash) {
176
- const migration = migrateGeneratedSkillMetadata(stash, files, typeRoot)
177
- if (migration.changed) {
178
- stash = migration.stash
179
- writeStashFile(dirPath, stash)
180
- }
181
- }
182
-
183
- if (!stash) {
184
- // Generate metadata heuristically
185
- stash = generateMetadata(dirPath, assetType, files, typeRoot)
186
- if (stash.entries.length > 0) {
187
- writeStashFile(dirPath, stash)
188
- generatedCount += stash.entries.length
189
- }
190
- }
191
-
192
- if (stash) {
193
- for (const entry of stash.entries) {
194
- const entryPath = entry.entry
195
- ? path.join(dirPath, entry.entry)
196
- : files[0] || dirPath
197
- const entryKey = `${currentStashDir}:${entry.type}:${entry.name}`
198
- const searchText = buildSearchText(entry)
199
-
200
- upsertEntry(db, entryKey, dirPath, entryPath, currentStashDir, entry, searchText)
201
- }
202
-
203
- // Collect dirs needing LLM enhancement during the first walk
204
- if (stash.entries.some((e) => e.generated)) {
205
- dirsNeedingLlm.push({ dirPath, files, assetType, currentStashDir })
206
- }
207
- }
208
- }
209
- }
210
- }
211
- })
212
-
213
- insertTransaction()
214
-
215
- return { scannedDirs, skippedDirs, generatedCount, dirsNeedingLlm }
216
- }
217
-
218
- async function enhanceDirsWithLlm(
219
- db: import("bun:sqlite").Database,
220
- config: import("./config").AgentikitConfig,
221
- dirsNeedingLlm: Array<{ dirPath: string; files: string[]; assetType: AgentikitAssetType; currentStashDir: string }>,
222
- ): Promise<void> {
223
- if (!config.llm || dirsNeedingLlm.length === 0) return
224
-
225
- for (const { dirPath, files, currentStashDir } of dirsNeedingLlm) {
226
- let stash = loadStashFile(dirPath)
227
- if (!stash) continue
228
- stash = await enhanceStashWithLlm(config.llm, stash, dirPath, files)
229
- writeStashFile(dirPath, stash)
230
-
231
- // Re-upsert enhanced entries
232
- for (const entry of stash.entries) {
233
- const entryPath = entry.entry ? path.join(dirPath, entry.entry) : files[0] || dirPath
234
- const entryKey = `${currentStashDir}:${entry.type}:${entry.name}`
235
- const searchText = buildSearchText(entry)
236
- upsertEntry(db, entryKey, dirPath, entryPath, currentStashDir, entry, searchText)
237
- }
238
- }
239
- }
240
-
241
- async function generateEmbeddingsForDb(
242
- db: import("bun:sqlite").Database,
243
- config: import("./config").AgentikitConfig,
244
- ): Promise<boolean> {
245
- if (!config.semanticSearch || !isVecAvailable()) return false
246
-
247
- try {
248
- const { embed } = await import("./embedder.js")
249
- const allEntries = getAllEntriesForEmbedding(db)
250
- for (const { id, searchText } of allEntries) {
251
- const embedding = await embed(searchText, config.embedding)
252
- upsertEmbedding(db, id, embedding)
253
- }
254
- return true
255
- } catch (error) {
256
- console.warn("Embedding generation failed, continuing without:", error instanceof Error ? error.message : String(error))
257
- return false
258
- }
259
- }
260
-
261
- // ── Helpers ─────────────────────────────────────────────────────────────────
262
-
263
- function getAllEntriesForEmbedding(db: import("bun:sqlite").Database): Array<{ id: number; searchText: string }> {
264
- return db
265
- .prepare(`
266
- SELECT e.id, e.search_text AS searchText FROM entries e
267
- WHERE NOT EXISTS (SELECT 1 FROM entries_vec v WHERE v.id = e.id)
268
- `)
269
- .all() as Array<{ id: number; searchText: string }>
270
- }
271
-
272
- function isDirStale(
273
- dirPath: string,
274
- currentFiles: string[],
275
- previousEntries: DbIndexedEntry[],
276
- builtAtMs: number,
277
- ): boolean {
278
- // Check if file set changed (additions or deletions)
279
- const prevFileNames = new Set(
280
- previousEntries
281
- .map((ie) => ie.entry.entry)
282
- .filter((e): e is string => !!e),
283
- )
284
- const currFileNames = new Set(currentFiles.map((f) => path.basename(f)))
285
- if (prevFileNames.size !== currFileNames.size) return true
286
- for (const name of currFileNames) {
287
- if (!prevFileNames.has(name)) return true
288
- }
289
-
290
- // Check modification times of current files
291
- for (const file of currentFiles) {
292
- try {
293
- if (fs.statSync(file).mtimeMs > builtAtMs) return true
294
- } catch {
295
- return true
296
- }
297
- }
298
-
299
- // Check .stash.json modification time
300
- const stashPath = path.join(dirPath, ".stash.json")
301
- try {
302
- if (fs.existsSync(stashPath) && fs.statSync(stashPath).mtimeMs > builtAtMs) return true
303
- } catch {
304
- // ignore
305
- }
306
-
307
- return false
308
- }
309
-
310
- function migrateGeneratedSkillMetadata(
311
- stash: StashFile,
312
- files: string[],
313
- typeRoot: string,
314
- ): { stash: StashFile; changed: boolean } {
315
- const fileByBaseName = new Map(files.map((filePath) => [path.basename(filePath), filePath]))
316
- let changed = false
317
-
318
- const entries = stash.entries.map((entry) => {
319
- if (entry.type !== "skill" || entry.generated !== true) return entry
320
-
321
- const hintedFilePath = entry.entry ? fileByBaseName.get(path.basename(entry.entry)) : undefined
322
- const skillFilePath = hintedFilePath ?? fileByBaseName.get("SKILL.md")
323
- if (!skillFilePath) return entry
324
-
325
- const canonicalName = deriveCanonicalAssetName("skill", typeRoot, skillFilePath)
326
- if (!canonicalName || canonicalName === entry.name) return entry
327
-
328
- changed = true
329
- return { ...entry, name: canonicalName }
330
- })
331
-
332
- if (!changed) {
333
- return { stash, changed: false }
334
- }
335
-
336
- return {
337
- stash: { entries },
338
- changed: true,
339
- }
340
- }
341
-
342
- async function enhanceStashWithLlm(
343
- llmConfig: LlmConnectionConfig,
344
- stash: StashFile,
345
- dirPath: string,
346
- files: string[],
347
- ): Promise<StashFile> {
348
- const { enhanceMetadata } = await import("./llm.js")
349
-
350
- const enhanced: StashEntry[] = []
351
- for (const entry of stash.entries) {
352
- try {
353
- const entryFile = entry.entry
354
- ? files.find((f) => path.basename(f) === entry.entry) ?? files[0]
355
- : files[0]
356
- let fileContent: string | undefined
357
- if (entryFile) {
358
- try {
359
- fileContent = fs.readFileSync(entryFile, "utf8")
360
- } catch { /* ignore unreadable files */ }
361
- }
362
-
363
- const improvements = await enhanceMetadata(llmConfig, entry, fileContent)
364
- const updated = { ...entry }
365
- if (improvements.description) updated.description = improvements.description
366
- if (improvements.intents?.length) updated.intents = improvements.intents
367
- if (improvements.tags?.length) updated.tags = improvements.tags
368
- enhanced.push(updated)
369
- } catch {
370
- enhanced.push(entry)
371
- }
372
- }
373
- return { entries: enhanced }
374
- }
375
-
376
- export function buildSearchText(entry: StashEntry): string {
377
- const parts: string[] = [entry.name.replace(/[-_]/g, " ")]
378
- if (entry.description) parts.push(entry.description)
379
- if (entry.tags) parts.push(entry.tags.join(" "))
380
- if (entry.examples) parts.push(entry.examples.join(" "))
381
- if (entry.aliases) parts.push(entry.aliases.join(" "))
382
- if (entry.intents) parts.push(entry.intents.join(" "))
383
- if (entry.intent) {
384
- if (entry.intent.when) parts.push(entry.intent.when)
385
- if (entry.intent.input) parts.push(entry.intent.input)
386
- if (entry.intent.output) parts.push(entry.intent.output)
387
- }
388
- if (entry.toc) {
389
- parts.push(entry.toc.map((h) => h.text).join(" "))
390
- }
391
- return parts.join(" ").toLowerCase()
392
- }
package/src/init.ts DELETED
@@ -1,114 +0,0 @@
1
- /**
2
- * Agentikit initialization logic.
3
- *
4
- * Creates the working stash directory structure, sets the AKM_STASH_DIR
5
- * environment variable, and ensures ripgrep is available.
6
- */
7
-
8
- import { spawnSync } from "node:child_process"
9
- import fs from "node:fs"
10
- import path from "node:path"
11
- import { IS_WINDOWS, TYPE_DIRS } from "./common"
12
- import { ensureRg } from "./ripgrep-install"
13
- import { getConfigPath, saveConfig, DEFAULT_CONFIG } from "./config"
14
-
15
- export interface InitResponse {
16
- stashDir: string
17
- created: boolean
18
- envSet: boolean
19
- profileUpdated?: string
20
- configPath: string
21
- ripgrep?: {
22
- rgPath: string
23
- installed: boolean
24
- version: string
25
- }
26
- }
27
-
28
- export async function agentikitInit(): Promise<InitResponse> {
29
- let stashDir: string
30
- if (IS_WINDOWS) {
31
- const userProfile = process.env.USERPROFILE?.trim()
32
- if (!userProfile) {
33
- throw new Error("Unable to determine Documents folder. Ensure USERPROFILE is set.")
34
- }
35
- const docs = path.join(userProfile, "Documents")
36
- stashDir = path.join(docs, "agentikit")
37
- } else {
38
- const home = process.env.HOME?.trim()
39
- if (!home) {
40
- throw new Error("Unable to determine home directory. Set HOME.")
41
- }
42
- stashDir = path.join(home, "agentikit")
43
- }
44
-
45
- let created = false
46
- if (!fs.existsSync(stashDir)) {
47
- fs.mkdirSync(stashDir, { recursive: true })
48
- created = true
49
- }
50
-
51
- for (const sub of Object.values(TYPE_DIRS)) {
52
- const subDir = path.join(stashDir, sub)
53
- if (!fs.existsSync(subDir)) {
54
- fs.mkdirSync(subDir, { recursive: true })
55
- }
56
- }
57
-
58
- let envSet = false
59
- let profileUpdated: string | undefined
60
-
61
- if (IS_WINDOWS) {
62
- const result = spawnSync("setx", ["AKM_STASH_DIR", stashDir], {
63
- encoding: "utf8",
64
- timeout: 10_000,
65
- })
66
- envSet = result.status === 0
67
- } else {
68
- const shell = process.env.SHELL || ""
69
- const homeDir = process.env.HOME! // already validated non-empty above
70
- let profile: string
71
- if (shell.endsWith("/zsh")) {
72
- profile = path.join(homeDir, ".zshrc")
73
- } else if (shell.endsWith("/bash")) {
74
- profile = path.join(homeDir, ".bashrc")
75
- } else {
76
- profile = path.join(homeDir, ".profile")
77
- }
78
-
79
- const exportLine = `export AKM_STASH_DIR="${stashDir}"`
80
- const existing = fs.existsSync(profile) ? fs.readFileSync(profile, "utf8") : ""
81
- if (!existing.includes("AKM_STASH_DIR")) {
82
- const updated = existing + `\n# Agentikit working stash directory\n${exportLine}\n`
83
- const tmpPath = profile + `.tmp.${process.pid}`
84
- try {
85
- fs.writeFileSync(tmpPath, updated, "utf8")
86
- fs.renameSync(tmpPath, profile)
87
- } catch (err) {
88
- try { fs.unlinkSync(tmpPath) } catch { /* ignore */ }
89
- throw err
90
- }
91
- envSet = true
92
- profileUpdated = profile
93
- }
94
- }
95
-
96
- // Create default config.json if it doesn't exist
97
- const configPath = getConfigPath()
98
- if (!fs.existsSync(configPath)) {
99
- saveConfig(DEFAULT_CONFIG)
100
- }
101
-
102
- process.env.AKM_STASH_DIR = stashDir
103
-
104
- // Ensure ripgrep is available (install to stash/bin if needed)
105
- let ripgrep: InitResponse["ripgrep"]
106
- try {
107
- const rgResult = ensureRg(stashDir)
108
- ripgrep = rgResult
109
- } catch {
110
- // Non-fatal: ripgrep is optional, search works without it
111
- }
112
-
113
- return { stashDir, created, envSet, profileUpdated, configPath, ripgrep }
114
- }
package/src/llm.ts DELETED
@@ -1,125 +0,0 @@
1
- import type { LlmConnectionConfig } from "./config"
2
- import { fetchWithTimeout } from "./common"
3
- import type { StashEntry } from "./metadata"
4
-
5
- // ── OpenAI-compatible chat completions ──────────────────────────────────────
6
-
7
- interface ChatMessage {
8
- role: "system" | "user" | "assistant"
9
- content: string
10
- }
11
-
12
- interface ChatCompletionResponse {
13
- choices: Array<{ message: { content: string } }>
14
- }
15
-
16
- async function chatCompletion(
17
- config: LlmConnectionConfig,
18
- messages: ChatMessage[],
19
- ): Promise<string> {
20
- const headers: Record<string, string> = { "Content-Type": "application/json" }
21
- if (config.apiKey) {
22
- headers["Authorization"] = `Bearer ${config.apiKey}`
23
- }
24
-
25
- const response = await fetchWithTimeout(config.endpoint, {
26
- method: "POST",
27
- headers,
28
- body: JSON.stringify({
29
- model: config.model,
30
- messages,
31
- temperature: config.temperature ?? 0.3,
32
- max_tokens: config.maxTokens ?? 512,
33
- }),
34
- })
35
-
36
- if (!response.ok) {
37
- const body = await response.text().catch(() => "")
38
- throw new Error(`LLM request failed (${response.status}): ${body}`)
39
- }
40
-
41
- const json = (await response.json()) as ChatCompletionResponse
42
- return json.choices?.[0]?.message?.content?.trim() ?? ""
43
- }
44
-
45
- // ── Metadata Enhancement ────────────────────────────────────────────────────
46
-
47
- const SYSTEM_PROMPT = `You are a metadata generator for a developer tool registry. Given a tool/skill/command/agent entry, generate improved metadata. Respond with ONLY valid JSON, no markdown fencing.`
48
-
49
- /**
50
- * Use an LLM to enhance a stash entry's metadata: improve description,
51
- * generate intents, and suggest tags.
52
- */
53
- export async function enhanceMetadata(
54
- config: LlmConnectionConfig,
55
- entry: StashEntry,
56
- fileContent?: string,
57
- ): Promise<{ description?: string; intents?: string[]; tags?: string[] }> {
58
- const contextParts = [
59
- `Name: ${entry.name}`,
60
- `Type: ${entry.type}`,
61
- ]
62
- if (entry.description) contextParts.push(`Current description: ${entry.description}`)
63
- if (entry.tags?.length) contextParts.push(`Current tags: ${entry.tags.join(", ")}`)
64
- if (fileContent) {
65
- // Limit content to first 2000 chars to stay within token limits
66
- const truncated = fileContent.length > 2000
67
- ? fileContent.slice(0, 2000) + "\n... (truncated)"
68
- : fileContent
69
- contextParts.push(`File content:\n${truncated}`)
70
- }
71
-
72
- const userPrompt = `${contextParts.join("\n")}
73
-
74
- Generate improved metadata for this ${entry.type}. Return JSON with these fields:
75
- - "description": a clear, concise one-sentence description of what this does
76
- - "intents": an array of 3-6 natural language task phrases an agent might use to find this (e.g. "deploy a docker container", "run database migrations")
77
- - "tags": an array of 3-8 relevant keyword tags
78
-
79
- Return ONLY the JSON object, no explanation.`
80
-
81
- const raw = await chatCompletion(config, [
82
- { role: "system", content: SYSTEM_PROMPT },
83
- { role: "user", content: userPrompt },
84
- ])
85
-
86
- try {
87
- // Strip markdown code fences if present
88
- const cleaned = raw.replace(/^```(?:json)?\s*\n?/i, "").replace(/\n?```\s*$/i, "")
89
- const parsed = JSON.parse(cleaned) as Record<string, unknown>
90
- const result: { description?: string; intents?: string[]; tags?: string[] } = {}
91
-
92
- if (typeof parsed.description === "string" && parsed.description) {
93
- result.description = parsed.description
94
- }
95
- if (Array.isArray(parsed.intents)) {
96
- result.intents = parsed.intents.filter(
97
- (s): s is string => typeof s === "string" && s.trim().length > 0,
98
- ).slice(0, 8)
99
- }
100
- if (Array.isArray(parsed.tags)) {
101
- result.tags = parsed.tags.filter(
102
- (s): s is string => typeof s === "string" && s.trim().length > 0,
103
- ).slice(0, 10)
104
- }
105
-
106
- return result
107
- } catch {
108
- // LLM returned unparseable output, return empty
109
- return {}
110
- }
111
- }
112
-
113
- /**
114
- * Check if the LLM endpoint is reachable.
115
- */
116
- export async function isLlmAvailable(config: LlmConnectionConfig): Promise<boolean> {
117
- try {
118
- const result = await chatCompletion(config, [
119
- { role: "user", content: "Respond with just the word: ok" },
120
- ])
121
- return result.length > 0
122
- } catch {
123
- return false
124
- }
125
- }