agentikit 0.0.9 → 0.0.13
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 +139 -208
- package/dist/index.d.ts +8 -2
- package/dist/index.js +4 -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 +201 -75
- 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 +19 -6
- package/dist/src/config.js +139 -29
- 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 +0 -1
- package/dist/src/metadata.js +6 -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 +2 -2
- package/dist/src/registry-install.js +142 -35
- package/dist/src/registry-resolve.js +90 -22
- package/dist/src/registry-search.d.ts +22 -0
- package/dist/src/registry-search.js +231 -97
- package/dist/src/registry-types.d.ts +9 -2
- package/dist/src/stash-add.js +4 -4
- 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.js +12 -12
- package/dist/src/stash-resolve.js +3 -0
- package/dist/src/stash-search.js +168 -164
- package/dist/src/stash-show.d.ts +1 -1
- package/dist/src/stash-show.js +28 -96
- package/dist/src/stash-source.d.ts +24 -0
- package/dist/src/stash-source.js +81 -0
- package/dist/src/stash-types.d.ts +14 -4
- package/dist/src/stash.d.ts +6 -0
- package/dist/src/stash.js +3 -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 +213 -82
- package/src/common.ts +23 -5
- package/src/config-cli.ts +499 -0
- package/src/config.ts +160 -38
- 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 +5 -65
- package/src/origin-resolve.ts +67 -0
- package/src/registry-install.ts +158 -42
- package/src/registry-resolve.ts +92 -23
- package/src/registry-search.ts +288 -98
- package/src/registry-types.ts +10 -2
- package/src/stash-add.ts +14 -17
- package/src/stash-clone.ts +127 -0
- package/src/stash-ref.ts +84 -26
- package/src/stash-registry.ts +12 -12
- package/src/stash-resolve.ts +3 -0
- package/src/stash-search.ts +202 -184
- package/src/stash-show.ts +33 -90
- package/src/stash-source.ts +103 -0
- package/src/stash-types.ts +14 -4
- package/src/stash.ts +8 -0
- package/src/tool-runner.ts +18 -5
- package/dist/src/similarity.d.ts +0 -34
- package/dist/src/similarity.js +0 -211
- 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
|
@@ -30,7 +30,6 @@ export declare function loadStashFile(dirPath: string): StashFile | null;
|
|
|
30
30
|
export declare function writeStashFile(dirPath: string, stash: StashFile): void;
|
|
31
31
|
export declare function validateStashEntry(entry: unknown): StashEntry | null;
|
|
32
32
|
export declare function generateMetadata(dirPath: string, assetType: AgentikitAssetType, files: string[], typeRoot?: string): StashFile;
|
|
33
|
-
export declare function generateIntents(description: string, tags: string[], name: string): string[];
|
|
34
33
|
export declare function extractDescriptionFromComments(filePath: string): string | null;
|
|
35
34
|
export declare function extractFrontmatterDescription(filePath: string): string | null;
|
|
36
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) {
|
|
@@ -153,26 +153,10 @@ export function generateMetadata(dirPath, assetType, files, typeRoot = dirPath)
|
|
|
153
153
|
entry.confidence = 0.9;
|
|
154
154
|
}
|
|
155
155
|
}
|
|
156
|
-
//
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
const toc = parseMarkdownToc(mdContent);
|
|
161
|
-
if (toc.headings.length > 0)
|
|
162
|
-
entry.toc = toc.headings;
|
|
163
|
-
}
|
|
164
|
-
catch {
|
|
165
|
-
// Non-fatal: skip TOC if file can't be read
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
// Priority 3: Code comments (for script files)
|
|
169
|
-
if (SCRIPT_EXTENSIONS.has(ext) && ext !== ".md") {
|
|
170
|
-
const commentDesc = extractDescriptionFromComments(file);
|
|
171
|
-
if (commentDesc && !entry.description) {
|
|
172
|
-
entry.description = commentDesc;
|
|
173
|
-
entry.source = "comments";
|
|
174
|
-
entry.confidence = 0.7;
|
|
175
|
-
}
|
|
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);
|
|
176
160
|
}
|
|
177
161
|
// Priority 4: Filename heuristics (fallback)
|
|
178
162
|
if (!entry.description) {
|
|
@@ -214,48 +198,6 @@ function buildAliases(name, tags) {
|
|
|
214
198
|
aliases.add(tags.join(" "));
|
|
215
199
|
return Array.from(aliases);
|
|
216
200
|
}
|
|
217
|
-
// ── Intent Generation ────────────────────────────────────────────────────────
|
|
218
|
-
export function generateIntents(description, tags, name) {
|
|
219
|
-
const intents = new Set();
|
|
220
|
-
// Split name on separators to extract tokens and potential verb
|
|
221
|
-
const nameTokens = name
|
|
222
|
-
.replace(/[-_]+/g, " ")
|
|
223
|
-
.replace(/([a-z])([A-Z])/g, "$1 $2")
|
|
224
|
-
.toLowerCase()
|
|
225
|
-
.trim()
|
|
226
|
-
.split(/\s+/)
|
|
227
|
-
.filter((t) => t.length > 1);
|
|
228
|
-
// Intent from name as phrase (e.g. "summarize diff")
|
|
229
|
-
const namePhrase = nameTokens.join(" ");
|
|
230
|
-
if (namePhrase.length > 2)
|
|
231
|
-
intents.add(namePhrase);
|
|
232
|
-
// Intent from description (lowercased)
|
|
233
|
-
const desc = description.toLowerCase().trim();
|
|
234
|
-
if (desc.length > 2)
|
|
235
|
-
intents.add(desc);
|
|
236
|
-
// Combine first name token (potential verb) with tags
|
|
237
|
-
// e.g. name "summarize-diff", tags ["git"] → "summarize git diff"
|
|
238
|
-
if (nameTokens.length >= 1 && tags.length > 0) {
|
|
239
|
-
const verb = nameTokens[0];
|
|
240
|
-
const rest = nameTokens.slice(1).join(" ");
|
|
241
|
-
for (const tag of tags) {
|
|
242
|
-
const tagLower = tag.toLowerCase();
|
|
243
|
-
// verb + tag + rest (e.g. "summarize git diff")
|
|
244
|
-
const parts = [verb, tagLower, rest].filter((p) => p.length > 0);
|
|
245
|
-
const phrase = parts.join(" ");
|
|
246
|
-
if (phrase !== namePhrase && phrase.length > 2)
|
|
247
|
-
intents.add(phrase);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
// Join tag pairs (e.g. ["git", "diff"] → "git diff")
|
|
251
|
-
if (tags.length >= 2) {
|
|
252
|
-
const tagPhrase = tags.map((t) => t.toLowerCase()).join(" ");
|
|
253
|
-
if (tagPhrase.length > 2)
|
|
254
|
-
intents.add(tagPhrase);
|
|
255
|
-
}
|
|
256
|
-
// Cap at 8 intents
|
|
257
|
-
return Array.from(intents).slice(0, 8);
|
|
258
|
-
}
|
|
259
201
|
export function extractDescriptionFromComments(filePath) {
|
|
260
202
|
let content;
|
|
261
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;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { parseRegistryRef } from "./registry-resolve";
|
|
3
|
+
/**
|
|
4
|
+
* Given an origin string (from an AssetRef) and the full list of stash
|
|
5
|
+
* sources, return the subset of sources to search.
|
|
6
|
+
*
|
|
7
|
+
* Resolution order:
|
|
8
|
+
* 1. undefined → all sources (working → mounted → installed)
|
|
9
|
+
* 2. "local" → working stash only
|
|
10
|
+
* 3. exact match → installed source whose registryId matches verbatim
|
|
11
|
+
* 4. parsed match → parse origin as a registry ref, match by parsed ID
|
|
12
|
+
* 5. path match → mounted source whose path matches
|
|
13
|
+
* 6. empty → indicates a remote/uninstalled origin (caller decides)
|
|
14
|
+
*/
|
|
15
|
+
export function resolveSourcesForOrigin(origin, allSources) {
|
|
16
|
+
if (!origin)
|
|
17
|
+
return allSources;
|
|
18
|
+
if (origin === "local") {
|
|
19
|
+
return allSources.filter((s) => s.kind === "working");
|
|
20
|
+
}
|
|
21
|
+
// Exact registryId match (e.g. origin is "npm:@scope/pkg")
|
|
22
|
+
const byExactId = allSources.filter((s) => s.kind === "installed" && s.registryId === origin);
|
|
23
|
+
if (byExactId.length > 0)
|
|
24
|
+
return byExactId;
|
|
25
|
+
// Parse origin as a registry ref and match by parsed ID.
|
|
26
|
+
// Allows shorthand: "owner/repo" matches "github:owner/repo",
|
|
27
|
+
// "@scope/pkg" matches "npm:@scope/pkg".
|
|
28
|
+
try {
|
|
29
|
+
const parsed = parseRegistryRef(origin);
|
|
30
|
+
const byParsedId = allSources.filter((s) => s.kind === "installed" && s.registryId === parsed.id);
|
|
31
|
+
if (byParsedId.length > 0)
|
|
32
|
+
return byParsedId;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// Not a valid registry ref — continue to path matching
|
|
36
|
+
}
|
|
37
|
+
// Mounted stash by resolved path
|
|
38
|
+
const resolvedOrigin = path.resolve(origin);
|
|
39
|
+
const byPath = allSources.filter((s) => s.kind === "mounted" && path.resolve(s.path) === resolvedOrigin);
|
|
40
|
+
if (byPath.length > 0)
|
|
41
|
+
return byPath;
|
|
42
|
+
// No match — origin may be remote/uninstalled
|
|
43
|
+
return [];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Check whether an origin refers to something that could be fetched remotely
|
|
47
|
+
* (i.e. it looks like a registry ref but isn't installed locally).
|
|
48
|
+
*/
|
|
49
|
+
export function isRemoteOrigin(origin, allSources) {
|
|
50
|
+
if (origin === "local")
|
|
51
|
+
return false;
|
|
52
|
+
return resolveSourcesForOrigin(origin, allSources).length === 0;
|
|
53
|
+
}
|
|
@@ -5,7 +5,7 @@ export interface InstallRegistryRefOptions {
|
|
|
5
5
|
now?: Date;
|
|
6
6
|
}
|
|
7
7
|
export declare function installRegistryRef(ref: string, options?: InstallRegistryRefOptions): Promise<RegistryInstallResult>;
|
|
8
|
-
export declare function upsertInstalledRegistryEntry(entry: RegistryInstalledEntry
|
|
9
|
-
export declare function removeInstalledRegistryEntry(id: string
|
|
8
|
+
export declare function upsertInstalledRegistryEntry(entry: RegistryInstalledEntry): AgentikitConfig;
|
|
9
|
+
export declare function removeInstalledRegistryEntry(id: string): AgentikitConfig;
|
|
10
10
|
export declare function getRegistryCacheRootDir(): string;
|
|
11
11
|
export declare function detectStashRoot(extractedDir: string): string;
|