agentikit 0.0.13 → 0.0.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +385 -0
- package/README.md +187 -110
- package/dist/{src/asset-spec.js → asset-spec.js} +11 -2
- package/dist/{src/asset-type-handler.js → asset-type-handler.js} +4 -3
- package/dist/cli.js +709 -0
- package/dist/common.js +192 -0
- package/dist/{src/config-cli.js → config-cli.js} +36 -30
- package/dist/{src/config.js → config.js} +95 -25
- package/dist/{src/db.js → db.js} +123 -51
- package/dist/{src/embedder.js → embedder.js} +57 -2
- package/dist/errors.js +28 -0
- package/dist/file-context.js +188 -0
- package/dist/{src/frontmatter.js → frontmatter.js} +1 -1
- package/dist/{src/github.js → github.js} +1 -3
- package/dist/handlers/agent-handler.js +19 -0
- package/dist/handlers/command-handler.js +20 -0
- package/dist/handlers/handler-bridge.js +51 -0
- package/dist/handlers/index.js +19 -0
- package/dist/handlers/knowledge-handler.js +32 -0
- package/dist/handlers/script-handler.js +42 -0
- package/dist/{src/handlers → handlers}/skill-handler.js +5 -6
- package/dist/{src/handlers → handlers}/tool-handler.js +8 -24
- package/dist/{src/indexer.js → indexer.js} +50 -26
- package/dist/init.js +43 -0
- package/dist/{src/llm.js → llm.js} +6 -11
- package/dist/lockfile.js +60 -0
- package/dist/matchers.js +163 -0
- package/dist/{src/metadata.js → metadata.js} +36 -16
- package/dist/{src/origin-resolve.js → origin-resolve.js} +10 -9
- package/dist/paths.js +83 -0
- package/dist/{src/registry-install.js → registry-install.js} +151 -19
- package/dist/{src/registry-resolve.js → registry-resolve.js} +190 -26
- package/dist/{src/registry-search.js → registry-search.js} +13 -21
- package/dist/renderers.js +286 -0
- package/dist/{src/ripgrep-install.js → ripgrep-install.js} +8 -27
- package/dist/{src/ripgrep-resolve.js → ripgrep-resolve.js} +21 -11
- package/dist/ripgrep.js +2 -0
- package/dist/self-update.js +226 -0
- package/dist/{src/stash-add.js → stash-add.js} +14 -4
- package/dist/stash-clone.js +115 -0
- package/dist/{src/stash-ref.js → stash-ref.js} +10 -9
- package/dist/{src/stash-registry.js → stash-registry.js} +21 -46
- package/dist/{src/stash-resolve.js → stash-resolve.js} +10 -9
- package/dist/{src/stash-search.js → stash-search.js} +89 -74
- package/dist/stash-show.js +74 -0
- package/dist/stash-source.js +127 -0
- package/dist/submit.js +557 -0
- package/dist/{src/tool-runner.js → tool-runner.js} +1 -5
- package/dist/{src/walker.js → walker.js} +38 -0
- package/dist/warn.js +20 -0
- package/package.json +13 -18
- package/dist/index.d.ts +0 -28
- package/dist/index.js +0 -15
- package/dist/src/asset-spec.d.ts +0 -16
- package/dist/src/asset-type-handler.d.ts +0 -27
- package/dist/src/cli.d.ts +0 -2
- package/dist/src/cli.js +0 -399
- package/dist/src/common.d.ts +0 -13
- package/dist/src/common.js +0 -60
- package/dist/src/config-cli.d.ts +0 -9
- package/dist/src/config.d.ts +0 -50
- package/dist/src/db.d.ts +0 -46
- package/dist/src/embedder.d.ts +0 -10
- package/dist/src/frontmatter.d.ts +0 -30
- package/dist/src/github.d.ts +0 -4
- package/dist/src/handlers/agent-handler.d.ts +0 -2
- package/dist/src/handlers/agent-handler.js +0 -26
- package/dist/src/handlers/command-handler.d.ts +0 -2
- package/dist/src/handlers/command-handler.js +0 -23
- package/dist/src/handlers/index.d.ts +0 -6
- package/dist/src/handlers/index.js +0 -23
- package/dist/src/handlers/knowledge-handler.d.ts +0 -2
- package/dist/src/handlers/knowledge-handler.js +0 -56
- package/dist/src/handlers/markdown-helpers.d.ts +0 -7
- package/dist/src/handlers/script-handler.d.ts +0 -2
- package/dist/src/handlers/script-handler.js +0 -78
- package/dist/src/handlers/skill-handler.d.ts +0 -2
- package/dist/src/handlers/tool-handler.d.ts +0 -2
- package/dist/src/indexer.d.ts +0 -22
- package/dist/src/init.d.ts +0 -19
- package/dist/src/init.js +0 -99
- package/dist/src/llm.d.ts +0 -15
- package/dist/src/markdown.d.ts +0 -18
- package/dist/src/metadata.d.ts +0 -41
- package/dist/src/origin-resolve.d.ts +0 -19
- package/dist/src/registry-install.d.ts +0 -11
- package/dist/src/registry-resolve.d.ts +0 -3
- package/dist/src/registry-search.d.ts +0 -27
- package/dist/src/registry-types.d.ts +0 -62
- package/dist/src/ripgrep-install.d.ts +0 -12
- package/dist/src/ripgrep-resolve.d.ts +0 -13
- package/dist/src/ripgrep.d.ts +0 -3
- package/dist/src/ripgrep.js +0 -2
- package/dist/src/stash-add.d.ts +0 -4
- package/dist/src/stash-clone.d.ts +0 -22
- package/dist/src/stash-clone.js +0 -83
- package/dist/src/stash-ref.d.ts +0 -31
- package/dist/src/stash-registry.d.ts +0 -18
- package/dist/src/stash-resolve.d.ts +0 -2
- package/dist/src/stash-search.d.ts +0 -8
- package/dist/src/stash-show.d.ts +0 -5
- package/dist/src/stash-show.js +0 -46
- package/dist/src/stash-source.d.ts +0 -24
- package/dist/src/stash-source.js +0 -81
- package/dist/src/stash-types.d.ts +0 -227
- package/dist/src/stash.d.ts +0 -16
- package/dist/src/stash.js +0 -9
- package/dist/src/tool-runner.d.ts +0 -35
- package/dist/src/walker.d.ts +0 -19
- package/src/asset-spec.ts +0 -85
- package/src/asset-type-handler.ts +0 -77
- package/src/cli.ts +0 -427
- package/src/common.ts +0 -76
- package/src/config-cli.ts +0 -499
- package/src/config.ts +0 -305
- package/src/db.ts +0 -411
- package/src/embedder.ts +0 -128
- package/src/frontmatter.ts +0 -95
- package/src/github.ts +0 -21
- package/src/handlers/agent-handler.ts +0 -32
- package/src/handlers/command-handler.ts +0 -29
- package/src/handlers/index.ts +0 -25
- package/src/handlers/knowledge-handler.ts +0 -62
- package/src/handlers/markdown-helpers.ts +0 -19
- package/src/handlers/script-handler.ts +0 -92
- package/src/handlers/skill-handler.ts +0 -37
- package/src/handlers/tool-handler.ts +0 -71
- package/src/indexer.ts +0 -392
- package/src/init.ts +0 -114
- package/src/llm.ts +0 -125
- package/src/markdown.ts +0 -106
- package/src/metadata.ts +0 -333
- package/src/origin-resolve.ts +0 -67
- package/src/registry-install.ts +0 -361
- package/src/registry-resolve.ts +0 -341
- package/src/registry-search.ts +0 -335
- package/src/registry-types.ts +0 -72
- package/src/ripgrep-install.ts +0 -200
- package/src/ripgrep-resolve.ts +0 -72
- package/src/ripgrep.ts +0 -3
- package/src/stash-add.ts +0 -63
- package/src/stash-clone.ts +0 -127
- package/src/stash-ref.ts +0 -99
- package/src/stash-registry.ts +0 -259
- package/src/stash-resolve.ts +0 -50
- package/src/stash-search.ts +0 -613
- package/src/stash-show.ts +0 -55
- package/src/stash-source.ts +0 -103
- package/src/stash-types.ts +0 -231
- package/src/stash.ts +0 -39
- package/src/tool-runner.ts +0 -142
- package/src/walker.ts +0 -53
- /package/dist/{src/handlers → handlers}/markdown-helpers.js +0 -0
- /package/dist/{src/markdown.js → markdown.js} +0 -0
- /package/dist/{src/registry-types.js → registry-types.js} +0 -0
- /package/dist/{src/stash-types.js → stash-types.js} +0 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bridge utility that converts legacy ShowInput into a RenderContext,
|
|
3
|
+
* allowing handlers to delegate their buildShowResponse to renderers.
|
|
4
|
+
*/
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { toPosix } from "../common";
|
|
7
|
+
/**
|
|
8
|
+
* Convert a legacy ShowInput into a RenderContext suitable for passing
|
|
9
|
+
* to an AssetRenderer's buildShowResponse method.
|
|
10
|
+
*
|
|
11
|
+
* This avoids hitting the filesystem since ShowInput already carries
|
|
12
|
+
* the file content.
|
|
13
|
+
*/
|
|
14
|
+
export function showInputToRenderContext(input, rendererName) {
|
|
15
|
+
const absPath = path.resolve(input.path);
|
|
16
|
+
const stashDirs = input.stashDirs ?? [];
|
|
17
|
+
// Derive a stash root from stashDirs if possible
|
|
18
|
+
const stashRoot = stashDirs.find((d) => absPath.startsWith(path.resolve(d) + path.sep)) ?? stashDirs[0] ?? path.dirname(absPath);
|
|
19
|
+
const relPath = toPosix(path.relative(stashRoot, absPath));
|
|
20
|
+
const ext = path.extname(absPath).toLowerCase();
|
|
21
|
+
const fileName = path.basename(absPath);
|
|
22
|
+
const parentDirAbs = path.dirname(absPath);
|
|
23
|
+
const parentDir = path.basename(parentDirAbs);
|
|
24
|
+
const relDir = toPosix(path.dirname(relPath));
|
|
25
|
+
const ancestorDirs = relDir === "." ? [] : relDir.split("/").filter((seg) => seg.length > 0);
|
|
26
|
+
// Cache the content from input (no filesystem read needed)
|
|
27
|
+
const cachedContent = input.content;
|
|
28
|
+
const matchResult = {
|
|
29
|
+
type: rendererName.split("-")[0], // e.g. "tool" from "tool-script"
|
|
30
|
+
specificity: 10,
|
|
31
|
+
renderer: rendererName,
|
|
32
|
+
meta: { name: input.name, view: input.view },
|
|
33
|
+
};
|
|
34
|
+
return {
|
|
35
|
+
absPath,
|
|
36
|
+
relPath,
|
|
37
|
+
ext,
|
|
38
|
+
fileName,
|
|
39
|
+
parentDir,
|
|
40
|
+
parentDirAbs,
|
|
41
|
+
ancestorDirs,
|
|
42
|
+
stashRoot,
|
|
43
|
+
content: () => cachedContent,
|
|
44
|
+
frontmatter: () => null, // Renderers parse frontmatter from content() themselves
|
|
45
|
+
stat() {
|
|
46
|
+
throw new Error("stat() not available in handler bridge context");
|
|
47
|
+
},
|
|
48
|
+
matchResult,
|
|
49
|
+
stashDirs,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { registerAssetType } from "../asset-type-handler";
|
|
2
|
+
import { agentHandler } from "./agent-handler";
|
|
3
|
+
import { commandHandler } from "./command-handler";
|
|
4
|
+
import { knowledgeHandler } from "./knowledge-handler";
|
|
5
|
+
import { scriptHandler } from "./script-handler";
|
|
6
|
+
import { skillHandler } from "./skill-handler";
|
|
7
|
+
import { toolHandler } from "./tool-handler";
|
|
8
|
+
/**
|
|
9
|
+
* Register all built-in asset type handlers.
|
|
10
|
+
* Called once from ensureHandlersRegistered in asset-type-handler.ts.
|
|
11
|
+
*/
|
|
12
|
+
export function registerBuiltinHandlers() {
|
|
13
|
+
registerAssetType(toolHandler);
|
|
14
|
+
registerAssetType(skillHandler);
|
|
15
|
+
registerAssetType(commandHandler);
|
|
16
|
+
registerAssetType(agentHandler);
|
|
17
|
+
registerAssetType(knowledgeHandler);
|
|
18
|
+
registerAssetType(scriptHandler);
|
|
19
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import { getRenderer } from "../file-context";
|
|
3
|
+
import { parseMarkdownToc } from "../markdown";
|
|
4
|
+
import { showInputToRenderContext } from "./handler-bridge";
|
|
5
|
+
import { isMarkdownFile, markdownAssetPath, markdownCanonicalName } from "./markdown-helpers";
|
|
6
|
+
export const knowledgeHandler = {
|
|
7
|
+
typeName: "knowledge",
|
|
8
|
+
stashDir: "knowledge",
|
|
9
|
+
isRelevantFile: isMarkdownFile,
|
|
10
|
+
toCanonicalName: markdownCanonicalName,
|
|
11
|
+
toAssetPath: markdownAssetPath,
|
|
12
|
+
buildShowResponse(input) {
|
|
13
|
+
const renderer = getRenderer("knowledge-md");
|
|
14
|
+
const ctx = showInputToRenderContext(input, "knowledge-md");
|
|
15
|
+
return renderer.buildShowResponse(ctx);
|
|
16
|
+
},
|
|
17
|
+
defaultUsageGuide: [
|
|
18
|
+
"Use `akm show <openRef>` to read the document; start with `--view toc` for large files.",
|
|
19
|
+
"Use `--view section` or `--view lines` to load only the part you need.",
|
|
20
|
+
],
|
|
21
|
+
extractTypeMetadata(entry, file) {
|
|
22
|
+
try {
|
|
23
|
+
const mdContent = fs.readFileSync(file, "utf8");
|
|
24
|
+
const toc = parseMarkdownToc(mdContent);
|
|
25
|
+
if (toc.headings.length > 0)
|
|
26
|
+
entry.toc = toc.headings;
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Non-fatal: skip TOC if file can't be read
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import path from "node:path";
|
|
2
|
+
import { SCRIPT_EXTENSIONS_BROAD } from "../asset-spec";
|
|
3
|
+
import { toPosix } from "../common";
|
|
4
|
+
import { getRenderer } from "../file-context";
|
|
5
|
+
import { extractDescriptionFromComments } from "../metadata";
|
|
6
|
+
import { showInputToRenderContext } from "./handler-bridge";
|
|
7
|
+
export const scriptHandler = {
|
|
8
|
+
typeName: "script",
|
|
9
|
+
stashDir: "scripts",
|
|
10
|
+
isRelevantFile(fileName) {
|
|
11
|
+
return SCRIPT_EXTENSIONS_BROAD.has(path.extname(fileName).toLowerCase());
|
|
12
|
+
},
|
|
13
|
+
toCanonicalName(typeRoot, filePath) {
|
|
14
|
+
return toPosix(path.relative(typeRoot, filePath));
|
|
15
|
+
},
|
|
16
|
+
toAssetPath(typeRoot, name) {
|
|
17
|
+
return path.join(typeRoot, name);
|
|
18
|
+
},
|
|
19
|
+
buildShowResponse(input) {
|
|
20
|
+
const renderer = getRenderer("script-source");
|
|
21
|
+
const ctx = showInputToRenderContext(input, "script-source");
|
|
22
|
+
return renderer.buildShowResponse(ctx);
|
|
23
|
+
},
|
|
24
|
+
enrichSearchHit(hit, stashDir) {
|
|
25
|
+
const renderer = getRenderer("script-source");
|
|
26
|
+
renderer.enrichSearchHit(hit, stashDir);
|
|
27
|
+
},
|
|
28
|
+
defaultUsageGuide: [
|
|
29
|
+
"Use the hit's runCmd for execution when available, or run the script directly with the appropriate interpreter.",
|
|
30
|
+
"Use `akm show <openRef>` to inspect the script before running it.",
|
|
31
|
+
],
|
|
32
|
+
extractTypeMetadata(entry, file, ext) {
|
|
33
|
+
if (ext !== ".md") {
|
|
34
|
+
const commentDesc = extractDescriptionFromComments(file);
|
|
35
|
+
if (commentDesc && !entry.description) {
|
|
36
|
+
entry.description = commentDesc;
|
|
37
|
+
entry.source = "comments";
|
|
38
|
+
entry.confidence = 0.7;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { toPosix } from "../common";
|
|
3
|
+
import { getRenderer } from "../file-context";
|
|
4
|
+
import { showInputToRenderContext } from "./handler-bridge";
|
|
3
5
|
export const skillHandler = {
|
|
4
6
|
typeName: "skill",
|
|
5
7
|
stashDir: "skills",
|
|
@@ -16,12 +18,9 @@ export const skillHandler = {
|
|
|
16
18
|
return path.join(typeRoot, name, "SKILL.md");
|
|
17
19
|
},
|
|
18
20
|
buildShowResponse(input) {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
path: input.path,
|
|
23
|
-
content: input.content,
|
|
24
|
-
};
|
|
21
|
+
const renderer = getRenderer("skill-md");
|
|
22
|
+
const ctx = showInputToRenderContext(input, "skill-md");
|
|
23
|
+
return renderer.buildShowResponse(ctx);
|
|
25
24
|
},
|
|
26
25
|
defaultUsageGuide: [
|
|
27
26
|
"Read and apply the skill instructions as written, then adapt examples to your current repo state and task.",
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import { SCRIPT_EXTENSIONS } from "../asset-spec";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { toPosix } from "../common";
|
|
4
|
+
import { getRenderer } from "../file-context";
|
|
5
5
|
import { extractDescriptionFromComments } from "../metadata";
|
|
6
|
+
import { showInputToRenderContext } from "./handler-bridge";
|
|
6
7
|
export const toolHandler = {
|
|
7
8
|
typeName: "tool",
|
|
8
9
|
stashDir: "tools",
|
|
@@ -16,30 +17,13 @@ export const toolHandler = {
|
|
|
16
17
|
return path.join(typeRoot, name);
|
|
17
18
|
},
|
|
18
19
|
buildShowResponse(input) {
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
return { type: "tool", name: input.name, path: input.path, content: input.content };
|
|
23
|
-
}
|
|
24
|
-
const toolInfo = buildToolInfo(assetStashDir, input.path);
|
|
25
|
-
return {
|
|
26
|
-
type: "tool",
|
|
27
|
-
name: input.name,
|
|
28
|
-
path: input.path,
|
|
29
|
-
runCmd: toolInfo.runCmd,
|
|
30
|
-
kind: toolInfo.kind,
|
|
31
|
-
};
|
|
20
|
+
const renderer = getRenderer("tool-script");
|
|
21
|
+
const ctx = showInputToRenderContext(input, "tool-script");
|
|
22
|
+
return renderer.buildShowResponse(ctx);
|
|
32
23
|
},
|
|
33
24
|
enrichSearchHit(hit, stashDir) {
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
hit.runCmd = toolInfo.runCmd;
|
|
37
|
-
hit.kind = toolInfo.kind;
|
|
38
|
-
}
|
|
39
|
-
catch (error) {
|
|
40
|
-
if (!hasErrnoCode(error, "ENOENT"))
|
|
41
|
-
throw error;
|
|
42
|
-
}
|
|
25
|
+
const renderer = getRenderer("tool-script");
|
|
26
|
+
renderer.enrichSearchHit(hit, stashDir);
|
|
43
27
|
},
|
|
44
28
|
defaultUsageGuide: [
|
|
45
29
|
"Use the hit's runCmd for execution so runtime and working directory stay correct.",
|
|
@@ -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();
|
|
@@ -26,14 +28,23 @@ export async function agentikitIndex(options) {
|
|
|
26
28
|
const builtAtMs = isIncremental ? new Date(prevBuiltAt).getTime() : 0;
|
|
27
29
|
if (options?.full || !isIncremental) {
|
|
28
30
|
// Wipe all entries for full rebuild or stashDir change
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
31
|
+
// Delete from child tables first to respect foreign key constraints
|
|
32
|
+
try {
|
|
33
|
+
db.exec("DELETE FROM embeddings");
|
|
34
|
+
}
|
|
35
|
+
catch {
|
|
36
|
+
/* ignore */
|
|
37
|
+
}
|
|
38
|
+
if (isVecAvailable(db)) {
|
|
32
39
|
try {
|
|
33
40
|
db.exec("DELETE FROM entries_vec");
|
|
34
41
|
}
|
|
35
|
-
catch {
|
|
42
|
+
catch {
|
|
43
|
+
/* ignore */
|
|
44
|
+
}
|
|
36
45
|
}
|
|
46
|
+
db.exec("DELETE FROM entries_fts");
|
|
47
|
+
db.exec("DELETE FROM entries");
|
|
37
48
|
}
|
|
38
49
|
const tWalkStart = Date.now();
|
|
39
50
|
// Walk stash dirs and index entries
|
|
@@ -54,6 +65,8 @@ export async function agentikitIndex(options) {
|
|
|
54
65
|
setMeta(db, "stashDirs", JSON.stringify(allStashDirs));
|
|
55
66
|
setMeta(db, "hasEmbeddings", hasEmbeddings ? "1" : "0");
|
|
56
67
|
const totalEntries = getEntryCount(db);
|
|
68
|
+
// Warn on every index run if using JS fallback with many entries
|
|
69
|
+
warnIfVecMissing(db);
|
|
57
70
|
const tEnd = Date.now();
|
|
58
71
|
return {
|
|
59
72
|
stashDir,
|
|
@@ -117,6 +130,18 @@ function indexEntries(db, allStashDirs, stashDir, isIncremental, builtAtMs) {
|
|
|
117
130
|
stash = migration.stash;
|
|
118
131
|
writeStashFile(dirPath, stash);
|
|
119
132
|
}
|
|
133
|
+
// Check for files on disk that aren't covered by existing .stash.json entries.
|
|
134
|
+
// This handles the case where new files are added after the initial index.
|
|
135
|
+
const coveredFiles = new Set(stash.entries.map((e) => e.entry).filter((e) => !!e));
|
|
136
|
+
const uncoveredFiles = files.filter((f) => !coveredFiles.has(path.basename(f)));
|
|
137
|
+
if (uncoveredFiles.length > 0) {
|
|
138
|
+
const generated = generateMetadata(dirPath, assetType, uncoveredFiles, typeRoot);
|
|
139
|
+
if (generated.entries.length > 0) {
|
|
140
|
+
stash = { entries: [...stash.entries, ...generated.entries] };
|
|
141
|
+
writeStashFile(dirPath, stash);
|
|
142
|
+
generatedCount += generated.entries.length;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
120
145
|
}
|
|
121
146
|
if (!stash) {
|
|
122
147
|
// Generate metadata heuristically
|
|
@@ -128,9 +153,7 @@ function indexEntries(db, allStashDirs, stashDir, isIncremental, builtAtMs) {
|
|
|
128
153
|
}
|
|
129
154
|
if (stash) {
|
|
130
155
|
for (const entry of stash.entries) {
|
|
131
|
-
const entryPath = entry.entry
|
|
132
|
-
? path.join(dirPath, entry.entry)
|
|
133
|
-
: files[0] || dirPath;
|
|
156
|
+
const entryPath = entry.entry ? path.join(dirPath, entry.entry) : files[0] || dirPath;
|
|
134
157
|
const entryKey = `${currentStashDir}:${entry.type}:${entry.name}`;
|
|
135
158
|
const searchText = buildSearchText(entry);
|
|
136
159
|
upsertEntry(db, entryKey, dirPath, entryPath, currentStashDir, entry, searchText);
|
|
@@ -166,19 +189,22 @@ async function enhanceDirsWithLlm(db, config, dirsNeedingLlm) {
|
|
|
166
189
|
}
|
|
167
190
|
}
|
|
168
191
|
async function generateEmbeddingsForDb(db, config) {
|
|
169
|
-
if (!config.semanticSearch
|
|
192
|
+
if (!config.semanticSearch)
|
|
170
193
|
return false;
|
|
171
194
|
try {
|
|
172
|
-
const {
|
|
195
|
+
const { embedBatch } = await import("./embedder.js");
|
|
173
196
|
const allEntries = getAllEntriesForEmbedding(db);
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
197
|
+
if (allEntries.length === 0)
|
|
198
|
+
return true;
|
|
199
|
+
const texts = allEntries.map((e) => e.searchText);
|
|
200
|
+
const embeddings = await embedBatch(texts, config.embedding);
|
|
201
|
+
for (let i = 0; i < allEntries.length; i++) {
|
|
202
|
+
upsertEmbedding(db, allEntries[i].id, embeddings[i]);
|
|
177
203
|
}
|
|
178
204
|
return true;
|
|
179
205
|
}
|
|
180
206
|
catch (error) {
|
|
181
|
-
|
|
207
|
+
warn("Embedding generation failed, continuing without:", error instanceof Error ? error.message : String(error));
|
|
182
208
|
return false;
|
|
183
209
|
}
|
|
184
210
|
}
|
|
@@ -187,15 +213,13 @@ function getAllEntriesForEmbedding(db) {
|
|
|
187
213
|
return db
|
|
188
214
|
.prepare(`
|
|
189
215
|
SELECT e.id, e.search_text AS searchText FROM entries e
|
|
190
|
-
WHERE NOT EXISTS (SELECT 1 FROM
|
|
216
|
+
WHERE NOT EXISTS (SELECT 1 FROM embeddings b WHERE b.id = e.id)
|
|
191
217
|
`)
|
|
192
218
|
.all();
|
|
193
219
|
}
|
|
194
220
|
function isDirStale(dirPath, currentFiles, previousEntries, builtAtMs) {
|
|
195
221
|
// Check if file set changed (additions or deletions)
|
|
196
|
-
const prevFileNames = new Set(previousEntries
|
|
197
|
-
.map((ie) => ie.entry.entry)
|
|
198
|
-
.filter((e) => !!e));
|
|
222
|
+
const prevFileNames = new Set(previousEntries.map((ie) => ie.entry.entry).filter((e) => !!e));
|
|
199
223
|
const currFileNames = new Set(currentFiles.map((f) => path.basename(f)));
|
|
200
224
|
if (prevFileNames.size !== currFileNames.size)
|
|
201
225
|
return true;
|
|
@@ -216,11 +240,11 @@ function isDirStale(dirPath, currentFiles, previousEntries, builtAtMs) {
|
|
|
216
240
|
// Check .stash.json modification time
|
|
217
241
|
const stashPath = path.join(dirPath, ".stash.json");
|
|
218
242
|
try {
|
|
219
|
-
if (fs.
|
|
243
|
+
if (fs.statSync(stashPath).mtimeMs > builtAtMs)
|
|
220
244
|
return true;
|
|
221
245
|
}
|
|
222
246
|
catch {
|
|
223
|
-
//
|
|
247
|
+
// file doesn't exist, not stale
|
|
224
248
|
}
|
|
225
249
|
return false;
|
|
226
250
|
}
|
|
@@ -253,15 +277,15 @@ async function enhanceStashWithLlm(llmConfig, stash, dirPath, files) {
|
|
|
253
277
|
const enhanced = [];
|
|
254
278
|
for (const entry of stash.entries) {
|
|
255
279
|
try {
|
|
256
|
-
const entryFile = entry.entry
|
|
257
|
-
? files.find((f) => path.basename(f) === entry.entry) ?? files[0]
|
|
258
|
-
: files[0];
|
|
280
|
+
const entryFile = entry.entry ? (files.find((f) => path.basename(f) === entry.entry) ?? files[0]) : files[0];
|
|
259
281
|
let fileContent;
|
|
260
282
|
if (entryFile) {
|
|
261
283
|
try {
|
|
262
284
|
fileContent = fs.readFileSync(entryFile, "utf8");
|
|
263
285
|
}
|
|
264
|
-
catch {
|
|
286
|
+
catch {
|
|
287
|
+
/* ignore unreadable files */
|
|
288
|
+
}
|
|
265
289
|
}
|
|
266
290
|
const improvements = await enhanceMetadata(llmConfig, entry, fileContent);
|
|
267
291
|
const updated = { ...entry };
|
package/dist/init.js
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agentikit initialization logic.
|
|
3
|
+
*
|
|
4
|
+
* Creates the working stash directory structure, persists the stashDir
|
|
5
|
+
* in config.json, and ensures ripgrep is available.
|
|
6
|
+
*/
|
|
7
|
+
import fs from "node:fs";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import { TYPE_DIRS } from "./asset-spec";
|
|
10
|
+
import { getConfigPath, loadConfig, saveConfig } from "./config";
|
|
11
|
+
import { getBinDir, getDefaultStashDir } from "./paths";
|
|
12
|
+
import { ensureRg } from "./ripgrep-install";
|
|
13
|
+
export async function agentikitInit(options) {
|
|
14
|
+
const stashDir = options?.dir ? path.resolve(options.dir) : getDefaultStashDir();
|
|
15
|
+
let created = false;
|
|
16
|
+
if (!fs.existsSync(stashDir)) {
|
|
17
|
+
fs.mkdirSync(stashDir, { recursive: true });
|
|
18
|
+
created = true;
|
|
19
|
+
}
|
|
20
|
+
for (const sub of Object.values(TYPE_DIRS)) {
|
|
21
|
+
const subDir = path.join(stashDir, sub);
|
|
22
|
+
if (!fs.existsSync(subDir)) {
|
|
23
|
+
fs.mkdirSync(subDir, { recursive: true });
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
// Persist stashDir in config.json
|
|
27
|
+
const configPath = getConfigPath();
|
|
28
|
+
const existing = loadConfig();
|
|
29
|
+
if (!existing.stashDir || existing.stashDir !== stashDir) {
|
|
30
|
+
saveConfig({ ...existing, stashDir });
|
|
31
|
+
}
|
|
32
|
+
// Ensure ripgrep is available (install to cache/bin if needed)
|
|
33
|
+
let ripgrep;
|
|
34
|
+
try {
|
|
35
|
+
const binDir = getBinDir();
|
|
36
|
+
const rgResult = ensureRg(binDir);
|
|
37
|
+
ripgrep = rgResult;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
// Non-fatal: ripgrep is optional, search works without it
|
|
41
|
+
}
|
|
42
|
+
return { stashDir, created, configPath, ripgrep };
|
|
43
|
+
}
|
|
@@ -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
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { getConfigDir } from "./config";
|
|
4
|
+
// ── Paths ───────────────────────────────────────────────────────────────────
|
|
5
|
+
function getLockfilePath() {
|
|
6
|
+
return path.join(getConfigDir(), "stash.lock");
|
|
7
|
+
}
|
|
8
|
+
// ── Read / Write ────────────────────────────────────────────────────────────
|
|
9
|
+
export function readLockfile() {
|
|
10
|
+
const lockfilePath = getLockfilePath();
|
|
11
|
+
try {
|
|
12
|
+
const raw = JSON.parse(fs.readFileSync(lockfilePath, "utf8"));
|
|
13
|
+
if (!Array.isArray(raw))
|
|
14
|
+
return [];
|
|
15
|
+
return raw.filter(isValidLockfileEntry);
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
return [];
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export function writeLockfile(entries) {
|
|
22
|
+
const lockfilePath = getLockfilePath();
|
|
23
|
+
const dir = path.dirname(lockfilePath);
|
|
24
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
25
|
+
const tmpPath = lockfilePath + `.tmp.${process.pid}`;
|
|
26
|
+
try {
|
|
27
|
+
fs.writeFileSync(tmpPath, JSON.stringify(entries, null, 2) + "\n", "utf8");
|
|
28
|
+
fs.renameSync(tmpPath, lockfilePath);
|
|
29
|
+
}
|
|
30
|
+
catch (err) {
|
|
31
|
+
try {
|
|
32
|
+
fs.unlinkSync(tmpPath);
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
/* ignore cleanup failure */
|
|
36
|
+
}
|
|
37
|
+
throw err;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
export function upsertLockEntry(entry) {
|
|
41
|
+
const entries = readLockfile();
|
|
42
|
+
const withoutExisting = entries.filter((e) => e.id !== entry.id);
|
|
43
|
+
writeLockfile([...withoutExisting, entry]);
|
|
44
|
+
}
|
|
45
|
+
export function removeLockEntry(id) {
|
|
46
|
+
const entries = readLockfile();
|
|
47
|
+
writeLockfile(entries.filter((e) => e.id !== id));
|
|
48
|
+
}
|
|
49
|
+
// ── Helpers ─────────────────────────────────────────────────────────────────
|
|
50
|
+
function isValidLockfileEntry(value) {
|
|
51
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
52
|
+
return false;
|
|
53
|
+
const obj = value;
|
|
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 !== "");
|
|
60
|
+
}
|