skilld 0.13.3 → 0.13.5

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.
@@ -1,8 +1,11 @@
1
- import { n as __require } from "./chunk.mjs";
1
+ import { n as __require, t as __exportAll } from "./chunk.mjs";
2
2
  import { t as CACHE_DIR } from "./config.mjs";
3
3
  import { join } from "pathe";
4
4
  import { rmSync } from "node:fs";
5
- import { cachedEmbeddings } from "retriv/embeddings/cached";
5
+ var embedding_cache_exports = /* @__PURE__ */ __exportAll({
6
+ cachedEmbeddings: () => cachedEmbeddings,
7
+ clearEmbeddingCache: () => clearEmbeddingCache
8
+ });
6
9
  const EMBEDDINGS_DB_PATH = join(CACHE_DIR, "embeddings.db");
7
10
  function openDb() {
8
11
  const { DatabaseSync: DB } = __require("node:sqlite");
@@ -28,11 +31,12 @@ function createSqliteStorage(db) {
28
31
  }
29
32
  };
30
33
  }
31
- function cachedEmbeddings$1(config) {
34
+ async function cachedEmbeddings(config) {
35
+ const { cachedEmbeddings: retrivCached } = await import("retriv/embeddings/cached");
32
36
  const db = openDb();
33
37
  const storage = createSqliteStorage(db);
34
38
  const originalResolve = config.resolve;
35
- return cachedEmbeddings({ async resolve() {
39
+ return retrivCached({ async resolve() {
36
40
  const resolved = await originalResolve();
37
41
  const getMetaStmt = db.prepare("SELECT value FROM meta WHERE key = ?");
38
42
  const setMetaStmt = db.prepare("INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)");
@@ -45,6 +49,6 @@ function cachedEmbeddings$1(config) {
45
49
  function clearEmbeddingCache() {
46
50
  rmSync(EMBEDDINGS_DB_PATH, { force: true });
47
51
  }
48
- export { clearEmbeddingCache as n, cachedEmbeddings$1 as t };
52
+ export { clearEmbeddingCache as n, embedding_cache_exports as r, cachedEmbeddings as t };
49
53
 
50
- //# sourceMappingURL=embedding-cache.mjs.map
54
+ //# sourceMappingURL=embedding-cache2.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"embedding-cache2.mjs","names":[],"sources":["../../src/retriv/embedding-cache.ts"],"sourcesContent":["import type { DatabaseSync } from 'node:sqlite'\nimport type { Embedding } from 'retriv'\nimport { rmSync } from 'node:fs'\nimport { join } from 'pathe'\nimport { CACHE_DIR } from '../cache/index.ts'\n\ninterface EmbeddingConfig {\n resolve: () => Promise<{ embedder: (texts: string[]) => Promise<Embedding[]>, dimensions: number, maxTokens?: number }>\n}\n\nconst EMBEDDINGS_DB_PATH = join(CACHE_DIR, 'embeddings.db')\n\nfunction openDb(): DatabaseSync {\n // eslint-disable-next-line ts/no-require-imports\n const { DatabaseSync: DB } = require('node:sqlite') as typeof import('node:sqlite')\n const db = new DB(EMBEDDINGS_DB_PATH)\n db.exec('PRAGMA journal_mode=WAL')\n db.exec('PRAGMA busy_timeout=5000')\n db.exec(`CREATE TABLE IF NOT EXISTS embeddings (text_hash TEXT PRIMARY KEY, embedding BLOB NOT NULL)`)\n db.exec(`CREATE TABLE IF NOT EXISTS meta (key TEXT PRIMARY KEY, value TEXT NOT NULL)`)\n return db\n}\n\nfunction createSqliteStorage(db: DatabaseSync) {\n const getStmt = db.prepare('SELECT embedding FROM embeddings WHERE text_hash = ?')\n const setStmt = db.prepare('INSERT OR IGNORE INTO embeddings (text_hash, embedding) VALUES (?, ?)')\n\n return {\n get: (hash: string): Embedding | null => {\n const row = getStmt.get(hash) as { embedding: Buffer } | undefined\n if (!row)\n return null\n return new Float32Array(row.embedding.buffer, row.embedding.byteOffset, row.embedding.byteLength / 4)\n },\n set: (hash: string, embedding: Embedding): void => {\n const arr = embedding instanceof Float32Array ? embedding : new Float32Array(embedding)\n setStmt.run(hash, Buffer.from(arr.buffer, arr.byteOffset, arr.byteLength))\n },\n }\n}\n\nexport async function cachedEmbeddings(config: EmbeddingConfig): Promise<EmbeddingConfig> {\n const { cachedEmbeddings: retrivCached } = await import('retriv/embeddings/cached')\n const db = openDb()\n const storage = createSqliteStorage(db)\n\n const originalResolve = config.resolve\n const validatedConfig: EmbeddingConfig = {\n async resolve() {\n const resolved = await originalResolve()\n const getMetaStmt = db.prepare('SELECT value FROM meta WHERE key = ?')\n const setMetaStmt = db.prepare('INSERT OR REPLACE INTO meta (key, value) VALUES (?, ?)')\n\n const storedDims = getMetaStmt.get('dimensions') as { value: string } | undefined\n if (storedDims && Number(storedDims.value) !== resolved.dimensions) {\n db.exec('DELETE FROM embeddings')\n }\n setMetaStmt.run('dimensions', String(resolved.dimensions))\n\n return resolved\n },\n }\n\n return retrivCached(validatedConfig, { storage })\n}\n\nexport function clearEmbeddingCache(): void {\n rmSync(EMBEDDINGS_DB_PATH, { force: true })\n}\n"],"mappings":";;;;;;;;AAUA,MAAM,qBAAqB,KAAK,WAAW,gBAAgB;AAE3D,SAAS,SAAuB;CAE9B,MAAM,EAAE,cAAc,OAAA,UAAe,cAAc;CACnD,MAAM,KAAK,IAAI,GAAG,mBAAmB;AACrC,IAAG,KAAK,0BAA0B;AAClC,IAAG,KAAK,2BAA2B;AACnC,IAAG,KAAK,8FAA8F;AACtG,IAAG,KAAK,8EAA8E;AACtF,QAAO;;AAGT,SAAS,oBAAoB,IAAkB;CAC7C,MAAM,UAAU,GAAG,QAAQ,uDAAuD;CAClF,MAAM,UAAU,GAAG,QAAQ,wEAAwE;AAEnG,QAAO;EACL,MAAM,SAAmC;GACvC,MAAM,MAAM,QAAQ,IAAI,KAAK;AAC7B,OAAI,CAAC,IACH,QAAO;AACT,UAAO,IAAI,aAAa,IAAI,UAAU,QAAQ,IAAI,UAAU,YAAY,IAAI,UAAU,aAAa,EAAE;;EAEvG,MAAM,MAAc,cAA+B;GACjD,MAAM,MAAM,qBAAqB,eAAe,YAAY,IAAI,aAAa,UAAU;AACvF,WAAQ,IAAI,MAAM,OAAO,KAAK,IAAI,QAAQ,IAAI,YAAY,IAAI,WAAW,CAAC;;EAE7E;;AAGH,eAAsB,iBAAiB,QAAmD;CACxF,MAAM,EAAE,kBAAkB,iBAAiB,MAAM,OAAO;CACxD,MAAM,KAAK,QAAQ;CACnB,MAAM,UAAU,oBAAoB,GAAG;CAEvC,MAAM,kBAAkB,OAAO;AAiB/B,QAAO,aAhBkC,EACvC,MAAM,UAAU;EACd,MAAM,WAAW,MAAM,iBAAiB;EACxC,MAAM,cAAc,GAAG,QAAQ,uCAAuC;EACtE,MAAM,cAAc,GAAG,QAAQ,yDAAyD;EAExF,MAAM,aAAa,YAAY,IAAI,aAAa;AAChD,MAAI,cAAc,OAAO,WAAW,MAAM,KAAK,SAAS,WACtD,IAAG,KAAK,yBAAyB;AAEnC,cAAY,IAAI,cAAc,OAAO,SAAS,WAAW,CAAC;AAE1D,SAAO;IAEV,EAEoC,EAAE,SAAS,CAAC;;AAGnD,SAAgB,sBAA4B;AAC1C,QAAO,oBAAoB,EAAE,OAAO,MAAM,CAAC"}
@@ -10,8 +10,9 @@ import { gt } from "semver";
10
10
  import { ofetch } from "ofetch";
11
11
  import { crawlAndGenerate } from "@mdream/crawl";
12
12
  import { globby } from "globby";
13
- import pLimit from "p-limit";
13
+ import { downloadTemplate } from "giget";
14
14
  import { fileURLToPath, pathToFileURL } from "node:url";
15
+ import pLimit from "p-limit";
15
16
  import { Writable } from "node:stream";
16
17
  import { resolvePathSync } from "mlly";
17
18
  const BOT_USERS = new Set([
@@ -1119,11 +1120,20 @@ function parseSkillFrontmatterName(content) {
1119
1120
  description: fm.description
1120
1121
  };
1121
1122
  }
1122
- const SUPPORTING_DIRS = [
1123
- "scripts",
1124
- "references",
1125
- "assets"
1126
- ];
1123
+ function collectFiles(dir, prefix = "") {
1124
+ const files = [];
1125
+ if (!existsSync(dir)) return files;
1126
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
1127
+ const relPath = prefix ? `${prefix}/${entry.name}` : entry.name;
1128
+ const fullPath = resolve(dir, entry.name);
1129
+ if (entry.isDirectory()) files.push(...collectFiles(fullPath, relPath));
1130
+ else if (entry.isFile()) files.push({
1131
+ path: relPath,
1132
+ content: readFileSync(fullPath, "utf-8")
1133
+ });
1134
+ }
1135
+ return files;
1136
+ }
1127
1137
  async function fetchGitSkills(source, onProgress) {
1128
1138
  if (source.type === "local") return fetchLocalSkills(source);
1129
1139
  if (source.type === "github") return fetchGitHubSkills(source, onProgress);
@@ -1153,18 +1163,7 @@ function readLocalSkill(dir, repoPath) {
1153
1163
  const frontmatter = parseSkillFrontmatterName(content);
1154
1164
  const dirName = dir.split("/").pop();
1155
1165
  const name = frontmatter.name || dirName;
1156
- const files = [];
1157
- for (const subdir of SUPPORTING_DIRS) {
1158
- const subdirPath = resolve(dir, subdir);
1159
- if (!existsSync(subdirPath)) continue;
1160
- for (const file of readdirSync(subdirPath, { withFileTypes: true })) {
1161
- if (!file.isFile()) continue;
1162
- files.push({
1163
- path: `${subdir}/${file.name}`,
1164
- content: readFileSync(resolve(subdirPath, file.name), "utf-8")
1165
- });
1166
- }
1167
- }
1166
+ const files = collectFiles(dir).filter((f) => f.path !== "SKILL.md");
1168
1167
  return {
1169
1168
  name,
1170
1169
  description: frontmatter.description || "",
@@ -1177,120 +1176,110 @@ async function fetchGitHubSkills(source, onProgress) {
1177
1176
  const { owner, repo } = source;
1178
1177
  if (!owner || !repo) return { skills: [] };
1179
1178
  const ref = source.ref || "main";
1180
- onProgress?.(`Listing files at ${owner}/${repo}@${ref}`);
1181
- const data = await $fetch(`https://ungh.cc/repos/${owner}/${repo}/files/${ref}`).catch(() => null);
1182
- if (!data?.files?.length) {
1183
- if (ref === "main") {
1184
- const fallback = await $fetch(`https://ungh.cc/repos/${owner}/${repo}/files/master`).catch(() => null);
1185
- if (fallback?.files?.length) return extractGitHubSkills(owner, repo, "master", fallback, source.skillPath, onProgress);
1186
- }
1187
- return { skills: [] };
1179
+ const refs = ref === "main" ? ["main", "master"] : [ref];
1180
+ for (const tryRef of refs) {
1181
+ const skills = await downloadGitHubSkills(owner, repo, tryRef, source.skillPath, onProgress);
1182
+ if (skills.length > 0) return { skills };
1188
1183
  }
1189
- return extractGitHubSkills(owner, repo, ref, data, source.skillPath, onProgress);
1190
- }
1191
- async function extractGitHubSkills(owner, repo, ref, data, skillPath, onProgress) {
1192
- const allFiles = data.files.map((f) => f.path);
1193
- const commitSha = data.meta?.sha;
1194
- let skillMdPaths;
1195
- if (skillPath) {
1196
- const candidates = [`${skillPath}/SKILL.md`, skillPath.endsWith("/SKILL.md") ? skillPath : null].filter(Boolean);
1197
- skillMdPaths = allFiles.filter((f) => candidates.includes(f));
1198
- } else skillMdPaths = allFiles.filter((f) => f.match(/^skills\/[^/]+\/SKILL\.md$/) || f === "SKILL.md");
1199
- if (skillMdPaths.length === 0) return {
1200
- skills: [],
1201
- commitSha
1202
- };
1203
- const limit = pLimit(5);
1204
- const skills = [];
1205
- onProgress?.(`Found ${skillMdPaths.length} skill(s), downloading...`);
1206
- await Promise.all(skillMdPaths.map((mdPath) => limit(async () => {
1207
- const skillDir = mdPath === "SKILL.md" ? "" : mdPath.replace(/\/SKILL\.md$/, "");
1208
- const content = await fetchRawGitHub(owner, repo, ref, mdPath);
1209
- if (!content) return;
1210
- const frontmatter = parseSkillFrontmatterName(content);
1211
- const dirName = skillDir ? skillDir.split("/").pop() : repo;
1212
- const name = frontmatter.name || dirName;
1213
- const supportingFiles = [];
1214
- const prefix = skillDir ? `${skillDir}/` : "";
1215
- for (const subdir of SUPPORTING_DIRS) {
1216
- const subdirPrefix = `${prefix}${subdir}/`;
1217
- const matching = allFiles.filter((f) => f.startsWith(subdirPrefix));
1218
- for (const filePath of matching) {
1219
- const fileContent = await fetchRawGitHub(owner, repo, ref, filePath);
1220
- if (fileContent) {
1221
- const relativePath = filePath.slice(prefix.length);
1222
- supportingFiles.push({
1223
- path: relativePath,
1224
- content: fileContent
1225
- });
1226
- }
1184
+ return { skills: [] };
1185
+ }
1186
+ async function downloadGitHubSkills(owner, repo, ref, skillPath, onProgress) {
1187
+ const tempDir = join(tmpdir(), `skilld-${Date.now()}`);
1188
+ try {
1189
+ if (skillPath) {
1190
+ onProgress?.(`Downloading ${owner}/${repo}/${skillPath}@${ref}`);
1191
+ const { dir } = await downloadTemplate(`github:${owner}/${repo}/${skillPath}#${ref}`, {
1192
+ dir: tempDir,
1193
+ force: true
1194
+ });
1195
+ const skill = readLocalSkill(dir, skillPath);
1196
+ return skill ? [skill] : [];
1197
+ }
1198
+ onProgress?.(`Downloading ${owner}/${repo}/skills@${ref}`);
1199
+ try {
1200
+ const { dir } = await downloadTemplate(`github:${owner}/${repo}/skills#${ref}`, {
1201
+ dir: tempDir,
1202
+ force: true
1203
+ });
1204
+ const skills = [];
1205
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
1206
+ if (!entry.isDirectory()) continue;
1207
+ const skill = readLocalSkill(resolve(dir, entry.name), `skills/${entry.name}`);
1208
+ if (skill) skills.push(skill);
1209
+ }
1210
+ if (skills.length > 0) {
1211
+ onProgress?.(`Found ${skills.length} skill(s)`);
1212
+ return skills;
1227
1213
  }
1214
+ } catch {}
1215
+ const content = await $fetch(`https://raw.githubusercontent.com/${owner}/${repo}/${ref}/SKILL.md`, { responseType: "text" }).catch(() => null);
1216
+ if (content) {
1217
+ const fm = parseSkillFrontmatterName(content);
1218
+ onProgress?.("Found 1 skill");
1219
+ return [{
1220
+ name: fm.name || repo,
1221
+ description: fm.description || "",
1222
+ path: "",
1223
+ content,
1224
+ files: []
1225
+ }];
1228
1226
  }
1229
- skills.push({
1230
- name,
1231
- description: frontmatter.description || "",
1232
- path: skillDir,
1233
- content,
1234
- files: supportingFiles
1227
+ return [];
1228
+ } catch {
1229
+ return [];
1230
+ } finally {
1231
+ rmSync(tempDir, {
1232
+ recursive: true,
1233
+ force: true
1235
1234
  });
1236
- })));
1237
- return {
1238
- skills,
1239
- commitSha
1240
- };
1241
- }
1242
- async function fetchRawGitHub(owner, repo, ref, path) {
1243
- return $fetch(`https://raw.githubusercontent.com/${owner}/${repo}/${ref}/${path}`, { responseType: "text" }).catch(() => null);
1235
+ }
1244
1236
  }
1245
1237
  async function fetchGitLabSkills(source, onProgress) {
1246
1238
  const { owner, repo } = source;
1247
1239
  if (!owner || !repo) return { skills: [] };
1248
1240
  const ref = source.ref || "main";
1249
- const projectId = encodeURIComponent(`${owner}/${repo}`);
1250
- onProgress?.(`Listing files at ${owner}/${repo}@${ref}`);
1251
- const tree = await $fetch(`https://gitlab.com/api/v4/projects/${projectId}/repository/tree?ref=${ref}&recursive=true&per_page=100`).catch(() => null);
1252
- if (!tree?.length) return { skills: [] };
1253
- const allFiles = tree.filter((e) => e.type === "blob").map((e) => e.path);
1254
- const skillMdPaths = source.skillPath ? allFiles.filter((f) => f === `${source.skillPath}/SKILL.md`) : allFiles.filter((f) => f.match(/^skills\/[^/]+\/SKILL\.md$/) || f === "SKILL.md");
1255
- if (skillMdPaths.length === 0) return { skills: [] };
1256
- const limit = pLimit(5);
1257
- const skills = [];
1258
- onProgress?.(`Found ${skillMdPaths.length} skill(s), downloading...`);
1259
- await Promise.all(skillMdPaths.map((mdPath) => limit(async () => {
1260
- const skillDir = mdPath === "SKILL.md" ? "" : mdPath.replace(/\/SKILL\.md$/, "");
1261
- const content = await fetchRawGitLab(owner, repo, ref, mdPath);
1262
- if (!content) return;
1263
- const frontmatter = parseSkillFrontmatterName(content);
1264
- const dirName = skillDir ? skillDir.split("/").pop() : repo;
1265
- const name = frontmatter.name || dirName;
1266
- const supportingFiles = [];
1267
- const prefix = skillDir ? `${skillDir}/` : "";
1268
- for (const subdir of SUPPORTING_DIRS) {
1269
- const subdirPrefix = `${prefix}${subdir}/`;
1270
- const matching = allFiles.filter((f) => f.startsWith(subdirPrefix));
1271
- for (const filePath of matching) {
1272
- const fileContent = await fetchRawGitLab(owner, repo, ref, filePath);
1273
- if (fileContent) {
1274
- const relativePath = filePath.slice(prefix.length);
1275
- supportingFiles.push({
1276
- path: relativePath,
1277
- content: fileContent
1278
- });
1279
- }
1280
- }
1241
+ const tempDir = join(tmpdir(), `skilld-gitlab-${Date.now()}`);
1242
+ try {
1243
+ const subdir = source.skillPath || "skills";
1244
+ onProgress?.(`Downloading ${owner}/${repo}/${subdir}@${ref}`);
1245
+ const { dir } = await downloadTemplate(`gitlab:${owner}/${repo}/${subdir}#${ref}`, {
1246
+ dir: tempDir,
1247
+ force: true
1248
+ });
1249
+ if (source.skillPath) {
1250
+ const skill = readLocalSkill(dir, source.skillPath);
1251
+ return { skills: skill ? [skill] : [] };
1281
1252
  }
1282
- skills.push({
1283
- name,
1284
- description: frontmatter.description || "",
1285
- path: skillDir,
1286
- content,
1287
- files: supportingFiles
1253
+ const skills = [];
1254
+ for (const entry of readdirSync(dir, { withFileTypes: true })) {
1255
+ if (!entry.isDirectory()) continue;
1256
+ const skill = readLocalSkill(resolve(dir, entry.name), `skills/${entry.name}`);
1257
+ if (skill) skills.push(skill);
1258
+ }
1259
+ if (skills.length > 0) {
1260
+ onProgress?.(`Found ${skills.length} skill(s)`);
1261
+ return { skills };
1262
+ }
1263
+ const content = await $fetch(`https://gitlab.com/${owner}/${repo}/-/raw/${ref}/SKILL.md`, { responseType: "text" }).catch(() => null);
1264
+ if (content) {
1265
+ const fm = parseSkillFrontmatterName(content);
1266
+ return { skills: [{
1267
+ name: fm.name || repo,
1268
+ description: fm.description || "",
1269
+ path: "",
1270
+ content,
1271
+ files: []
1272
+ }] };
1273
+ }
1274
+ return { skills: [] };
1275
+ } catch {
1276
+ return { skills: [] };
1277
+ } finally {
1278
+ rmSync(tempDir, {
1279
+ recursive: true,
1280
+ force: true
1288
1281
  });
1289
- })));
1290
- return { skills };
1291
- }
1292
- async function fetchRawGitLab(owner, repo, ref, path) {
1293
- return $fetch(`https://gitlab.com/${owner}/${repo}/-/raw/${ref}/${path}`, { responseType: "text" }).catch(() => null);
1282
+ }
1294
1283
  }
1295
1284
  async function fetchLlmsUrl(docsUrl) {
1296
1285
  const llmsUrl = `${new URL(docsUrl).origin}/llms.txt`;