agentikit 0.0.7 → 0.0.9
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 +215 -76
- package/dist/index.d.ts +17 -3
- package/dist/index.js +10 -2
- package/dist/src/asset-spec.d.ts +14 -0
- package/dist/src/asset-spec.js +46 -0
- package/dist/src/cli.js +268 -57
- package/dist/src/common.d.ts +8 -0
- package/dist/src/common.js +46 -0
- package/dist/src/config.d.ts +37 -0
- package/dist/src/config.js +124 -0
- package/dist/src/embedder.d.ts +10 -0
- package/dist/src/embedder.js +87 -0
- package/dist/src/frontmatter.d.ts +30 -0
- package/dist/src/frontmatter.js +86 -0
- package/dist/src/indexer.d.ts +20 -2
- package/dist/src/indexer.js +212 -80
- package/dist/src/init.d.ts +19 -0
- package/dist/src/init.js +87 -0
- package/dist/src/llm.d.ts +15 -0
- package/dist/src/llm.js +91 -0
- package/dist/src/markdown.d.ts +18 -0
- package/dist/src/markdown.js +77 -0
- package/dist/src/metadata.d.ts +11 -2
- package/dist/src/metadata.js +161 -29
- package/dist/src/registry-install.d.ts +11 -0
- package/dist/src/registry-install.js +208 -0
- package/dist/src/registry-resolve.d.ts +3 -0
- package/dist/src/registry-resolve.js +231 -0
- package/dist/src/registry-search.d.ts +5 -0
- package/dist/src/registry-search.js +129 -0
- package/dist/src/registry-types.d.ts +55 -0
- package/dist/src/registry-types.js +1 -0
- package/dist/src/ripgrep-install.d.ts +12 -0
- package/dist/src/ripgrep-install.js +169 -0
- package/dist/src/ripgrep-resolve.d.ts +13 -0
- package/dist/src/ripgrep-resolve.js +68 -0
- package/dist/src/ripgrep.d.ts +3 -36
- package/dist/src/ripgrep.js +2 -262
- package/dist/src/similarity.d.ts +1 -2
- package/dist/src/similarity.js +11 -0
- package/dist/src/stash-add.d.ts +4 -0
- package/dist/src/stash-add.js +59 -0
- package/dist/src/stash-ref.d.ts +7 -0
- package/dist/src/stash-ref.js +33 -0
- package/dist/src/stash-registry.d.ts +18 -0
- package/dist/src/stash-registry.js +221 -0
- package/dist/src/stash-resolve.d.ts +2 -0
- package/dist/src/stash-resolve.js +45 -0
- package/dist/src/stash-search.d.ts +8 -0
- package/dist/src/stash-search.js +484 -0
- package/dist/src/stash-show.d.ts +5 -0
- package/dist/src/stash-show.js +114 -0
- package/dist/src/stash-types.d.ts +217 -0
- package/dist/src/stash-types.js +1 -0
- package/dist/src/stash.d.ts +10 -63
- package/dist/src/stash.js +6 -633
- package/dist/src/tool-runner.d.ts +35 -0
- package/dist/src/tool-runner.js +100 -0
- package/dist/src/walker.d.ts +19 -0
- package/dist/src/walker.js +47 -0
- package/package.json +8 -14
- package/src/asset-spec.ts +69 -0
- package/src/cli.ts +282 -46
- package/src/common.ts +58 -0
- package/src/config.ts +183 -0
- package/src/embedder.ts +117 -0
- package/src/frontmatter.ts +95 -0
- package/src/indexer.ts +244 -84
- package/src/init.ts +106 -0
- package/src/llm.ts +124 -0
- package/src/markdown.ts +106 -0
- package/src/metadata.ts +171 -27
- package/src/registry-install.ts +245 -0
- package/src/registry-resolve.ts +272 -0
- package/src/registry-search.ts +145 -0
- package/src/registry-types.ts +64 -0
- package/src/ripgrep-install.ts +200 -0
- package/src/ripgrep-resolve.ts +72 -0
- package/src/ripgrep.ts +3 -315
- package/src/similarity.ts +13 -1
- package/src/stash-add.ts +66 -0
- package/src/stash-ref.ts +41 -0
- package/src/stash-registry.ts +259 -0
- package/src/stash-resolve.ts +47 -0
- package/src/stash-search.ts +595 -0
- package/src/stash-show.ts +112 -0
- package/src/stash-types.ts +221 -0
- package/src/stash.ts +31 -760
- package/src/tool-runner.ts +129 -0
- package/src/walker.ts +53 -0
- package/.claude-plugin/plugin.json +0 -21
- package/commands/open.md +0 -11
- package/commands/run.md +0 -11
- package/commands/search.md +0 -11
- package/dist/src/plugin.d.ts +0 -2
- package/dist/src/plugin.js +0 -55
- package/skills/stash/SKILL.md +0 -73
- package/src/plugin.ts +0 -56
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { spawnSync } from "node:child_process";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { IS_WINDOWS } from "./common";
|
|
5
|
+
import { RG_BINARY, resolveRg } from "./ripgrep-resolve";
|
|
6
|
+
/**
|
|
7
|
+
* Platform and architecture detection for ripgrep binary downloads.
|
|
8
|
+
*/
|
|
9
|
+
function getRgPlatformTarget() {
|
|
10
|
+
const platform = process.platform;
|
|
11
|
+
const arch = process.arch;
|
|
12
|
+
if (platform === "linux" && arch === "x64") {
|
|
13
|
+
return { platform: "x86_64-unknown-linux-musl", arch: "x64", ext: ".tar.gz" };
|
|
14
|
+
}
|
|
15
|
+
if (platform === "linux" && arch === "arm64") {
|
|
16
|
+
return { platform: "aarch64-unknown-linux-gnu", arch: "arm64", ext: ".tar.gz" };
|
|
17
|
+
}
|
|
18
|
+
if (platform === "darwin" && arch === "x64") {
|
|
19
|
+
return { platform: "x86_64-apple-darwin", arch: "x64", ext: ".tar.gz" };
|
|
20
|
+
}
|
|
21
|
+
if (platform === "darwin" && arch === "arm64") {
|
|
22
|
+
return { platform: "aarch64-apple-darwin", arch: "arm64", ext: ".tar.gz" };
|
|
23
|
+
}
|
|
24
|
+
if (platform === "win32" && arch === "x64") {
|
|
25
|
+
return { platform: "x86_64-pc-windows-msvc", arch: "x64", ext: ".zip" };
|
|
26
|
+
}
|
|
27
|
+
return null;
|
|
28
|
+
}
|
|
29
|
+
const RG_VERSION = "14.1.1";
|
|
30
|
+
/**
|
|
31
|
+
* Ensure ripgrep is available. If not found on PATH or in stash/bin,
|
|
32
|
+
* download and install it to stash/bin.
|
|
33
|
+
*
|
|
34
|
+
* Returns the path to the ripgrep binary and whether it was newly installed.
|
|
35
|
+
*/
|
|
36
|
+
export function ensureRg(stashDir) {
|
|
37
|
+
// Already available?
|
|
38
|
+
const existing = resolveRg(stashDir);
|
|
39
|
+
if (existing) {
|
|
40
|
+
return { rgPath: existing, installed: false, version: getRgVersion(existing) };
|
|
41
|
+
}
|
|
42
|
+
// Determine platform
|
|
43
|
+
const target = getRgPlatformTarget();
|
|
44
|
+
if (!target) {
|
|
45
|
+
throw new Error(`Unsupported platform for ripgrep auto-install: ${process.platform}/${process.arch}. ` +
|
|
46
|
+
`Install ripgrep manually: https://github.com/BurntSushi/ripgrep#installation`);
|
|
47
|
+
}
|
|
48
|
+
const binDir = path.join(stashDir, "bin");
|
|
49
|
+
if (!fs.existsSync(binDir)) {
|
|
50
|
+
fs.mkdirSync(binDir, { recursive: true });
|
|
51
|
+
}
|
|
52
|
+
const archiveName = `ripgrep-${RG_VERSION}-${target.platform}`;
|
|
53
|
+
const url = `https://github.com/BurntSushi/ripgrep/releases/download/${RG_VERSION}/${archiveName}${target.ext}`;
|
|
54
|
+
const destBinary = path.join(binDir, RG_BINARY);
|
|
55
|
+
if (target.ext === ".tar.gz") {
|
|
56
|
+
downloadAndExtractTarGz(url, archiveName, destBinary);
|
|
57
|
+
}
|
|
58
|
+
else {
|
|
59
|
+
downloadAndExtractZip(url, archiveName, destBinary);
|
|
60
|
+
}
|
|
61
|
+
// Make executable
|
|
62
|
+
if (!IS_WINDOWS) {
|
|
63
|
+
fs.chmodSync(destBinary, 0o755);
|
|
64
|
+
}
|
|
65
|
+
return { rgPath: destBinary, installed: true, version: RG_VERSION };
|
|
66
|
+
}
|
|
67
|
+
function downloadAndExtractTarGz(url, archiveName, destBinary) {
|
|
68
|
+
const destDir = path.dirname(destBinary);
|
|
69
|
+
const tmpTarGz = path.join(destDir, "rg-download.tar.gz");
|
|
70
|
+
try {
|
|
71
|
+
// Download archive to a temporary file without using a shell
|
|
72
|
+
const curlResult = spawnSync("curl", ["-fsSL", "-o", tmpTarGz, url], {
|
|
73
|
+
encoding: "utf8",
|
|
74
|
+
timeout: 60_000,
|
|
75
|
+
});
|
|
76
|
+
if (curlResult.status !== 0) {
|
|
77
|
+
const err = curlResult.stderr?.trim() || curlResult.error?.message || "unknown error";
|
|
78
|
+
throw new Error(`Failed to download ripgrep from ${url}: ${err}`);
|
|
79
|
+
}
|
|
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
|
+
], {
|
|
89
|
+
encoding: "utf8",
|
|
90
|
+
timeout: 60_000,
|
|
91
|
+
});
|
|
92
|
+
if (tarResult.status !== 0) {
|
|
93
|
+
const err = tarResult.stderr?.trim() || tarResult.error?.message || "unknown error";
|
|
94
|
+
throw new Error(`Failed to extract ripgrep from ${url}: ${err}`);
|
|
95
|
+
}
|
|
96
|
+
if (!fs.existsSync(destBinary)) {
|
|
97
|
+
throw new Error(`ripgrep binary not found at ${destBinary} after extraction`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
finally {
|
|
101
|
+
// Best-effort cleanup of temporary archive
|
|
102
|
+
try {
|
|
103
|
+
if (fs.existsSync(tmpTarGz)) {
|
|
104
|
+
fs.unlinkSync(tmpTarGz);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
catch {
|
|
108
|
+
// ignore cleanup errors
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function downloadAndExtractZip(url, archiveName, destBinary) {
|
|
113
|
+
const destDir = path.dirname(destBinary);
|
|
114
|
+
const tmpZip = path.join(destDir, "rg-download.zip");
|
|
115
|
+
const expandedDir = path.join(destDir, archiveName);
|
|
116
|
+
try {
|
|
117
|
+
// Download
|
|
118
|
+
const dlResult = spawnSync("curl", ["-fsSL", "-o", tmpZip, url], {
|
|
119
|
+
encoding: "utf8",
|
|
120
|
+
timeout: 60_000,
|
|
121
|
+
});
|
|
122
|
+
if (dlResult.status !== 0) {
|
|
123
|
+
throw new Error(dlResult.stderr?.trim() || "download failed");
|
|
124
|
+
}
|
|
125
|
+
// Extract the zip archive using separate spawnSync calls with argument arrays
|
|
126
|
+
// 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
|
+
], {
|
|
134
|
+
encoding: "utf8",
|
|
135
|
+
timeout: 60_000,
|
|
136
|
+
});
|
|
137
|
+
if (expandResult.status !== 0) {
|
|
138
|
+
throw new Error(expandResult.stderr?.trim() || "extraction failed");
|
|
139
|
+
}
|
|
140
|
+
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
|
+
], {
|
|
148
|
+
encoding: "utf8",
|
|
149
|
+
timeout: 60_000,
|
|
150
|
+
});
|
|
151
|
+
if (moveResult.status !== 0) {
|
|
152
|
+
throw new Error(moveResult.stderr?.trim() || "move failed");
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
if (fs.existsSync(tmpZip))
|
|
157
|
+
fs.unlinkSync(tmpZip);
|
|
158
|
+
if (fs.existsSync(expandedDir))
|
|
159
|
+
fs.rmSync(expandedDir, { recursive: true, force: true });
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
function getRgVersion(rgPath) {
|
|
163
|
+
const result = spawnSync(rgPath, ["--version"], { encoding: "utf8", timeout: 5_000 });
|
|
164
|
+
if (result.status === 0 && result.stdout) {
|
|
165
|
+
const match = result.stdout.match(/ripgrep\s+([\d.]+)/);
|
|
166
|
+
return match ? match[1] : "unknown";
|
|
167
|
+
}
|
|
168
|
+
return "unknown";
|
|
169
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export declare const RG_BINARY: string;
|
|
2
|
+
/**
|
|
3
|
+
* Resolve the path to a usable ripgrep binary.
|
|
4
|
+
* Checks in order:
|
|
5
|
+
* 1. stashDir/bin/rg
|
|
6
|
+
* 2. system PATH (rg)
|
|
7
|
+
* Returns null if ripgrep is not available.
|
|
8
|
+
*/
|
|
9
|
+
export declare function resolveRg(stashDir?: string): string | null;
|
|
10
|
+
/**
|
|
11
|
+
* Check if ripgrep is available (either in stash/bin or system PATH).
|
|
12
|
+
*/
|
|
13
|
+
export declare function isRgAvailable(stashDir?: string): boolean;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import fs from "node:fs";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { IS_WINDOWS } from "./common";
|
|
4
|
+
export const RG_BINARY = IS_WINDOWS ? "rg.exe" : "rg";
|
|
5
|
+
function canExecute(filePath) {
|
|
6
|
+
if (!fs.existsSync(filePath))
|
|
7
|
+
return false;
|
|
8
|
+
if (IS_WINDOWS)
|
|
9
|
+
return true;
|
|
10
|
+
try {
|
|
11
|
+
fs.accessSync(filePath, fs.constants.X_OK);
|
|
12
|
+
return true;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
function resolveFromPath() {
|
|
19
|
+
const rawPath = process.env.PATH;
|
|
20
|
+
if (!rawPath)
|
|
21
|
+
return null;
|
|
22
|
+
const pathEntries = rawPath.split(path.delimiter).filter(Boolean);
|
|
23
|
+
if (IS_WINDOWS) {
|
|
24
|
+
const pathext = (process.env.PATHEXT || ".EXE;.CMD;.BAT;.COM")
|
|
25
|
+
.split(";")
|
|
26
|
+
.filter(Boolean)
|
|
27
|
+
.map((ext) => ext.toLowerCase());
|
|
28
|
+
for (const entry of pathEntries) {
|
|
29
|
+
const directCandidate = path.join(entry, "rg");
|
|
30
|
+
if (canExecute(directCandidate))
|
|
31
|
+
return directCandidate;
|
|
32
|
+
for (const ext of pathext) {
|
|
33
|
+
const candidate = path.join(entry, `rg${ext}`);
|
|
34
|
+
if (canExecute(candidate))
|
|
35
|
+
return candidate;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
for (const entry of pathEntries) {
|
|
41
|
+
const candidate = path.join(entry, "rg");
|
|
42
|
+
if (canExecute(candidate))
|
|
43
|
+
return candidate;
|
|
44
|
+
}
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolve the path to a usable ripgrep binary.
|
|
49
|
+
* Checks in order:
|
|
50
|
+
* 1. stashDir/bin/rg
|
|
51
|
+
* 2. system PATH (rg)
|
|
52
|
+
* Returns null if ripgrep is not available.
|
|
53
|
+
*/
|
|
54
|
+
export function resolveRg(stashDir) {
|
|
55
|
+
// Check stash bin directory first
|
|
56
|
+
if (stashDir) {
|
|
57
|
+
const stashRg = path.join(stashDir, "bin", RG_BINARY);
|
|
58
|
+
if (canExecute(stashRg))
|
|
59
|
+
return stashRg;
|
|
60
|
+
}
|
|
61
|
+
return resolveFromPath();
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Check if ripgrep is available (either in stash/bin or system PATH).
|
|
65
|
+
*/
|
|
66
|
+
export function isRgAvailable(stashDir) {
|
|
67
|
+
return resolveRg(stashDir) !== null;
|
|
68
|
+
}
|
package/dist/src/ripgrep.d.ts
CHANGED
|
@@ -1,36 +1,3 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
* 1. stashDir/bin/rg
|
|
5
|
-
* 2. system PATH (rg)
|
|
6
|
-
* Returns null if ripgrep is not available.
|
|
7
|
-
*/
|
|
8
|
-
export declare function resolveRg(stashDir?: string): string | null;
|
|
9
|
-
/**
|
|
10
|
-
* Check if ripgrep is available (either in stash/bin or system PATH).
|
|
11
|
-
*/
|
|
12
|
-
export declare function isRgAvailable(stashDir?: string): boolean;
|
|
13
|
-
export interface RgCandidateResult {
|
|
14
|
-
matchedFiles: string[];
|
|
15
|
-
usedRg: boolean;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Use ripgrep to find .stash.json files that match query tokens.
|
|
19
|
-
* Returns paths to matching .stash.json files.
|
|
20
|
-
*
|
|
21
|
-
* If ripgrep is not available or the query is empty, returns null
|
|
22
|
-
* to signal that the caller should skip pre-filtering.
|
|
23
|
-
*/
|
|
24
|
-
export declare function rgFilterCandidates(query: string, searchDir: string, stashDir?: string): RgCandidateResult | null;
|
|
25
|
-
export interface EnsureRgResult {
|
|
26
|
-
rgPath: string;
|
|
27
|
-
installed: boolean;
|
|
28
|
-
version: string;
|
|
29
|
-
}
|
|
30
|
-
/**
|
|
31
|
-
* Ensure ripgrep is available. If not found on PATH or in stash/bin,
|
|
32
|
-
* download and install it to stash/bin.
|
|
33
|
-
*
|
|
34
|
-
* Returns the path to the ripgrep binary and whether it was newly installed.
|
|
35
|
-
*/
|
|
36
|
-
export declare function ensureRg(stashDir: string): EnsureRgResult;
|
|
1
|
+
export { resolveRg, isRgAvailable } from "./ripgrep-resolve";
|
|
2
|
+
export { ensureRg } from "./ripgrep-install";
|
|
3
|
+
export type { EnsureRgResult } from "./ripgrep-install";
|
package/dist/src/ripgrep.js
CHANGED
|
@@ -1,262 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
import path from "node:path";
|
|
4
|
-
// ── ripgrep Resolution ──────────────────────────────────────────────────────
|
|
5
|
-
const IS_WINDOWS = process.platform === "win32";
|
|
6
|
-
const RG_BINARY = IS_WINDOWS ? "rg.exe" : "rg";
|
|
7
|
-
function canExecute(filePath) {
|
|
8
|
-
if (!fs.existsSync(filePath))
|
|
9
|
-
return false;
|
|
10
|
-
if (IS_WINDOWS)
|
|
11
|
-
return true;
|
|
12
|
-
try {
|
|
13
|
-
fs.accessSync(filePath, fs.constants.X_OK);
|
|
14
|
-
return true;
|
|
15
|
-
}
|
|
16
|
-
catch {
|
|
17
|
-
return false;
|
|
18
|
-
}
|
|
19
|
-
}
|
|
20
|
-
function resolveFromPath() {
|
|
21
|
-
const rawPath = process.env.PATH;
|
|
22
|
-
if (!rawPath)
|
|
23
|
-
return null;
|
|
24
|
-
const pathEntries = rawPath.split(path.delimiter).filter(Boolean);
|
|
25
|
-
if (IS_WINDOWS) {
|
|
26
|
-
const pathext = (process.env.PATHEXT || ".EXE;.CMD;.BAT;.COM")
|
|
27
|
-
.split(";")
|
|
28
|
-
.filter(Boolean)
|
|
29
|
-
.map((ext) => ext.toLowerCase());
|
|
30
|
-
for (const entry of pathEntries) {
|
|
31
|
-
const directCandidate = path.join(entry, "rg");
|
|
32
|
-
if (canExecute(directCandidate))
|
|
33
|
-
return directCandidate;
|
|
34
|
-
for (const ext of pathext) {
|
|
35
|
-
const candidate = path.join(entry, `rg${ext}`);
|
|
36
|
-
if (canExecute(candidate))
|
|
37
|
-
return candidate;
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
return null;
|
|
41
|
-
}
|
|
42
|
-
for (const entry of pathEntries) {
|
|
43
|
-
const candidate = path.join(entry, "rg");
|
|
44
|
-
if (canExecute(candidate))
|
|
45
|
-
return candidate;
|
|
46
|
-
}
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Resolve the path to a usable ripgrep binary.
|
|
51
|
-
* Checks in order:
|
|
52
|
-
* 1. stashDir/bin/rg
|
|
53
|
-
* 2. system PATH (rg)
|
|
54
|
-
* Returns null if ripgrep is not available.
|
|
55
|
-
*/
|
|
56
|
-
export function resolveRg(stashDir) {
|
|
57
|
-
// Check stash bin directory first
|
|
58
|
-
if (stashDir) {
|
|
59
|
-
const stashRg = path.join(stashDir, "bin", RG_BINARY);
|
|
60
|
-
if (canExecute(stashRg))
|
|
61
|
-
return stashRg;
|
|
62
|
-
}
|
|
63
|
-
return resolveFromPath();
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Check if ripgrep is available (either in stash/bin or system PATH).
|
|
67
|
-
*/
|
|
68
|
-
export function isRgAvailable(stashDir) {
|
|
69
|
-
return resolveRg(stashDir) !== null;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Use ripgrep to find .stash.json files that match query tokens.
|
|
73
|
-
* Returns paths to matching .stash.json files.
|
|
74
|
-
*
|
|
75
|
-
* If ripgrep is not available or the query is empty, returns null
|
|
76
|
-
* to signal that the caller should skip pre-filtering.
|
|
77
|
-
*/
|
|
78
|
-
export function rgFilterCandidates(query, searchDir, stashDir) {
|
|
79
|
-
if (!query.trim())
|
|
80
|
-
return null;
|
|
81
|
-
const rgPath = resolveRg(stashDir);
|
|
82
|
-
if (!rgPath)
|
|
83
|
-
return null;
|
|
84
|
-
// Tokenize the query into an OR pattern for ripgrep
|
|
85
|
-
const tokens = query
|
|
86
|
-
.toLowerCase()
|
|
87
|
-
.replace(/[^a-z0-9\s]/g, " ")
|
|
88
|
-
.split(/\s+/)
|
|
89
|
-
.filter((t) => t.length > 1);
|
|
90
|
-
if (tokens.length === 0)
|
|
91
|
-
return null;
|
|
92
|
-
const pattern = tokens.join("|");
|
|
93
|
-
const result = spawnSync(rgPath, [
|
|
94
|
-
"-i", // case insensitive
|
|
95
|
-
"-l", // files-with-matches only
|
|
96
|
-
"--hidden", // include hidden files such as .stash.json
|
|
97
|
-
"--no-ignore", // include ignored files to ensure metadata is searchable
|
|
98
|
-
"--glob", ".stash.json", // only search .stash.json files
|
|
99
|
-
pattern,
|
|
100
|
-
searchDir,
|
|
101
|
-
], {
|
|
102
|
-
encoding: "utf8",
|
|
103
|
-
timeout: 10_000,
|
|
104
|
-
});
|
|
105
|
-
if (result.status !== 0 && result.status !== 1) {
|
|
106
|
-
// rg exit code 1 = no matches (normal), anything else = error
|
|
107
|
-
return null;
|
|
108
|
-
}
|
|
109
|
-
const files = (result.stdout || "")
|
|
110
|
-
.trim()
|
|
111
|
-
.split(/\r?\n/)
|
|
112
|
-
.filter((f) => f.length > 0);
|
|
113
|
-
return { matchedFiles: files, usedRg: true };
|
|
114
|
-
}
|
|
115
|
-
// ── ripgrep Installation ────────────────────────────────────────────────────
|
|
116
|
-
/**
|
|
117
|
-
* Platform and architecture detection for ripgrep binary downloads.
|
|
118
|
-
*/
|
|
119
|
-
function getRgPlatformTarget() {
|
|
120
|
-
const platform = process.platform;
|
|
121
|
-
const arch = process.arch;
|
|
122
|
-
if (platform === "linux" && arch === "x64") {
|
|
123
|
-
return { platform: "x86_64-unknown-linux-musl", arch: "x64", ext: ".tar.gz" };
|
|
124
|
-
}
|
|
125
|
-
if (platform === "linux" && arch === "arm64") {
|
|
126
|
-
return { platform: "aarch64-unknown-linux-gnu", arch: "arm64", ext: ".tar.gz" };
|
|
127
|
-
}
|
|
128
|
-
if (platform === "darwin" && arch === "x64") {
|
|
129
|
-
return { platform: "x86_64-apple-darwin", arch: "x64", ext: ".tar.gz" };
|
|
130
|
-
}
|
|
131
|
-
if (platform === "darwin" && arch === "arm64") {
|
|
132
|
-
return { platform: "aarch64-apple-darwin", arch: "arm64", ext: ".tar.gz" };
|
|
133
|
-
}
|
|
134
|
-
if (platform === "win32" && arch === "x64") {
|
|
135
|
-
return { platform: "x86_64-pc-windows-msvc", arch: "x64", ext: ".zip" };
|
|
136
|
-
}
|
|
137
|
-
return null;
|
|
138
|
-
}
|
|
139
|
-
const RG_VERSION = "14.1.1";
|
|
140
|
-
/**
|
|
141
|
-
* Ensure ripgrep is available. If not found on PATH or in stash/bin,
|
|
142
|
-
* download and install it to stash/bin.
|
|
143
|
-
*
|
|
144
|
-
* Returns the path to the ripgrep binary and whether it was newly installed.
|
|
145
|
-
*/
|
|
146
|
-
export function ensureRg(stashDir) {
|
|
147
|
-
// Already available?
|
|
148
|
-
const existing = resolveRg(stashDir);
|
|
149
|
-
if (existing) {
|
|
150
|
-
return { rgPath: existing, installed: false, version: getRgVersion(existing) };
|
|
151
|
-
}
|
|
152
|
-
// Determine platform
|
|
153
|
-
const target = getRgPlatformTarget();
|
|
154
|
-
if (!target) {
|
|
155
|
-
throw new Error(`Unsupported platform for ripgrep auto-install: ${process.platform}/${process.arch}. ` +
|
|
156
|
-
`Install ripgrep manually: https://github.com/BurntSushi/ripgrep#installation`);
|
|
157
|
-
}
|
|
158
|
-
const binDir = path.join(stashDir, "bin");
|
|
159
|
-
if (!fs.existsSync(binDir)) {
|
|
160
|
-
fs.mkdirSync(binDir, { recursive: true });
|
|
161
|
-
}
|
|
162
|
-
const archiveName = `ripgrep-${RG_VERSION}-${target.platform}`;
|
|
163
|
-
const url = `https://github.com/BurntSushi/ripgrep/releases/download/${RG_VERSION}/${archiveName}${target.ext}`;
|
|
164
|
-
const destBinary = path.join(binDir, RG_BINARY);
|
|
165
|
-
if (target.ext === ".tar.gz") {
|
|
166
|
-
downloadAndExtractTarGz(url, archiveName, destBinary);
|
|
167
|
-
}
|
|
168
|
-
else {
|
|
169
|
-
downloadAndExtractZip(url, archiveName, destBinary);
|
|
170
|
-
}
|
|
171
|
-
// Make executable
|
|
172
|
-
if (!IS_WINDOWS) {
|
|
173
|
-
fs.chmodSync(destBinary, 0o755);
|
|
174
|
-
}
|
|
175
|
-
return { rgPath: destBinary, installed: true, version: RG_VERSION };
|
|
176
|
-
}
|
|
177
|
-
function downloadAndExtractTarGz(url, archiveName, destBinary) {
|
|
178
|
-
const destDir = path.dirname(destBinary);
|
|
179
|
-
const tmpTarGz = path.join(destDir, "rg-download.tar.gz");
|
|
180
|
-
try {
|
|
181
|
-
// Download archive to a temporary file without using a shell
|
|
182
|
-
const curlResult = spawnSync("curl", ["-fsSL", "-o", tmpTarGz, url], {
|
|
183
|
-
encoding: "utf8",
|
|
184
|
-
timeout: 60_000,
|
|
185
|
-
});
|
|
186
|
-
if (curlResult.status !== 0) {
|
|
187
|
-
const err = curlResult.stderr?.trim() || curlResult.error?.message || "unknown error";
|
|
188
|
-
throw new Error(`Failed to download ripgrep from ${url}: ${err}`);
|
|
189
|
-
}
|
|
190
|
-
// Extract the specific binary from the archive into destDir
|
|
191
|
-
const tarResult = spawnSync("tar", [
|
|
192
|
-
"xzf",
|
|
193
|
-
tmpTarGz,
|
|
194
|
-
"--strip-components=1",
|
|
195
|
-
"-C",
|
|
196
|
-
destDir,
|
|
197
|
-
`${archiveName}/rg`,
|
|
198
|
-
], {
|
|
199
|
-
encoding: "utf8",
|
|
200
|
-
timeout: 60_000,
|
|
201
|
-
});
|
|
202
|
-
if (tarResult.status !== 0) {
|
|
203
|
-
const err = tarResult.stderr?.trim() || tarResult.error?.message || "unknown error";
|
|
204
|
-
throw new Error(`Failed to extract ripgrep from ${url}: ${err}`);
|
|
205
|
-
}
|
|
206
|
-
if (!fs.existsSync(destBinary)) {
|
|
207
|
-
throw new Error(`ripgrep binary not found at ${destBinary} after extraction`);
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
finally {
|
|
211
|
-
// Best-effort cleanup of temporary archive
|
|
212
|
-
try {
|
|
213
|
-
if (fs.existsSync(tmpTarGz)) {
|
|
214
|
-
fs.unlinkSync(tmpTarGz);
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
catch {
|
|
218
|
-
// ignore cleanup errors
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
function downloadAndExtractZip(url, archiveName, destBinary) {
|
|
223
|
-
const destDir = path.dirname(destBinary);
|
|
224
|
-
const tmpZip = path.join(destDir, "rg-download.zip");
|
|
225
|
-
const expandedDir = path.join(destDir, archiveName);
|
|
226
|
-
try {
|
|
227
|
-
// Download
|
|
228
|
-
const dlResult = spawnSync("curl", ["-fsSL", "-o", tmpZip, url], {
|
|
229
|
-
encoding: "utf8",
|
|
230
|
-
timeout: 60_000,
|
|
231
|
-
});
|
|
232
|
-
if (dlResult.status !== 0) {
|
|
233
|
-
throw new Error(dlResult.stderr?.trim() || "download failed");
|
|
234
|
-
}
|
|
235
|
-
// Extract just the rg.exe
|
|
236
|
-
const extractResult = spawnSync("powershell", [
|
|
237
|
-
"-Command",
|
|
238
|
-
`Expand-Archive -Path "${tmpZip}" -DestinationPath "${destDir}" -Force; ` +
|
|
239
|
-
`Move-Item -Force "${path.join(destDir, archiveName, "rg.exe")}" "${destBinary}"`,
|
|
240
|
-
], {
|
|
241
|
-
encoding: "utf8",
|
|
242
|
-
timeout: 60_000,
|
|
243
|
-
});
|
|
244
|
-
if (extractResult.status !== 0) {
|
|
245
|
-
throw new Error(extractResult.stderr?.trim() || "extraction failed");
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
finally {
|
|
249
|
-
if (fs.existsSync(tmpZip))
|
|
250
|
-
fs.unlinkSync(tmpZip);
|
|
251
|
-
if (fs.existsSync(expandedDir))
|
|
252
|
-
fs.rmSync(expandedDir, { recursive: true, force: true });
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
function getRgVersion(rgPath) {
|
|
256
|
-
const result = spawnSync(rgPath, ["--version"], { encoding: "utf8", timeout: 5_000 });
|
|
257
|
-
if (result.status === 0 && result.stdout) {
|
|
258
|
-
const match = result.stdout.match(/ripgrep\s+([\d.]+)/);
|
|
259
|
-
return match ? match[1] : "unknown";
|
|
260
|
-
}
|
|
261
|
-
return "unknown";
|
|
262
|
-
}
|
|
1
|
+
export { resolveRg, isRgAvailable } from "./ripgrep-resolve";
|
|
2
|
+
export { ensureRg } from "./ripgrep-install";
|
package/dist/src/similarity.d.ts
CHANGED
|
@@ -14,7 +14,7 @@ export interface SearchAdapter {
|
|
|
14
14
|
buildIndex(entries: ScoredEntry[]): void;
|
|
15
15
|
search(query: string, limit: number, typeFilter?: string): ScoredResult[];
|
|
16
16
|
}
|
|
17
|
-
interface SerializedTfIdf {
|
|
17
|
+
export interface SerializedTfIdf {
|
|
18
18
|
idf: Record<string, number>;
|
|
19
19
|
docs: Array<{
|
|
20
20
|
id: string;
|
|
@@ -32,4 +32,3 @@ export declare class TfIdfAdapter implements SearchAdapter {
|
|
|
32
32
|
static deserialize(data: SerializedTfIdf, entries: ScoredEntry[]): TfIdfAdapter;
|
|
33
33
|
private substringFallback;
|
|
34
34
|
}
|
|
35
|
-
export {};
|
package/dist/src/similarity.js
CHANGED
|
@@ -94,6 +94,17 @@ export class TfIdfAdapter {
|
|
|
94
94
|
score += 0.15;
|
|
95
95
|
}
|
|
96
96
|
}
|
|
97
|
+
// Boost: intent phrase contains query token
|
|
98
|
+
const intents = doc.entry.entry.intents || [];
|
|
99
|
+
for (const intent of intents) {
|
|
100
|
+
const intentLower = intent.toLowerCase();
|
|
101
|
+
for (const token of queryTokens) {
|
|
102
|
+
if (intentLower.includes(token)) {
|
|
103
|
+
score += 0.12;
|
|
104
|
+
break; // one boost per intent phrase
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
97
108
|
// Boost: name contains query token
|
|
98
109
|
const nameLower = doc.entry.entry.name.toLowerCase().replace(/[-_]/g, " ");
|
|
99
110
|
for (const token of queryTokens) {
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { agentikitIndex } from "./indexer";
|
|
2
|
+
import fs from "node:fs";
|
|
3
|
+
import { resolveStashDir } from "./common";
|
|
4
|
+
import { loadConfig } from "./config";
|
|
5
|
+
import { upsertInstalledRegistryEntry, installRegistryRef } from "./registry-install";
|
|
6
|
+
export async function agentikitAdd(input) {
|
|
7
|
+
const ref = input.ref.trim();
|
|
8
|
+
if (!ref)
|
|
9
|
+
throw new Error("Registry ref is required.");
|
|
10
|
+
const stashDir = resolveStashDir();
|
|
11
|
+
const installed = await installRegistryRef(ref);
|
|
12
|
+
const replaced = loadConfig(stashDir).registry?.installed.find((entry) => entry.id === installed.id);
|
|
13
|
+
const config = upsertInstalledRegistryEntry({
|
|
14
|
+
id: installed.id,
|
|
15
|
+
source: installed.source,
|
|
16
|
+
ref: installed.ref,
|
|
17
|
+
artifactUrl: installed.artifactUrl,
|
|
18
|
+
resolvedVersion: installed.resolvedVersion,
|
|
19
|
+
resolvedRevision: installed.resolvedRevision,
|
|
20
|
+
stashRoot: installed.stashRoot,
|
|
21
|
+
cacheDir: installed.cacheDir,
|
|
22
|
+
installedAt: installed.installedAt,
|
|
23
|
+
}, stashDir);
|
|
24
|
+
if (replaced && replaced.cacheDir !== installed.cacheDir) {
|
|
25
|
+
try {
|
|
26
|
+
fs.rmSync(replaced.cacheDir, { recursive: true, force: true });
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
// Best-effort cleanup only.
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
const index = await agentikitIndex({ stashDir });
|
|
33
|
+
return {
|
|
34
|
+
stashDir,
|
|
35
|
+
ref,
|
|
36
|
+
installed: {
|
|
37
|
+
id: installed.id,
|
|
38
|
+
source: installed.source,
|
|
39
|
+
ref: installed.ref,
|
|
40
|
+
artifactUrl: installed.artifactUrl,
|
|
41
|
+
resolvedVersion: installed.resolvedVersion,
|
|
42
|
+
resolvedRevision: installed.resolvedRevision,
|
|
43
|
+
stashRoot: installed.stashRoot,
|
|
44
|
+
cacheDir: installed.cacheDir,
|
|
45
|
+
extractedDir: installed.extractedDir,
|
|
46
|
+
installedAt: installed.installedAt,
|
|
47
|
+
},
|
|
48
|
+
config: {
|
|
49
|
+
additionalStashDirs: config.additionalStashDirs,
|
|
50
|
+
installedRegistryCount: config.registry?.installed.length ?? 0,
|
|
51
|
+
},
|
|
52
|
+
index: {
|
|
53
|
+
mode: index.mode,
|
|
54
|
+
totalEntries: index.totalEntries,
|
|
55
|
+
directoriesScanned: index.directoriesScanned,
|
|
56
|
+
directoriesSkipped: index.directoriesSkipped,
|
|
57
|
+
},
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type AgentikitAssetType } from "./common";
|
|
2
|
+
export interface OpenRef {
|
|
3
|
+
type: AgentikitAssetType;
|
|
4
|
+
name: string;
|
|
5
|
+
}
|
|
6
|
+
export declare function parseOpenRef(ref: string): OpenRef;
|
|
7
|
+
export declare function makeOpenRef(type: AgentikitAssetType, name: string): string;
|