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/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
|
-
}
|