agentikit 0.0.14 → 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/README.md +14 -7
- package/dist/asset-spec.js +11 -2
- package/dist/asset-type-handler.js +4 -3
- package/dist/cli.js +100 -62
- package/dist/common.js +6 -6
- package/dist/config-cli.js +23 -25
- package/dist/config.js +3 -1
- package/dist/db.js +28 -30
- package/dist/errors.js +28 -0
- package/dist/file-context.js +36 -6
- package/dist/frontmatter.js +1 -1
- package/dist/github.js +1 -3
- package/dist/handlers/agent-handler.js +6 -13
- package/dist/handlers/command-handler.js +8 -13
- package/dist/handlers/handler-bridge.js +51 -0
- package/dist/handlers/index.js +12 -10
- package/dist/handlers/knowledge-handler.js +7 -31
- package/dist/handlers/script-handler.js +9 -45
- package/dist/handlers/skill-handler.js +5 -6
- package/dist/handlers/tool-handler.js +8 -24
- package/dist/indexer.js +21 -21
- package/dist/init.js +3 -3
- package/dist/llm.js +6 -11
- package/dist/lockfile.js +9 -4
- package/dist/matchers.js +11 -5
- package/dist/metadata.js +25 -16
- package/dist/paths.js +5 -4
- package/dist/registry-install.js +9 -5
- package/dist/registry-resolve.js +12 -8
- package/dist/registry-search.js +5 -5
- package/dist/renderers.js +24 -14
- package/dist/ripgrep-install.js +3 -22
- package/dist/ripgrep.js +1 -1
- package/dist/self-update.js +15 -9
- package/dist/stash-add.js +4 -3
- package/dist/stash-clone.js +4 -4
- package/dist/stash-ref.js +10 -9
- package/dist/stash-registry.js +6 -5
- package/dist/stash-resolve.js +10 -9
- package/dist/stash-search.js +27 -24
- package/dist/stash-show.js +8 -7
- package/dist/stash-source.js +10 -11
- package/dist/submit.js +26 -21
- package/dist/tool-runner.js +1 -5
- package/dist/warn.js +20 -0
- package/package.json +7 -3
package/dist/indexer.js
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { ASSET_TYPES, deriveCanonicalAssetName, TYPE_DIRS } from "./asset-spec";
|
|
3
4
|
import { resolveStashDir } from "./common";
|
|
4
|
-
import {
|
|
5
|
-
import { loadStashFile, writeStashFile
|
|
5
|
+
import { closeDatabase, DB_VERSION, deleteEntriesByDir, getEntriesByDir, getEntryCount, getMeta, isVecAvailable, openDatabase, rebuildFts, setMeta, upsertEmbedding, upsertEntry, warnIfVecMissing, } from "./db";
|
|
6
|
+
import { generateMetadata, loadStashFile, writeStashFile } from "./metadata";
|
|
7
|
+
import { getDbPath } from "./paths";
|
|
6
8
|
import { walkStash } from "./walker";
|
|
7
|
-
import {
|
|
9
|
+
import { warn } from "./warn";
|
|
8
10
|
// ── Indexer ──────────────────────────────────────────────────────────────────
|
|
9
11
|
export async function agentikitIndex(options) {
|
|
10
12
|
const stashDir = options?.stashDir || resolveStashDir();
|
|
@@ -30,12 +32,16 @@ export async function agentikitIndex(options) {
|
|
|
30
32
|
try {
|
|
31
33
|
db.exec("DELETE FROM embeddings");
|
|
32
34
|
}
|
|
33
|
-
catch {
|
|
35
|
+
catch {
|
|
36
|
+
/* ignore */
|
|
37
|
+
}
|
|
34
38
|
if (isVecAvailable(db)) {
|
|
35
39
|
try {
|
|
36
40
|
db.exec("DELETE FROM entries_vec");
|
|
37
41
|
}
|
|
38
|
-
catch {
|
|
42
|
+
catch {
|
|
43
|
+
/* ignore */
|
|
44
|
+
}
|
|
39
45
|
}
|
|
40
46
|
db.exec("DELETE FROM entries_fts");
|
|
41
47
|
db.exec("DELETE FROM entries");
|
|
@@ -126,9 +132,7 @@ function indexEntries(db, allStashDirs, stashDir, isIncremental, builtAtMs) {
|
|
|
126
132
|
}
|
|
127
133
|
// Check for files on disk that aren't covered by existing .stash.json entries.
|
|
128
134
|
// This handles the case where new files are added after the initial index.
|
|
129
|
-
const coveredFiles = new Set(stash.entries
|
|
130
|
-
.map((e) => e.entry)
|
|
131
|
-
.filter((e) => !!e));
|
|
135
|
+
const coveredFiles = new Set(stash.entries.map((e) => e.entry).filter((e) => !!e));
|
|
132
136
|
const uncoveredFiles = files.filter((f) => !coveredFiles.has(path.basename(f)));
|
|
133
137
|
if (uncoveredFiles.length > 0) {
|
|
134
138
|
const generated = generateMetadata(dirPath, assetType, uncoveredFiles, typeRoot);
|
|
@@ -149,9 +153,7 @@ function indexEntries(db, allStashDirs, stashDir, isIncremental, builtAtMs) {
|
|
|
149
153
|
}
|
|
150
154
|
if (stash) {
|
|
151
155
|
for (const entry of stash.entries) {
|
|
152
|
-
const entryPath = entry.entry
|
|
153
|
-
? path.join(dirPath, entry.entry)
|
|
154
|
-
: files[0] || dirPath;
|
|
156
|
+
const entryPath = entry.entry ? path.join(dirPath, entry.entry) : files[0] || dirPath;
|
|
155
157
|
const entryKey = `${currentStashDir}:${entry.type}:${entry.name}`;
|
|
156
158
|
const searchText = buildSearchText(entry);
|
|
157
159
|
upsertEntry(db, entryKey, dirPath, entryPath, currentStashDir, entry, searchText);
|
|
@@ -202,7 +204,7 @@ async function generateEmbeddingsForDb(db, config) {
|
|
|
202
204
|
return true;
|
|
203
205
|
}
|
|
204
206
|
catch (error) {
|
|
205
|
-
|
|
207
|
+
warn("Embedding generation failed, continuing without:", error instanceof Error ? error.message : String(error));
|
|
206
208
|
return false;
|
|
207
209
|
}
|
|
208
210
|
}
|
|
@@ -217,9 +219,7 @@ function getAllEntriesForEmbedding(db) {
|
|
|
217
219
|
}
|
|
218
220
|
function isDirStale(dirPath, currentFiles, previousEntries, builtAtMs) {
|
|
219
221
|
// Check if file set changed (additions or deletions)
|
|
220
|
-
const prevFileNames = new Set(previousEntries
|
|
221
|
-
.map((ie) => ie.entry.entry)
|
|
222
|
-
.filter((e) => !!e));
|
|
222
|
+
const prevFileNames = new Set(previousEntries.map((ie) => ie.entry.entry).filter((e) => !!e));
|
|
223
223
|
const currFileNames = new Set(currentFiles.map((f) => path.basename(f)));
|
|
224
224
|
if (prevFileNames.size !== currFileNames.size)
|
|
225
225
|
return true;
|
|
@@ -240,11 +240,11 @@ function isDirStale(dirPath, currentFiles, previousEntries, builtAtMs) {
|
|
|
240
240
|
// Check .stash.json modification time
|
|
241
241
|
const stashPath = path.join(dirPath, ".stash.json");
|
|
242
242
|
try {
|
|
243
|
-
if (fs.
|
|
243
|
+
if (fs.statSync(stashPath).mtimeMs > builtAtMs)
|
|
244
244
|
return true;
|
|
245
245
|
}
|
|
246
246
|
catch {
|
|
247
|
-
//
|
|
247
|
+
// file doesn't exist, not stale
|
|
248
248
|
}
|
|
249
249
|
return false;
|
|
250
250
|
}
|
|
@@ -277,15 +277,15 @@ async function enhanceStashWithLlm(llmConfig, stash, dirPath, files) {
|
|
|
277
277
|
const enhanced = [];
|
|
278
278
|
for (const entry of stash.entries) {
|
|
279
279
|
try {
|
|
280
|
-
const entryFile = entry.entry
|
|
281
|
-
? files.find((f) => path.basename(f) === entry.entry) ?? files[0]
|
|
282
|
-
: files[0];
|
|
280
|
+
const entryFile = entry.entry ? (files.find((f) => path.basename(f) === entry.entry) ?? files[0]) : files[0];
|
|
283
281
|
let fileContent;
|
|
284
282
|
if (entryFile) {
|
|
285
283
|
try {
|
|
286
284
|
fileContent = fs.readFileSync(entryFile, "utf8");
|
|
287
285
|
}
|
|
288
|
-
catch {
|
|
286
|
+
catch {
|
|
287
|
+
/* ignore unreadable files */
|
|
288
|
+
}
|
|
289
289
|
}
|
|
290
290
|
const improvements = await enhanceMetadata(llmConfig, entry, fileContent);
|
|
291
291
|
const updated = { ...entry };
|
package/dist/init.js
CHANGED
|
@@ -6,10 +6,10 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import fs from "node:fs";
|
|
8
8
|
import path from "node:path";
|
|
9
|
-
import { TYPE_DIRS } from "./
|
|
9
|
+
import { TYPE_DIRS } from "./asset-spec";
|
|
10
|
+
import { getConfigPath, loadConfig, saveConfig } from "./config";
|
|
11
|
+
import { getBinDir, getDefaultStashDir } from "./paths";
|
|
10
12
|
import { ensureRg } from "./ripgrep-install";
|
|
11
|
-
import { loadConfig, saveConfig, getConfigPath } from "./config";
|
|
12
|
-
import { getDefaultStashDir, getBinDir } from "./paths";
|
|
13
13
|
export async function agentikitInit(options) {
|
|
14
14
|
const stashDir = options?.dir ? path.resolve(options.dir) : getDefaultStashDir();
|
|
15
15
|
let created = false;
|
package/dist/llm.js
CHANGED
|
@@ -28,19 +28,14 @@ const SYSTEM_PROMPT = `You are a metadata generator for a developer tool registr
|
|
|
28
28
|
* generate intents, and suggest tags.
|
|
29
29
|
*/
|
|
30
30
|
export async function enhanceMetadata(config, entry, fileContent) {
|
|
31
|
-
const contextParts = [
|
|
32
|
-
`Name: ${entry.name}`,
|
|
33
|
-
`Type: ${entry.type}`,
|
|
34
|
-
];
|
|
31
|
+
const contextParts = [`Name: ${entry.name}`, `Type: ${entry.type}`];
|
|
35
32
|
if (entry.description)
|
|
36
33
|
contextParts.push(`Current description: ${entry.description}`);
|
|
37
34
|
if (entry.tags?.length)
|
|
38
35
|
contextParts.push(`Current tags: ${entry.tags.join(", ")}`);
|
|
39
36
|
if (fileContent) {
|
|
40
37
|
// Limit content to first 2000 chars to stay within token limits
|
|
41
|
-
const truncated = fileContent.length > 2000
|
|
42
|
-
? fileContent.slice(0, 2000) + "\n... (truncated)"
|
|
43
|
-
: fileContent;
|
|
38
|
+
const truncated = fileContent.length > 2000 ? fileContent.slice(0, 2000) + "\n... (truncated)" : fileContent;
|
|
44
39
|
contextParts.push(`File content:\n${truncated}`);
|
|
45
40
|
}
|
|
46
41
|
const userPrompt = `${contextParts.join("\n")}
|
|
@@ -64,7 +59,9 @@ Return ONLY the JSON object, no explanation.`;
|
|
|
64
59
|
result.description = parsed.description;
|
|
65
60
|
}
|
|
66
61
|
if (Array.isArray(parsed.intents)) {
|
|
67
|
-
result.intents = parsed.intents
|
|
62
|
+
result.intents = parsed.intents
|
|
63
|
+
.filter((s) => typeof s === "string" && s.trim().length > 0)
|
|
64
|
+
.slice(0, 8);
|
|
68
65
|
}
|
|
69
66
|
if (Array.isArray(parsed.tags)) {
|
|
70
67
|
result.tags = parsed.tags.filter((s) => typeof s === "string" && s.trim().length > 0).slice(0, 10);
|
|
@@ -81,9 +78,7 @@ Return ONLY the JSON object, no explanation.`;
|
|
|
81
78
|
*/
|
|
82
79
|
export async function isLlmAvailable(config) {
|
|
83
80
|
try {
|
|
84
|
-
const result = await chatCompletion(config, [
|
|
85
|
-
{ role: "user", content: "Respond with just the word: ok" },
|
|
86
|
-
]);
|
|
81
|
+
const result = await chatCompletion(config, [{ role: "user", content: "Respond with just the word: ok" }]);
|
|
87
82
|
return result.length > 0;
|
|
88
83
|
}
|
|
89
84
|
catch {
|
package/dist/lockfile.js
CHANGED
|
@@ -31,7 +31,9 @@ export function writeLockfile(entries) {
|
|
|
31
31
|
try {
|
|
32
32
|
fs.unlinkSync(tmpPath);
|
|
33
33
|
}
|
|
34
|
-
catch {
|
|
34
|
+
catch {
|
|
35
|
+
/* ignore cleanup failure */
|
|
36
|
+
}
|
|
35
37
|
throw err;
|
|
36
38
|
}
|
|
37
39
|
}
|
|
@@ -49,7 +51,10 @@ function isValidLockfileEntry(value) {
|
|
|
49
51
|
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
50
52
|
return false;
|
|
51
53
|
const obj = value;
|
|
52
|
-
return (typeof obj.id === "string" &&
|
|
53
|
-
|
|
54
|
-
typeof obj.
|
|
54
|
+
return (typeof obj.id === "string" &&
|
|
55
|
+
obj.id !== "" &&
|
|
56
|
+
typeof obj.source === "string" &&
|
|
57
|
+
["npm", "github", "git", "local"].includes(obj.source) &&
|
|
58
|
+
typeof obj.ref === "string" &&
|
|
59
|
+
obj.ref !== "");
|
|
55
60
|
}
|
package/dist/matchers.js
CHANGED
|
@@ -150,8 +150,14 @@ export function smartMdMatcher(ctx) {
|
|
|
150
150
|
return { type: "knowledge", specificity: 5, renderer: "knowledge-md" };
|
|
151
151
|
}
|
|
152
152
|
// ── Registration ────────────────────────────────────────────────────────────
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
153
|
+
/** All built-in matchers in registration order (later wins ties). */
|
|
154
|
+
const builtinMatchers = [extensionMatcher, directoryMatcher, parentDirHintMatcher, smartMdMatcher];
|
|
155
|
+
/**
|
|
156
|
+
* Register all built-in matchers with the file-context registry.
|
|
157
|
+
* Called once from the CLI entry point (or ensureBuiltinsRegistered).
|
|
158
|
+
*/
|
|
159
|
+
export function registerBuiltinMatchers() {
|
|
160
|
+
for (const matcher of builtinMatchers) {
|
|
161
|
+
registerMatcher(matcher);
|
|
162
|
+
}
|
|
163
|
+
}
|
package/dist/metadata.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import fs from "node:fs";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { deriveCanonicalAssetName, isRelevantAssetFile } from "./asset-spec";
|
|
4
|
+
import { tryGetHandler } from "./asset-type-handler";
|
|
3
5
|
import { isAssetType } from "./common";
|
|
4
|
-
import { isRelevantAssetFile, deriveCanonicalAssetName } from "./asset-spec";
|
|
5
6
|
import { parseFrontmatter, toStringOrUndefined } from "./frontmatter";
|
|
6
|
-
import {
|
|
7
|
+
import { warn } from "./warn";
|
|
7
8
|
// ── Load / Write ────────────────────────────────────────────────────────────
|
|
8
9
|
const STASH_FILENAME = ".stash.json";
|
|
9
10
|
export function stashFilePath(dirPath) {
|
|
@@ -20,8 +21,15 @@ export function loadStashFile(dirPath) {
|
|
|
20
21
|
const entries = [];
|
|
21
22
|
for (const e of raw.entries) {
|
|
22
23
|
const validated = validateStashEntry(e);
|
|
23
|
-
if (validated)
|
|
24
|
+
if (validated) {
|
|
24
25
|
entries.push(validated);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
const name = typeof e === "object" && e !== null && typeof e.name === "string"
|
|
29
|
+
? e.name
|
|
30
|
+
: "(unknown)";
|
|
31
|
+
warn(`Warning: Skipping invalid entry "${name}" in ${filePath}`);
|
|
32
|
+
}
|
|
25
33
|
}
|
|
26
34
|
return entries.length > 0 ? { entries } : null;
|
|
27
35
|
}
|
|
@@ -40,7 +48,9 @@ export function writeStashFile(dirPath, stash) {
|
|
|
40
48
|
try {
|
|
41
49
|
fs.unlinkSync(tmpPath);
|
|
42
50
|
}
|
|
43
|
-
catch {
|
|
51
|
+
catch {
|
|
52
|
+
/* ignore cleanup failure */
|
|
53
|
+
}
|
|
44
54
|
throw err;
|
|
45
55
|
}
|
|
46
56
|
}
|
|
@@ -85,7 +95,8 @@ export function validateStashEntry(entry) {
|
|
|
85
95
|
result.quality = e.quality;
|
|
86
96
|
if (typeof e.confidence === "number" && Number.isFinite(e.confidence))
|
|
87
97
|
result.confidence = Math.max(0, Math.min(1, e.confidence));
|
|
88
|
-
if (typeof e.source === "string" &&
|
|
98
|
+
if (typeof e.source === "string" &&
|
|
99
|
+
["package", "frontmatter", "comments", "filename", "manual", "llm"].includes(e.source)) {
|
|
89
100
|
result.source = e.source;
|
|
90
101
|
}
|
|
91
102
|
if (Array.isArray(e.aliases)) {
|
|
@@ -98,9 +109,7 @@ export function validateStashEntry(entry) {
|
|
|
98
109
|
if (typeof h !== "object" || h === null)
|
|
99
110
|
return false;
|
|
100
111
|
const rec = h;
|
|
101
|
-
return typeof rec.level === "number"
|
|
102
|
-
&& typeof rec.text === "string"
|
|
103
|
-
&& typeof rec.line === "number";
|
|
112
|
+
return typeof rec.level === "number" && typeof rec.text === "string" && typeof rec.line === "number";
|
|
104
113
|
});
|
|
105
114
|
if (validated.length > 0)
|
|
106
115
|
result.toc = validated;
|
|
@@ -134,9 +143,7 @@ export function generateMetadata(dirPath, assetType, files, typeRoot = dirPath)
|
|
|
134
143
|
// Skip non-relevant files
|
|
135
144
|
if (!isRelevantAssetFile(assetType, fileName))
|
|
136
145
|
continue;
|
|
137
|
-
const canonicalName = assetType === "skill"
|
|
138
|
-
? deriveCanonicalAssetName(assetType, typeRoot, file) ?? baseName
|
|
139
|
-
: baseName;
|
|
146
|
+
const canonicalName = assetType === "skill" ? (deriveCanonicalAssetName(assetType, typeRoot, file) ?? baseName) : baseName;
|
|
140
147
|
const entry = {
|
|
141
148
|
name: canonicalName,
|
|
142
149
|
type: assetType,
|
|
@@ -145,7 +152,7 @@ export function generateMetadata(dirPath, assetType, files, typeRoot = dirPath)
|
|
|
145
152
|
confidence: 0.55,
|
|
146
153
|
source: "filename",
|
|
147
154
|
};
|
|
148
|
-
// Priority 1:
|
|
155
|
+
// Priority 1: Package.json metadata
|
|
149
156
|
if (pkgMeta) {
|
|
150
157
|
if (pkgMeta.description && !entry.description) {
|
|
151
158
|
entry.description = pkgMeta.description;
|
|
@@ -155,7 +162,7 @@ export function generateMetadata(dirPath, assetType, files, typeRoot = dirPath)
|
|
|
155
162
|
if (pkgMeta.keywords && pkgMeta.keywords.length > 0)
|
|
156
163
|
entry.tags = normalizeTerms(pkgMeta.keywords);
|
|
157
164
|
}
|
|
158
|
-
// Priority 2: Frontmatter (for .md files
|
|
165
|
+
// Priority 2: Frontmatter (for .md files -- overrides package.json description)
|
|
159
166
|
if (ext === ".md") {
|
|
160
167
|
const fm = extractFrontmatterDescription(file);
|
|
161
168
|
if (fm) {
|
|
@@ -164,7 +171,7 @@ export function generateMetadata(dirPath, assetType, files, typeRoot = dirPath)
|
|
|
164
171
|
entry.confidence = 0.9;
|
|
165
172
|
}
|
|
166
173
|
}
|
|
167
|
-
// Type-specific metadata extraction (e.g. TOC for knowledge, comments for tools/scripts)
|
|
174
|
+
// Priority 3: Type-specific metadata extraction (e.g. TOC for knowledge, comments for tools/scripts)
|
|
168
175
|
const handler = tryGetHandler(assetType);
|
|
169
176
|
if (handler?.extractTypeMetadata) {
|
|
170
177
|
handler.extractTypeMetadata(entry, file, ext);
|
|
@@ -226,7 +233,10 @@ export function extractDescriptionFromComments(filePath) {
|
|
|
226
233
|
const line = lines[i];
|
|
227
234
|
if (i > blockStart && /\*\//.test(line))
|
|
228
235
|
break;
|
|
229
|
-
const cleaned = line
|
|
236
|
+
const cleaned = line
|
|
237
|
+
.replace(/^\s*\/?\*\*?\s?/, "")
|
|
238
|
+
.replace(/\*\/\s*$/, "")
|
|
239
|
+
.trim();
|
|
230
240
|
if (cleaned)
|
|
231
241
|
desc.push(cleaned);
|
|
232
242
|
}
|
|
@@ -244,7 +254,6 @@ export function extractDescriptionFromComments(filePath) {
|
|
|
244
254
|
hashLines.push(line.replace(/^#+\s*/, "").trim());
|
|
245
255
|
}
|
|
246
256
|
else if (line === "") {
|
|
247
|
-
continue;
|
|
248
257
|
}
|
|
249
258
|
else {
|
|
250
259
|
break;
|
package/dist/paths.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* on Windows.
|
|
7
7
|
*/
|
|
8
8
|
import path from "node:path";
|
|
9
|
+
import { ConfigError } from "./errors";
|
|
9
10
|
const IS_WINDOWS = process.platform === "win32";
|
|
10
11
|
// ── Config directory ─────────────────────────────────────────────────────────
|
|
11
12
|
export function getConfigDir(env = process.env, platform = process.platform) {
|
|
@@ -15,7 +16,7 @@ export function getConfigDir(env = process.env, platform = process.platform) {
|
|
|
15
16
|
return path.join(appData, "agentikit");
|
|
16
17
|
const userProfile = env.USERPROFILE?.trim();
|
|
17
18
|
if (!userProfile) {
|
|
18
|
-
throw new
|
|
19
|
+
throw new ConfigError("Unable to determine config directory. Set APPDATA or USERPROFILE.");
|
|
19
20
|
}
|
|
20
21
|
return path.join(userProfile, "AppData", "Roaming", "agentikit");
|
|
21
22
|
}
|
|
@@ -24,7 +25,7 @@ export function getConfigDir(env = process.env, platform = process.platform) {
|
|
|
24
25
|
return path.join(xdgConfigHome, "agentikit");
|
|
25
26
|
const home = env.HOME?.trim();
|
|
26
27
|
if (!home) {
|
|
27
|
-
throw new
|
|
28
|
+
throw new ConfigError("Unable to determine config directory. Set XDG_CONFIG_HOME or HOME.");
|
|
28
29
|
}
|
|
29
30
|
return path.join(home, ".config", "agentikit");
|
|
30
31
|
}
|
|
@@ -42,7 +43,7 @@ export function getCacheDir() {
|
|
|
42
43
|
return path.join(userProfile, "AppData", "Local", "agentikit");
|
|
43
44
|
const appData = process.env.APPDATA?.trim();
|
|
44
45
|
if (!appData) {
|
|
45
|
-
throw new
|
|
46
|
+
throw new ConfigError("Unable to determine cache directory. Set LOCALAPPDATA, USERPROFILE, or APPDATA.");
|
|
46
47
|
}
|
|
47
48
|
return path.join(appData, "..", "Local", "agentikit");
|
|
48
49
|
}
|
|
@@ -76,7 +77,7 @@ export function getDefaultStashDir() {
|
|
|
76
77
|
}
|
|
77
78
|
const home = process.env.HOME?.trim();
|
|
78
79
|
if (!home) {
|
|
79
|
-
throw new
|
|
80
|
+
throw new ConfigError("Unable to determine default stash directory. Set HOME.");
|
|
80
81
|
}
|
|
81
82
|
return path.join(home, "agentikit");
|
|
82
83
|
}
|
package/dist/registry-install.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
1
|
import { spawnSync } from "node:child_process";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
3
3
|
import fs from "node:fs";
|
|
4
4
|
import path from "node:path";
|
|
5
|
-
import {
|
|
5
|
+
import { TYPE_DIRS } from "./asset-spec";
|
|
6
|
+
import { fetchWithRetry, isWithin } from "./common";
|
|
6
7
|
import { loadConfig, saveConfig } from "./config";
|
|
7
|
-
import { parseRegistryRef, resolveRegistryArtifact } from "./registry-resolve";
|
|
8
8
|
import { getRegistryCacheDir as _getRegistryCacheDir } from "./paths";
|
|
9
|
+
import { parseRegistryRef, resolveRegistryArtifact } from "./registry-resolve";
|
|
9
10
|
const REGISTRY_STASH_DIR_NAMES = new Set(Object.values(TYPE_DIRS));
|
|
10
11
|
export async function installRegistryRef(ref, options) {
|
|
11
12
|
const parsed = parseRegistryRef(ref);
|
|
@@ -231,7 +232,8 @@ async function downloadArchive(url, destination) {
|
|
|
231
232
|
}
|
|
232
233
|
// Stream response to disk instead of buffering the entire archive in memory.
|
|
233
234
|
// Uses Bun.write which handles Response streaming natively.
|
|
234
|
-
const BunRuntime = globalThis
|
|
235
|
+
const BunRuntime = globalThis
|
|
236
|
+
.Bun;
|
|
235
237
|
if (BunRuntime?.write) {
|
|
236
238
|
await BunRuntime.write(destination, response);
|
|
237
239
|
}
|
|
@@ -308,7 +310,9 @@ function validateTarEntries(listOutput) {
|
|
|
308
310
|
if (!stripped)
|
|
309
311
|
continue;
|
|
310
312
|
const normalizedStripped = path.posix.normalize(stripped);
|
|
311
|
-
if (normalizedStripped === ".." ||
|
|
313
|
+
if (normalizedStripped === ".." ||
|
|
314
|
+
normalizedStripped.startsWith("../") ||
|
|
315
|
+
path.posix.isAbsolute(normalizedStripped)) {
|
|
312
316
|
throw new Error(`Archive contains an unsafe entry after strip-components: ${entry}`);
|
|
313
317
|
}
|
|
314
318
|
}
|
package/dist/registry-resolve.js
CHANGED
|
@@ -3,7 +3,7 @@ import fs from "node:fs";
|
|
|
3
3
|
import path from "node:path";
|
|
4
4
|
import { pathToFileURL } from "node:url";
|
|
5
5
|
import { fetchWithRetry } from "./common";
|
|
6
|
-
import {
|
|
6
|
+
import { asRecord, asString, GITHUB_API_BASE, githubHeaders } from "./github";
|
|
7
7
|
export function parseRegistryRef(rawRef) {
|
|
8
8
|
const ref = rawRef.trim();
|
|
9
9
|
if (!ref)
|
|
@@ -177,7 +177,7 @@ async function resolveNpmArtifact(parsed) {
|
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
if (!resolvedVersion || !(resolvedVersion in versions)) {
|
|
180
|
-
throw new Error(`Unable to resolve npm ref
|
|
180
|
+
throw new Error(`Unable to resolve npm ref "${parsed.ref}".`);
|
|
181
181
|
}
|
|
182
182
|
const versionMeta = asRecord(versions[resolvedVersion]);
|
|
183
183
|
const dist = asRecord(versionMeta.dist);
|
|
@@ -287,16 +287,20 @@ function splitNpmNameAndVersion(input) {
|
|
|
287
287
|
}
|
|
288
288
|
function validateNpmPackageName(name) {
|
|
289
289
|
if (!name)
|
|
290
|
-
throw new Error(
|
|
290
|
+
throw new Error("Invalid npm package name: name is required.");
|
|
291
291
|
if (name.length > 214)
|
|
292
292
|
throw new Error(`Invalid npm package name: "${name}" exceeds 214 characters.`);
|
|
293
|
-
if (name !== name.toLowerCase() && !name.startsWith(
|
|
293
|
+
if (name !== name.toLowerCase() && !name.startsWith("@")) {
|
|
294
294
|
throw new Error(`Invalid npm package name: "${name}" must be lowercase.`);
|
|
295
295
|
}
|
|
296
|
-
if (name.startsWith(
|
|
296
|
+
if (name.startsWith(".") || name.startsWith("_")) {
|
|
297
297
|
throw new Error(`Invalid npm package name: "${name}" cannot start with . or _.`);
|
|
298
298
|
}
|
|
299
|
-
if (/[~'!()*]/.test(name) ||
|
|
299
|
+
if (/[~'!()*]/.test(name) ||
|
|
300
|
+
name.includes(" ") ||
|
|
301
|
+
encodeURIComponent(name)
|
|
302
|
+
.replace(/%40/g, "@")
|
|
303
|
+
.replace(/%2[Ff]/g, "/") !== name) {
|
|
300
304
|
throw new Error(`Invalid npm package name: "${name}" contains invalid characters.`);
|
|
301
305
|
}
|
|
302
306
|
}
|
|
@@ -449,11 +453,11 @@ async function fetchJson(url, headers) {
|
|
|
449
453
|
if (!response.ok) {
|
|
450
454
|
throw new Error(`Request failed (${response.status}) for ${url}`);
|
|
451
455
|
}
|
|
452
|
-
return await response.json();
|
|
456
|
+
return (await response.json());
|
|
453
457
|
}
|
|
454
458
|
async function tryFetchJson(url, headers) {
|
|
455
459
|
const response = await fetchWithRetry(url, { headers });
|
|
456
460
|
if (!response.ok)
|
|
457
461
|
return null;
|
|
458
|
-
return await response.json();
|
|
462
|
+
return (await response.json());
|
|
459
463
|
}
|
package/dist/registry-search.js
CHANGED
|
@@ -149,10 +149,7 @@ function parseKitEntry(raw) {
|
|
|
149
149
|
}
|
|
150
150
|
// ── Scoring ─────────────────────────────────────────────────────────────────
|
|
151
151
|
function scoreKits(kits, query, limit) {
|
|
152
|
-
const tokens = query
|
|
153
|
-
.toLowerCase()
|
|
154
|
-
.split(/\s+/)
|
|
155
|
-
.filter(Boolean);
|
|
152
|
+
const tokens = query.toLowerCase().split(/\s+/).filter(Boolean);
|
|
156
153
|
const scored = [];
|
|
157
154
|
for (const kit of kits) {
|
|
158
155
|
const score = scoreKit(kit, tokens);
|
|
@@ -226,7 +223,10 @@ function resolveRegistryUrls(override) {
|
|
|
226
223
|
// Allow env var override (comma-separated)
|
|
227
224
|
const envUrls = process.env.AKM_REGISTRY_URL?.trim();
|
|
228
225
|
if (envUrls) {
|
|
229
|
-
return envUrls
|
|
226
|
+
return envUrls
|
|
227
|
+
.split(",")
|
|
228
|
+
.map((u) => u.trim())
|
|
229
|
+
.filter(Boolean);
|
|
230
230
|
}
|
|
231
231
|
return [DEFAULT_REGISTRY_URL];
|
|
232
232
|
}
|
package/dist/renderers.js
CHANGED
|
@@ -6,15 +6,14 @@
|
|
|
6
6
|
* from ./file-context. Renderers are registered at module-load time so that
|
|
7
7
|
* importing this module is sufficient to make them available.
|
|
8
8
|
*/
|
|
9
|
-
import fs from "node:fs";
|
|
10
9
|
import path from "node:path";
|
|
11
|
-
import { registerRenderer } from "./file-context";
|
|
12
|
-
import { parseFrontmatter, toStringOrUndefined } from "./frontmatter";
|
|
13
10
|
import { SCRIPT_EXTENSIONS } from "./asset-spec";
|
|
14
|
-
import { buildToolInfo } from "./tool-runner";
|
|
15
11
|
import { hasErrnoCode } from "./common";
|
|
12
|
+
import { registerRenderer } from "./file-context";
|
|
13
|
+
import { parseFrontmatter, toStringOrUndefined } from "./frontmatter";
|
|
14
|
+
import { extractFrontmatterOnly, extractLineRange, extractSection, formatToc, parseMarkdownToc } from "./markdown";
|
|
16
15
|
import { extractDescriptionFromComments } from "./metadata";
|
|
17
|
-
import {
|
|
16
|
+
import { buildToolInfo } from "./tool-runner";
|
|
18
17
|
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
19
18
|
/**
|
|
20
19
|
* Derive a display name from the RenderContext.
|
|
@@ -35,7 +34,7 @@ function deriveName(ctx) {
|
|
|
35
34
|
* entry in the array when no prefix match is found.
|
|
36
35
|
*/
|
|
37
36
|
function findContainingStashDir(stashDirs, filePath) {
|
|
38
|
-
return
|
|
37
|
+
return stashDirs.find((d) => path.resolve(filePath).startsWith(path.resolve(d) + path.sep)) ?? stashDirs[0];
|
|
39
38
|
}
|
|
40
39
|
// ── 1. tool-script ───────────────────────────────────────────────────────────
|
|
41
40
|
const toolScriptRenderer = {
|
|
@@ -186,8 +185,7 @@ const knowledgeMdRenderer = {
|
|
|
186
185
|
},
|
|
187
186
|
extractMetadata(entry, ctx) {
|
|
188
187
|
try {
|
|
189
|
-
const
|
|
190
|
-
const toc = parseMarkdownToc(mdContent);
|
|
188
|
+
const toc = parseMarkdownToc(ctx.content());
|
|
191
189
|
if (toc.headings.length > 0)
|
|
192
190
|
entry.toc = toc.headings;
|
|
193
191
|
}
|
|
@@ -266,11 +264,23 @@ const scriptSourceRenderer = {
|
|
|
266
264
|
],
|
|
267
265
|
};
|
|
268
266
|
// ── Registration ─────────────────────────────────────────────────────────────
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
267
|
+
/** All built-in renderers. */
|
|
268
|
+
const builtinRenderers = [
|
|
269
|
+
toolScriptRenderer,
|
|
270
|
+
skillMdRenderer,
|
|
271
|
+
commandMdRenderer,
|
|
272
|
+
agentMdRenderer,
|
|
273
|
+
knowledgeMdRenderer,
|
|
274
|
+
scriptSourceRenderer,
|
|
275
|
+
];
|
|
276
|
+
/**
|
|
277
|
+
* Register all built-in renderers with the file-context registry.
|
|
278
|
+
* Called once from the CLI entry point (or ensureBuiltinsRegistered).
|
|
279
|
+
*/
|
|
280
|
+
export function registerBuiltinRenderers() {
|
|
281
|
+
for (const renderer of builtinRenderers) {
|
|
282
|
+
registerRenderer(renderer);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
275
285
|
// ── Named exports for testing ────────────────────────────────────────────────
|
|
276
286
|
export { toolScriptRenderer, skillMdRenderer, commandMdRenderer, agentMdRenderer, knowledgeMdRenderer, scriptSourceRenderer, };
|
package/dist/ripgrep-install.js
CHANGED
|
@@ -78,14 +78,7 @@ function downloadAndExtractTarGz(url, archiveName, destBinary) {
|
|
|
78
78
|
throw new Error(`Failed to download ripgrep from ${url}: ${err}`);
|
|
79
79
|
}
|
|
80
80
|
// Extract the specific binary from the archive into destDir
|
|
81
|
-
const tarResult = spawnSync("tar", [
|
|
82
|
-
"xzf",
|
|
83
|
-
tmpTarGz,
|
|
84
|
-
"--strip-components=1",
|
|
85
|
-
"-C",
|
|
86
|
-
destDir,
|
|
87
|
-
`${archiveName}/rg`,
|
|
88
|
-
], {
|
|
81
|
+
const tarResult = spawnSync("tar", ["xzf", tmpTarGz, "--strip-components=1", "-C", destDir, `${archiveName}/rg`], {
|
|
89
82
|
encoding: "utf8",
|
|
90
83
|
timeout: 60_000,
|
|
91
84
|
});
|
|
@@ -124,13 +117,7 @@ function downloadAndExtractZip(url, archiveName, destBinary) {
|
|
|
124
117
|
}
|
|
125
118
|
// Extract the zip archive using separate spawnSync calls with argument arrays
|
|
126
119
|
// to avoid shell injection via path interpolation in PowerShell -Command strings
|
|
127
|
-
const expandResult = spawnSync("powershell", [
|
|
128
|
-
"-Command",
|
|
129
|
-
"Expand-Archive",
|
|
130
|
-
"-Path", tmpZip,
|
|
131
|
-
"-DestinationPath", destDir,
|
|
132
|
-
"-Force",
|
|
133
|
-
], {
|
|
120
|
+
const expandResult = spawnSync("powershell", ["-Command", "Expand-Archive", "-Path", tmpZip, "-DestinationPath", destDir, "-Force"], {
|
|
134
121
|
encoding: "utf8",
|
|
135
122
|
timeout: 60_000,
|
|
136
123
|
});
|
|
@@ -138,13 +125,7 @@ function downloadAndExtractZip(url, archiveName, destBinary) {
|
|
|
138
125
|
throw new Error(expandResult.stderr?.trim() || "extraction failed");
|
|
139
126
|
}
|
|
140
127
|
const srcRgExe = path.join(destDir, archiveName, "rg.exe");
|
|
141
|
-
const moveResult = spawnSync("powershell", [
|
|
142
|
-
"-Command",
|
|
143
|
-
"Move-Item",
|
|
144
|
-
"-Force",
|
|
145
|
-
"-Path", srcRgExe,
|
|
146
|
-
"-Destination", destBinary,
|
|
147
|
-
], {
|
|
128
|
+
const moveResult = spawnSync("powershell", ["-Command", "Move-Item", "-Force", "-Path", srcRgExe, "-Destination", destBinary], {
|
|
148
129
|
encoding: "utf8",
|
|
149
130
|
timeout: 60_000,
|
|
150
131
|
});
|
package/dist/ripgrep.js
CHANGED