agentikit 0.0.8 → 0.0.12
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/README.md +135 -117
- package/dist/index.d.ts +13 -3
- package/dist/index.js +7 -1
- package/dist/src/asset-spec.d.ts +2 -0
- package/dist/src/asset-spec.js +22 -3
- package/dist/src/asset-type-handler.d.ts +27 -0
- package/dist/src/asset-type-handler.js +33 -0
- package/dist/src/cli.js +335 -100
- package/dist/src/common.d.ts +6 -1
- package/dist/src/common.js +18 -4
- package/dist/src/config-cli.d.ts +9 -0
- package/dist/src/config-cli.js +473 -0
- package/dist/src/config.d.ts +25 -6
- package/dist/src/config.js +188 -28
- package/dist/src/db.d.ts +46 -0
- package/dist/src/db.js +299 -0
- package/dist/src/embedder.js +12 -7
- package/dist/src/github.d.ts +4 -0
- package/dist/src/github.js +19 -0
- package/dist/src/handlers/agent-handler.d.ts +2 -0
- package/dist/src/handlers/agent-handler.js +26 -0
- package/dist/src/handlers/command-handler.d.ts +2 -0
- package/dist/src/handlers/command-handler.js +23 -0
- package/dist/src/handlers/index.d.ts +6 -0
- package/dist/src/handlers/index.js +23 -0
- package/dist/src/handlers/knowledge-handler.d.ts +2 -0
- package/dist/src/handlers/knowledge-handler.js +56 -0
- package/dist/src/handlers/markdown-helpers.d.ts +7 -0
- package/dist/src/handlers/markdown-helpers.js +15 -0
- package/dist/src/handlers/script-handler.d.ts +2 -0
- package/dist/src/handlers/script-handler.js +78 -0
- package/dist/src/handlers/skill-handler.d.ts +2 -0
- package/dist/src/handlers/skill-handler.js +30 -0
- package/dist/src/handlers/tool-handler.d.ts +2 -0
- package/dist/src/handlers/tool-handler.js +58 -0
- package/dist/src/indexer.d.ts +1 -23
- package/dist/src/indexer.js +162 -155
- package/dist/src/init.d.ts +2 -2
- package/dist/src/init.js +21 -9
- package/dist/src/llm.js +4 -3
- package/dist/src/metadata.d.ts +1 -1
- package/dist/src/metadata.js +22 -64
- package/dist/src/origin-resolve.d.ts +19 -0
- package/dist/src/origin-resolve.js +53 -0
- package/dist/src/registry-install.d.ts +11 -0
- package/dist/src/registry-install.js +315 -0
- package/dist/src/registry-resolve.d.ts +3 -0
- package/dist/src/registry-resolve.js +299 -0
- package/dist/src/registry-search.d.ts +27 -0
- package/dist/src/registry-search.js +263 -0
- package/dist/src/registry-types.d.ts +62 -0
- package/dist/src/registry-types.js +1 -0
- package/dist/src/stash-add.d.ts +4 -0
- package/dist/src/stash-add.js +59 -0
- package/dist/src/stash-clone.d.ts +22 -0
- package/dist/src/stash-clone.js +83 -0
- package/dist/src/stash-ref.d.ts +27 -3
- package/dist/src/stash-ref.js +63 -24
- package/dist/src/stash-registry.d.ts +18 -0
- package/dist/src/stash-registry.js +221 -0
- package/dist/src/stash-resolve.js +3 -0
- package/dist/src/stash-search.d.ts +3 -1
- package/dist/src/stash-search.js +357 -138
- package/dist/src/stash-show.d.ts +1 -1
- package/dist/src/stash-show.js +28 -89
- package/dist/src/stash-source.d.ts +24 -0
- package/dist/src/stash-source.js +81 -0
- package/dist/src/stash-types.d.ts +175 -1
- package/dist/src/stash.d.ts +9 -1
- package/dist/src/stash.js +5 -0
- package/dist/src/tool-runner.d.ts +1 -1
- package/dist/src/tool-runner.js +18 -5
- package/package.json +7 -2
- package/src/asset-spec.ts +20 -4
- package/src/asset-type-handler.ts +77 -0
- package/src/cli.ts +354 -103
- package/src/common.ts +23 -5
- package/src/config-cli.ts +499 -0
- package/src/config.ts +218 -37
- package/src/db.ts +411 -0
- package/src/embedder.ts +22 -11
- package/src/github.ts +21 -0
- package/src/handlers/agent-handler.ts +32 -0
- package/src/handlers/command-handler.ts +29 -0
- package/src/handlers/index.ts +25 -0
- package/src/handlers/knowledge-handler.ts +62 -0
- package/src/handlers/markdown-helpers.ts +19 -0
- package/src/handlers/script-handler.ts +92 -0
- package/src/handlers/skill-handler.ts +37 -0
- package/src/handlers/tool-handler.ts +71 -0
- package/src/indexer.ts +208 -187
- package/src/init.ts +17 -9
- package/src/llm.ts +4 -3
- package/src/metadata.ts +21 -65
- package/src/origin-resolve.ts +67 -0
- package/src/registry-install.ts +361 -0
- package/src/registry-resolve.ts +341 -0
- package/src/registry-search.ts +335 -0
- package/src/registry-types.ts +72 -0
- package/src/stash-add.ts +63 -0
- package/src/stash-clone.ts +127 -0
- package/src/stash-ref.ts +84 -26
- package/src/stash-registry.ts +259 -0
- package/src/stash-resolve.ts +3 -0
- package/src/stash-search.ts +425 -155
- package/src/stash-show.ts +33 -82
- package/src/stash-source.ts +103 -0
- package/src/stash-types.ts +186 -1
- package/src/stash.ts +23 -0
- package/src/tool-runner.ts +18 -5
- package/dist/src/similarity.d.ts +0 -34
- package/src/similarity.ts +0 -271
package/dist/src/indexer.js
CHANGED
|
@@ -3,185 +3,194 @@ import path from "node:path";
|
|
|
3
3
|
import { resolveStashDir } from "./common";
|
|
4
4
|
import { ASSET_TYPES, TYPE_DIRS, deriveCanonicalAssetName } from "./asset-spec";
|
|
5
5
|
import { loadStashFile, writeStashFile, generateMetadata, } from "./metadata";
|
|
6
|
-
import { TfIdfAdapter } from "./similarity";
|
|
7
6
|
import { walkStash } from "./walker";
|
|
8
|
-
|
|
9
|
-
const INDEX_VERSION = 4;
|
|
10
|
-
// ── Index Path ──────────────────────────────────────────────────────────────
|
|
11
|
-
export function getIndexPath() {
|
|
12
|
-
const cacheDir = process.env.XDG_CACHE_HOME
|
|
13
|
-
|| path.join(process.env.HOME || process.env.USERPROFILE || "", ".cache");
|
|
14
|
-
return path.join(cacheDir, "agentikit", "index.json");
|
|
15
|
-
}
|
|
16
|
-
export function loadSearchIndex() {
|
|
17
|
-
const indexPath = getIndexPath();
|
|
18
|
-
if (!fs.existsSync(indexPath))
|
|
19
|
-
return null;
|
|
20
|
-
try {
|
|
21
|
-
const raw = JSON.parse(fs.readFileSync(indexPath, "utf8"));
|
|
22
|
-
if (raw?.version !== INDEX_VERSION)
|
|
23
|
-
return null;
|
|
24
|
-
return raw;
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
return null;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
7
|
+
import { openDatabase, closeDatabase, getDbPath, getMeta, setMeta, upsertEntry, deleteEntriesByDir, rebuildFts, upsertEmbedding, getEntriesByDir, getEntryCount, isVecAvailable, DB_VERSION, } from "./db";
|
|
30
8
|
// ── Indexer ──────────────────────────────────────────────────────────────────
|
|
31
9
|
export async function agentikitIndex(options) {
|
|
32
10
|
const stashDir = options?.stashDir || resolveStashDir();
|
|
33
|
-
// Load config
|
|
11
|
+
// Load config and resolve all stash sources
|
|
34
12
|
const { loadConfig } = await import("./config.js");
|
|
35
|
-
const config = loadConfig(
|
|
36
|
-
const
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
13
|
+
const config = loadConfig();
|
|
14
|
+
const { resolveAllStashDirs } = await import("./stash-source.js");
|
|
15
|
+
const allStashDirs = resolveAllStashDirs(stashDir);
|
|
16
|
+
const t0 = Date.now();
|
|
17
|
+
// Open database — pass embedding dimension from config if available
|
|
18
|
+
const dbPath = getDbPath();
|
|
19
|
+
const embeddingDim = config.embedding?.dimension;
|
|
20
|
+
const db = openDatabase(dbPath, embeddingDim ? { embeddingDim } : undefined);
|
|
21
|
+
try {
|
|
22
|
+
// Check if we should do incremental
|
|
23
|
+
const prevStashDir = getMeta(db, "stashDir");
|
|
24
|
+
const prevBuiltAt = getMeta(db, "builtAt");
|
|
25
|
+
const isIncremental = !options?.full && prevStashDir === stashDir && !!prevBuiltAt;
|
|
26
|
+
const builtAtMs = isIncremental ? new Date(prevBuiltAt).getTime() : 0;
|
|
27
|
+
if (options?.full || !isIncremental) {
|
|
28
|
+
// Wipe all entries for full rebuild or stashDir change
|
|
29
|
+
db.exec("DELETE FROM entries");
|
|
30
|
+
db.exec("DELETE FROM entries_fts");
|
|
31
|
+
if (isVecAvailable()) {
|
|
32
|
+
try {
|
|
33
|
+
db.exec("DELETE FROM entries_vec");
|
|
34
|
+
}
|
|
35
|
+
catch { /* ignore */ }
|
|
41
36
|
}
|
|
42
37
|
}
|
|
43
|
-
|
|
38
|
+
const tWalkStart = Date.now();
|
|
39
|
+
// Walk stash dirs and index entries
|
|
40
|
+
const { scannedDirs, skippedDirs, generatedCount, dirsNeedingLlm } = indexEntries(db, allStashDirs, stashDir, isIncremental, builtAtMs);
|
|
41
|
+
// Enhance entries with LLM if configured
|
|
42
|
+
await enhanceDirsWithLlm(db, config, dirsNeedingLlm);
|
|
43
|
+
const tWalkEnd = Date.now();
|
|
44
|
+
// Rebuild FTS after all inserts
|
|
45
|
+
rebuildFts(db);
|
|
46
|
+
const tFtsEnd = Date.now();
|
|
47
|
+
// Generate embeddings if semantic search is enabled
|
|
48
|
+
const hasEmbeddings = await generateEmbeddingsForDb(db, config);
|
|
49
|
+
const tEmbedEnd = Date.now();
|
|
50
|
+
// Update metadata
|
|
51
|
+
setMeta(db, "version", String(DB_VERSION));
|
|
52
|
+
setMeta(db, "builtAt", new Date().toISOString());
|
|
53
|
+
setMeta(db, "stashDir", stashDir);
|
|
54
|
+
setMeta(db, "stashDirs", JSON.stringify(allStashDirs));
|
|
55
|
+
setMeta(db, "hasEmbeddings", hasEmbeddings ? "1" : "0");
|
|
56
|
+
const totalEntries = getEntryCount(db);
|
|
57
|
+
const tEnd = Date.now();
|
|
58
|
+
return {
|
|
59
|
+
stashDir,
|
|
60
|
+
totalEntries,
|
|
61
|
+
generatedMetadata: generatedCount,
|
|
62
|
+
indexPath: dbPath,
|
|
63
|
+
mode: isIncremental ? "incremental" : "full",
|
|
64
|
+
directoriesScanned: scannedDirs,
|
|
65
|
+
directoriesSkipped: skippedDirs,
|
|
66
|
+
timing: {
|
|
67
|
+
totalMs: tEnd - t0,
|
|
68
|
+
walkMs: tWalkEnd - tWalkStart,
|
|
69
|
+
embedMs: tEmbedEnd - tFtsEnd,
|
|
70
|
+
ftsMs: tFtsEnd - tWalkEnd,
|
|
71
|
+
},
|
|
72
|
+
};
|
|
44
73
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
74
|
+
finally {
|
|
75
|
+
closeDatabase(db);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// ── Extracted helpers for agentikitIndex ─────────────────────────────────────
|
|
79
|
+
function indexEntries(db, allStashDirs, stashDir, isIncremental, builtAtMs) {
|
|
48
80
|
let scannedDirs = 0;
|
|
49
81
|
let skippedDirs = 0;
|
|
50
|
-
|
|
51
|
-
const previousIndex = !options?.full ? loadSearchIndex() : null;
|
|
52
|
-
const isIncremental = previousIndex !== null && previousIndex.stashDir === stashDir;
|
|
53
|
-
const builtAtMs = isIncremental ? new Date(previousIndex.builtAt).getTime() : 0;
|
|
54
|
-
// Build lookup of previous entries by dirPath
|
|
55
|
-
const previousEntriesByDir = new Map();
|
|
56
|
-
if (isIncremental) {
|
|
57
|
-
for (const ie of previousIndex.entries) {
|
|
58
|
-
const list = previousEntriesByDir.get(ie.dirPath) || [];
|
|
59
|
-
list.push(ie);
|
|
60
|
-
previousEntriesByDir.set(ie.dirPath, list);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
82
|
+
let generatedCount = 0;
|
|
63
83
|
const seenPaths = new Set();
|
|
64
|
-
const
|
|
65
|
-
|
|
66
|
-
for (const
|
|
67
|
-
const
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
// Group files by their immediate parent directory
|
|
76
|
-
const dirGroups = walkStash(typeRoot, assetType);
|
|
77
|
-
for (const { dirPath, files } of dirGroups) {
|
|
78
|
-
// Deduplicate by dirPath across stash dirs
|
|
79
|
-
if (seenPaths.has(path.resolve(dirPath)))
|
|
80
|
-
continue;
|
|
81
|
-
seenPaths.add(path.resolve(dirPath));
|
|
82
|
-
// Incremental: skip directories that haven't changed
|
|
83
|
-
const prevEntries = previousEntriesByDir.get(dirPath);
|
|
84
|
-
if (isIncremental && prevEntries && !isDirStale(dirPath, files, prevEntries, builtAtMs)) {
|
|
85
|
-
allEntries.push(...prevEntries);
|
|
86
|
-
skippedDirs++;
|
|
84
|
+
const dirsNeedingLlm = [];
|
|
85
|
+
const insertTransaction = db.transaction(() => {
|
|
86
|
+
for (const currentStashDir of allStashDirs) {
|
|
87
|
+
for (const assetType of ASSET_TYPES) {
|
|
88
|
+
const typeRoot = path.join(currentStashDir, TYPE_DIRS[assetType]);
|
|
89
|
+
try {
|
|
90
|
+
if (!fs.statSync(typeRoot).isDirectory())
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
87
94
|
continue;
|
|
88
95
|
}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
96
|
+
const dirGroups = walkStash(typeRoot, assetType);
|
|
97
|
+
for (const { dirPath, files } of dirGroups) {
|
|
98
|
+
if (seenPaths.has(path.resolve(dirPath)))
|
|
99
|
+
continue;
|
|
100
|
+
seenPaths.add(path.resolve(dirPath));
|
|
101
|
+
// Incremental: skip directories that haven't changed
|
|
102
|
+
if (isIncremental) {
|
|
103
|
+
const prevEntries = getEntriesByDir(db, dirPath);
|
|
104
|
+
if (prevEntries.length > 0 && !isDirStale(dirPath, files, prevEntries, builtAtMs)) {
|
|
105
|
+
skippedDirs++;
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
97
108
|
}
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
stash
|
|
102
|
-
|
|
103
|
-
if (
|
|
104
|
-
|
|
109
|
+
scannedDirs++;
|
|
110
|
+
// Delete old entries for this dir (will be re-inserted)
|
|
111
|
+
deleteEntriesByDir(db, dirPath);
|
|
112
|
+
// Try loading existing .stash.json (user metadata overrides)
|
|
113
|
+
let stash = loadStashFile(dirPath);
|
|
114
|
+
if (stash) {
|
|
115
|
+
const migration = migrateGeneratedSkillMetadata(stash, files, typeRoot);
|
|
116
|
+
if (migration.changed) {
|
|
117
|
+
stash = migration.stash;
|
|
118
|
+
writeStashFile(dirPath, stash);
|
|
119
|
+
}
|
|
105
120
|
}
|
|
106
|
-
if (stash
|
|
107
|
-
|
|
108
|
-
|
|
121
|
+
if (!stash) {
|
|
122
|
+
// Generate metadata heuristically
|
|
123
|
+
stash = generateMetadata(dirPath, assetType, files, typeRoot);
|
|
124
|
+
if (stash.entries.length > 0) {
|
|
125
|
+
writeStashFile(dirPath, stash);
|
|
126
|
+
generatedCount += stash.entries.length;
|
|
127
|
+
}
|
|
109
128
|
}
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
129
|
+
if (stash) {
|
|
130
|
+
for (const entry of stash.entries) {
|
|
131
|
+
const entryPath = entry.entry
|
|
132
|
+
? path.join(dirPath, entry.entry)
|
|
133
|
+
: files[0] || dirPath;
|
|
134
|
+
const entryKey = `${currentStashDir}:${entry.type}:${entry.name}`;
|
|
135
|
+
const searchText = buildSearchText(entry);
|
|
136
|
+
upsertEntry(db, entryKey, dirPath, entryPath, currentStashDir, entry, searchText);
|
|
137
|
+
}
|
|
138
|
+
// Collect dirs needing LLM enhancement during the first walk
|
|
139
|
+
if (stash.entries.some((e) => e.generated)) {
|
|
140
|
+
dirsNeedingLlm.push({ dirPath, files, assetType, currentStashDir });
|
|
141
|
+
}
|
|
117
142
|
}
|
|
118
143
|
}
|
|
119
144
|
}
|
|
120
145
|
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
const
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
const text = buildSearchText(ie.entry);
|
|
141
|
-
ie.embedding = await embed(text, config.embedding);
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
hasEmbeddings = true;
|
|
146
|
+
});
|
|
147
|
+
insertTransaction();
|
|
148
|
+
return { scannedDirs, skippedDirs, generatedCount, dirsNeedingLlm };
|
|
149
|
+
}
|
|
150
|
+
async function enhanceDirsWithLlm(db, config, dirsNeedingLlm) {
|
|
151
|
+
if (!config.llm || dirsNeedingLlm.length === 0)
|
|
152
|
+
return;
|
|
153
|
+
for (const { dirPath, files, currentStashDir } of dirsNeedingLlm) {
|
|
154
|
+
let stash = loadStashFile(dirPath);
|
|
155
|
+
if (!stash)
|
|
156
|
+
continue;
|
|
157
|
+
stash = await enhanceStashWithLlm(config.llm, stash, dirPath, files);
|
|
158
|
+
writeStashFile(dirPath, stash);
|
|
159
|
+
// Re-upsert enhanced entries
|
|
160
|
+
for (const entry of stash.entries) {
|
|
161
|
+
const entryPath = entry.entry ? path.join(dirPath, entry.entry) : files[0] || dirPath;
|
|
162
|
+
const entryKey = `${currentStashDir}:${entry.type}:${entry.name}`;
|
|
163
|
+
const searchText = buildSearchText(entry);
|
|
164
|
+
upsertEntry(db, entryKey, dirPath, entryPath, currentStashDir, entry, searchText);
|
|
145
165
|
}
|
|
146
|
-
|
|
147
|
-
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
async function generateEmbeddingsForDb(db, config) {
|
|
169
|
+
if (!config.semanticSearch || !isVecAvailable())
|
|
170
|
+
return false;
|
|
171
|
+
try {
|
|
172
|
+
const { embed } = await import("./embedder.js");
|
|
173
|
+
const allEntries = getAllEntriesForEmbedding(db);
|
|
174
|
+
for (const { id, searchText } of allEntries) {
|
|
175
|
+
const embedding = await embed(searchText, config.embedding);
|
|
176
|
+
upsertEmbedding(db, id, embedding);
|
|
148
177
|
}
|
|
178
|
+
return true;
|
|
149
179
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
const indexDir = path.dirname(indexPath);
|
|
154
|
-
if (!fs.existsSync(indexDir)) {
|
|
155
|
-
fs.mkdirSync(indexDir, { recursive: true });
|
|
180
|
+
catch (error) {
|
|
181
|
+
console.warn("Embedding generation failed, continuing without:", error instanceof Error ? error.message : String(error));
|
|
182
|
+
return false;
|
|
156
183
|
}
|
|
157
|
-
const index = {
|
|
158
|
-
version: INDEX_VERSION,
|
|
159
|
-
builtAt: new Date().toISOString(),
|
|
160
|
-
stashDir,
|
|
161
|
-
stashDirs: allStashDirs,
|
|
162
|
-
entries: allEntries,
|
|
163
|
-
tfidf: adapter.serialize(),
|
|
164
|
-
hasEmbeddings,
|
|
165
|
-
};
|
|
166
|
-
fs.writeFileSync(indexPath, JSON.stringify(index) + "\n", "utf8");
|
|
167
|
-
const tEnd = Date.now();
|
|
168
|
-
return {
|
|
169
|
-
stashDir,
|
|
170
|
-
totalEntries: allEntries.length,
|
|
171
|
-
generatedMetadata: generatedCount,
|
|
172
|
-
indexPath,
|
|
173
|
-
mode: isIncremental ? "incremental" : "full",
|
|
174
|
-
directoriesScanned: scannedDirs,
|
|
175
|
-
directoriesSkipped: skippedDirs,
|
|
176
|
-
timing: {
|
|
177
|
-
totalMs: tEnd - t0,
|
|
178
|
-
walkMs: tWalkEnd - tWalkStart, // includes metadata generation (interleaved)
|
|
179
|
-
embedMs: tEmbedEnd - tTfidfEnd,
|
|
180
|
-
tfidfMs: tTfidfEnd - tWalkEnd,
|
|
181
|
-
},
|
|
182
|
-
};
|
|
183
184
|
}
|
|
184
185
|
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
186
|
+
function getAllEntriesForEmbedding(db) {
|
|
187
|
+
return db
|
|
188
|
+
.prepare(`
|
|
189
|
+
SELECT e.id, e.search_text AS searchText FROM entries e
|
|
190
|
+
WHERE NOT EXISTS (SELECT 1 FROM entries_vec v WHERE v.id = e.id)
|
|
191
|
+
`)
|
|
192
|
+
.all();
|
|
193
|
+
}
|
|
185
194
|
function isDirStale(dirPath, currentFiles, previousEntries, builtAtMs) {
|
|
186
195
|
// Check if file set changed (additions or deletions)
|
|
187
196
|
const prevFileNames = new Set(previousEntries
|
|
@@ -244,7 +253,6 @@ async function enhanceStashWithLlm(llmConfig, stash, dirPath, files) {
|
|
|
244
253
|
const enhanced = [];
|
|
245
254
|
for (const entry of stash.entries) {
|
|
246
255
|
try {
|
|
247
|
-
// Find the file matching this entry for content context
|
|
248
256
|
const entryFile = entry.entry
|
|
249
257
|
? files.find((f) => path.basename(f) === entry.entry) ?? files[0]
|
|
250
258
|
: files[0];
|
|
@@ -266,7 +274,6 @@ async function enhanceStashWithLlm(llmConfig, stash, dirPath, files) {
|
|
|
266
274
|
enhanced.push(updated);
|
|
267
275
|
}
|
|
268
276
|
catch {
|
|
269
|
-
// LLM enhancement failed for this entry, keep original
|
|
270
277
|
enhanced.push(entry);
|
|
271
278
|
}
|
|
272
279
|
}
|
package/dist/src/init.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agentikit initialization logic.
|
|
3
3
|
*
|
|
4
|
-
* Creates the stash directory structure, sets the
|
|
4
|
+
* Creates the working stash directory structure, sets the AKM_STASH_DIR
|
|
5
5
|
* environment variable, and ensures ripgrep is available.
|
|
6
6
|
*/
|
|
7
7
|
export interface InitResponse {
|
|
@@ -16,4 +16,4 @@ export interface InitResponse {
|
|
|
16
16
|
version: string;
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
|
-
export declare function agentikitInit(): InitResponse
|
|
19
|
+
export declare function agentikitInit(): Promise<InitResponse>;
|
package/dist/src/init.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Agentikit initialization logic.
|
|
3
3
|
*
|
|
4
|
-
* Creates the stash directory structure, sets the
|
|
4
|
+
* Creates the working stash directory structure, sets the AKM_STASH_DIR
|
|
5
5
|
* environment variable, and ensures ripgrep is available.
|
|
6
6
|
*/
|
|
7
7
|
import { spawnSync } from "node:child_process";
|
|
@@ -10,7 +10,7 @@ import path from "node:path";
|
|
|
10
10
|
import { IS_WINDOWS, TYPE_DIRS } from "./common";
|
|
11
11
|
import { ensureRg } from "./ripgrep-install";
|
|
12
12
|
import { getConfigPath, saveConfig, DEFAULT_CONFIG } from "./config";
|
|
13
|
-
export function agentikitInit() {
|
|
13
|
+
export async function agentikitInit() {
|
|
14
14
|
let stashDir;
|
|
15
15
|
if (IS_WINDOWS) {
|
|
16
16
|
const userProfile = process.env.USERPROFILE?.trim();
|
|
@@ -41,7 +41,7 @@ export function agentikitInit() {
|
|
|
41
41
|
let envSet = false;
|
|
42
42
|
let profileUpdated;
|
|
43
43
|
if (IS_WINDOWS) {
|
|
44
|
-
const result = spawnSync("setx", ["
|
|
44
|
+
const result = spawnSync("setx", ["AKM_STASH_DIR", stashDir], {
|
|
45
45
|
encoding: "utf8",
|
|
46
46
|
timeout: 10_000,
|
|
47
47
|
});
|
|
@@ -60,20 +60,32 @@ export function agentikitInit() {
|
|
|
60
60
|
else {
|
|
61
61
|
profile = path.join(homeDir, ".profile");
|
|
62
62
|
}
|
|
63
|
-
const exportLine = `export
|
|
63
|
+
const exportLine = `export AKM_STASH_DIR="${stashDir}"`;
|
|
64
64
|
const existing = fs.existsSync(profile) ? fs.readFileSync(profile, "utf8") : "";
|
|
65
|
-
if (!existing.includes("
|
|
66
|
-
|
|
65
|
+
if (!existing.includes("AKM_STASH_DIR")) {
|
|
66
|
+
const updated = existing + `\n# Agentikit working stash directory\n${exportLine}\n`;
|
|
67
|
+
const tmpPath = profile + `.tmp.${process.pid}`;
|
|
68
|
+
try {
|
|
69
|
+
fs.writeFileSync(tmpPath, updated, "utf8");
|
|
70
|
+
fs.renameSync(tmpPath, profile);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
try {
|
|
74
|
+
fs.unlinkSync(tmpPath);
|
|
75
|
+
}
|
|
76
|
+
catch { /* ignore */ }
|
|
77
|
+
throw err;
|
|
78
|
+
}
|
|
67
79
|
envSet = true;
|
|
68
80
|
profileUpdated = profile;
|
|
69
81
|
}
|
|
70
82
|
}
|
|
71
83
|
// Create default config.json if it doesn't exist
|
|
72
|
-
const configPath = getConfigPath(
|
|
84
|
+
const configPath = getConfigPath();
|
|
73
85
|
if (!fs.existsSync(configPath)) {
|
|
74
|
-
saveConfig(DEFAULT_CONFIG
|
|
86
|
+
saveConfig(DEFAULT_CONFIG);
|
|
75
87
|
}
|
|
76
|
-
process.env.
|
|
88
|
+
process.env.AKM_STASH_DIR = stashDir;
|
|
77
89
|
// Ensure ripgrep is available (install to stash/bin if needed)
|
|
78
90
|
let ripgrep;
|
|
79
91
|
try {
|
package/dist/src/llm.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
+
import { fetchWithTimeout } from "./common";
|
|
1
2
|
async function chatCompletion(config, messages) {
|
|
2
3
|
const headers = { "Content-Type": "application/json" };
|
|
3
4
|
if (config.apiKey) {
|
|
4
5
|
headers["Authorization"] = `Bearer ${config.apiKey}`;
|
|
5
6
|
}
|
|
6
|
-
const response = await
|
|
7
|
+
const response = await fetchWithTimeout(config.endpoint, {
|
|
7
8
|
method: "POST",
|
|
8
9
|
headers,
|
|
9
10
|
body: JSON.stringify({
|
|
10
11
|
model: config.model,
|
|
11
12
|
messages,
|
|
12
|
-
temperature: 0.3,
|
|
13
|
-
max_tokens: 512,
|
|
13
|
+
temperature: config.temperature ?? 0.3,
|
|
14
|
+
max_tokens: config.maxTokens ?? 512,
|
|
14
15
|
}),
|
|
15
16
|
});
|
|
16
17
|
if (!response.ok) {
|
package/dist/src/metadata.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ export interface StashEntry {
|
|
|
20
20
|
source?: "package" | "frontmatter" | "comments" | "filename" | "manual" | "llm";
|
|
21
21
|
aliases?: string[];
|
|
22
22
|
toc?: TocHeading[];
|
|
23
|
+
usage?: string[];
|
|
23
24
|
}
|
|
24
25
|
export interface StashFile {
|
|
25
26
|
entries: StashEntry[];
|
|
@@ -29,7 +30,6 @@ export declare function loadStashFile(dirPath: string): StashFile | null;
|
|
|
29
30
|
export declare function writeStashFile(dirPath: string, stash: StashFile): void;
|
|
30
31
|
export declare function validateStashEntry(entry: unknown): StashEntry | null;
|
|
31
32
|
export declare function generateMetadata(dirPath: string, assetType: AgentikitAssetType, files: string[], typeRoot?: string): StashFile;
|
|
32
|
-
export declare function generateIntents(description: string, tags: string[], name: string): string[];
|
|
33
33
|
export declare function extractDescriptionFromComments(filePath: string): string | null;
|
|
34
34
|
export declare function extractFrontmatterDescription(filePath: string): string | null;
|
|
35
35
|
export declare function extractPackageMetadata(dirPath: string): {
|
package/dist/src/metadata.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { isAssetType } from "./common";
|
|
4
|
-
import {
|
|
4
|
+
import { isRelevantAssetFile, deriveCanonicalAssetName } from "./asset-spec";
|
|
5
5
|
import { parseFrontmatter, toStringOrUndefined } from "./frontmatter";
|
|
6
|
-
import {
|
|
6
|
+
import { tryGetHandler } from "./asset-type-handler";
|
|
7
7
|
// ── Load / Write ────────────────────────────────────────────────────────────
|
|
8
8
|
const STASH_FILENAME = ".stash.json";
|
|
9
9
|
export function stashFilePath(dirPath) {
|
|
@@ -94,8 +94,24 @@ export function validateStashEntry(entry) {
|
|
|
94
94
|
if (validated.length > 0)
|
|
95
95
|
result.toc = validated;
|
|
96
96
|
}
|
|
97
|
+
const usage = normalizeNonEmptyStringList(e.usage);
|
|
98
|
+
if (usage)
|
|
99
|
+
result.usage = usage;
|
|
97
100
|
return result;
|
|
98
101
|
}
|
|
102
|
+
function normalizeNonEmptyStringList(value) {
|
|
103
|
+
if (typeof value === "string") {
|
|
104
|
+
const trimmed = value.trim();
|
|
105
|
+
return trimmed ? [trimmed] : undefined;
|
|
106
|
+
}
|
|
107
|
+
if (!Array.isArray(value))
|
|
108
|
+
return undefined;
|
|
109
|
+
const filtered = value
|
|
110
|
+
.filter((item) => typeof item === "string")
|
|
111
|
+
.map((item) => item.trim())
|
|
112
|
+
.filter((item) => item.length > 0);
|
|
113
|
+
return filtered.length > 0 ? filtered : undefined;
|
|
114
|
+
}
|
|
99
115
|
// ── Metadata Generation ─────────────────────────────────────────────────────
|
|
100
116
|
export function generateMetadata(dirPath, assetType, files, typeRoot = dirPath) {
|
|
101
117
|
const entries = [];
|
|
@@ -137,26 +153,10 @@ export function generateMetadata(dirPath, assetType, files, typeRoot = dirPath)
|
|
|
137
153
|
entry.confidence = 0.9;
|
|
138
154
|
}
|
|
139
155
|
}
|
|
140
|
-
//
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
const toc = parseMarkdownToc(mdContent);
|
|
145
|
-
if (toc.headings.length > 0)
|
|
146
|
-
entry.toc = toc.headings;
|
|
147
|
-
}
|
|
148
|
-
catch {
|
|
149
|
-
// Non-fatal: skip TOC if file can't be read
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
// Priority 3: Code comments (for script files)
|
|
153
|
-
if (SCRIPT_EXTENSIONS.has(ext) && ext !== ".md") {
|
|
154
|
-
const commentDesc = extractDescriptionFromComments(file);
|
|
155
|
-
if (commentDesc && !entry.description) {
|
|
156
|
-
entry.description = commentDesc;
|
|
157
|
-
entry.source = "comments";
|
|
158
|
-
entry.confidence = 0.7;
|
|
159
|
-
}
|
|
156
|
+
// Type-specific metadata extraction (e.g. TOC for knowledge, comments for tools/scripts)
|
|
157
|
+
const handler = tryGetHandler(assetType);
|
|
158
|
+
if (handler?.extractTypeMetadata) {
|
|
159
|
+
handler.extractTypeMetadata(entry, file, ext);
|
|
160
160
|
}
|
|
161
161
|
// Priority 4: Filename heuristics (fallback)
|
|
162
162
|
if (!entry.description) {
|
|
@@ -198,48 +198,6 @@ function buildAliases(name, tags) {
|
|
|
198
198
|
aliases.add(tags.join(" "));
|
|
199
199
|
return Array.from(aliases);
|
|
200
200
|
}
|
|
201
|
-
// ── Intent Generation ────────────────────────────────────────────────────────
|
|
202
|
-
export function generateIntents(description, tags, name) {
|
|
203
|
-
const intents = new Set();
|
|
204
|
-
// Split name on separators to extract tokens and potential verb
|
|
205
|
-
const nameTokens = name
|
|
206
|
-
.replace(/[-_]+/g, " ")
|
|
207
|
-
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
208
|
-
.toLowerCase()
|
|
209
|
-
.trim()
|
|
210
|
-
.split(/\s+/)
|
|
211
|
-
.filter((t) => t.length > 1);
|
|
212
|
-
// Intent from name as phrase (e.g. "summarize diff")
|
|
213
|
-
const namePhrase = nameTokens.join(" ");
|
|
214
|
-
if (namePhrase.length > 2)
|
|
215
|
-
intents.add(namePhrase);
|
|
216
|
-
// Intent from description (lowercased)
|
|
217
|
-
const desc = description.toLowerCase().trim();
|
|
218
|
-
if (desc.length > 2)
|
|
219
|
-
intents.add(desc);
|
|
220
|
-
// Combine first name token (potential verb) with tags
|
|
221
|
-
// e.g. name "summarize-diff", tags ["git"] → "summarize git diff"
|
|
222
|
-
if (nameTokens.length >= 1 && tags.length > 0) {
|
|
223
|
-
const verb = nameTokens[0];
|
|
224
|
-
const rest = nameTokens.slice(1).join(" ");
|
|
225
|
-
for (const tag of tags) {
|
|
226
|
-
const tagLower = tag.toLowerCase();
|
|
227
|
-
// verb + tag + rest (e.g. "summarize git diff")
|
|
228
|
-
const parts = [verb, tagLower, rest].filter((p) => p.length > 0);
|
|
229
|
-
const phrase = parts.join(" ");
|
|
230
|
-
if (phrase !== namePhrase && phrase.length > 2)
|
|
231
|
-
intents.add(phrase);
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
// Join tag pairs (e.g. ["git", "diff"] → "git diff")
|
|
235
|
-
if (tags.length >= 2) {
|
|
236
|
-
const tagPhrase = tags.map((t) => t.toLowerCase()).join(" ");
|
|
237
|
-
if (tagPhrase.length > 2)
|
|
238
|
-
intents.add(tagPhrase);
|
|
239
|
-
}
|
|
240
|
-
// Cap at 8 intents
|
|
241
|
-
return Array.from(intents).slice(0, 8);
|
|
242
|
-
}
|
|
243
201
|
export function extractDescriptionFromComments(filePath) {
|
|
244
202
|
let content;
|
|
245
203
|
try {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { StashSource } from "./stash-source";
|
|
2
|
+
/**
|
|
3
|
+
* Given an origin string (from an AssetRef) and the full list of stash
|
|
4
|
+
* sources, return the subset of sources to search.
|
|
5
|
+
*
|
|
6
|
+
* Resolution order:
|
|
7
|
+
* 1. undefined → all sources (working → mounted → installed)
|
|
8
|
+
* 2. "local" → working stash only
|
|
9
|
+
* 3. exact match → installed source whose registryId matches verbatim
|
|
10
|
+
* 4. parsed match → parse origin as a registry ref, match by parsed ID
|
|
11
|
+
* 5. path match → mounted source whose path matches
|
|
12
|
+
* 6. empty → indicates a remote/uninstalled origin (caller decides)
|
|
13
|
+
*/
|
|
14
|
+
export declare function resolveSourcesForOrigin(origin: string | undefined, allSources: StashSource[]): StashSource[];
|
|
15
|
+
/**
|
|
16
|
+
* Check whether an origin refers to something that could be fetched remotely
|
|
17
|
+
* (i.e. it looks like a registry ref but isn't installed locally).
|
|
18
|
+
*/
|
|
19
|
+
export declare function isRemoteOrigin(origin: string, allSources: StashSource[]): boolean;
|