agdex 0.4.0 → 0.4.2
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/dist/cli/index.js +2 -2
- package/dist/index-2hm5b6yq.js +2111 -0
- package/dist/index.js +1 -1
- package/dist/lib/agents-md.d.ts.map +1 -1
- package/dist/lib/providers/generic.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,2111 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
7
|
+
var __toESM = (mod, isNodeMode, target) => {
|
|
8
|
+
target = mod != null ? __create(__getProtoOf(mod)) : {};
|
|
9
|
+
const to = isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target;
|
|
10
|
+
for (let key of __getOwnPropNames(mod))
|
|
11
|
+
if (!__hasOwnProp.call(to, key))
|
|
12
|
+
__defProp(to, key, {
|
|
13
|
+
get: () => mod[key],
|
|
14
|
+
enumerable: true
|
|
15
|
+
});
|
|
16
|
+
return to;
|
|
17
|
+
};
|
|
18
|
+
var __commonJS = (cb, mod) => () => (mod || cb((mod = { exports: {} }).exports, mod), mod.exports);
|
|
19
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
20
|
+
|
|
21
|
+
// src/lib/agents-md.ts
|
|
22
|
+
import { execSync } from "child_process";
|
|
23
|
+
import fs from "fs";
|
|
24
|
+
import path from "path";
|
|
25
|
+
import os from "os";
|
|
26
|
+
var START_MARKER_PREFIX = "<!-- AGENTS-MD-EMBED-START";
|
|
27
|
+
var END_MARKER_PREFIX = "<!-- AGENTS-MD-EMBED-END";
|
|
28
|
+
var MARKER_SUFFIX = " -->";
|
|
29
|
+
function getStartMarker(providerName) {
|
|
30
|
+
return providerName ? `${START_MARKER_PREFIX}:${providerName}${MARKER_SUFFIX}` : `${START_MARKER_PREFIX}${MARKER_SUFFIX}`;
|
|
31
|
+
}
|
|
32
|
+
function getEndMarker(providerName) {
|
|
33
|
+
return providerName ? `${END_MARKER_PREFIX}:${providerName}${MARKER_SUFFIX}` : `${END_MARKER_PREFIX}${MARKER_SUFFIX}`;
|
|
34
|
+
}
|
|
35
|
+
async function pullDocs(provider, options) {
|
|
36
|
+
const { cwd, version: versionOverride, docsDir } = options;
|
|
37
|
+
let version;
|
|
38
|
+
if (versionOverride) {
|
|
39
|
+
version = versionOverride;
|
|
40
|
+
} else if (provider.detectVersion) {
|
|
41
|
+
const versionResult = provider.detectVersion(cwd);
|
|
42
|
+
if (!versionResult.version) {
|
|
43
|
+
return {
|
|
44
|
+
success: false,
|
|
45
|
+
error: versionResult.error || `Could not detect ${provider.displayName} version`
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
version = versionResult.version;
|
|
49
|
+
} else {
|
|
50
|
+
return {
|
|
51
|
+
success: false,
|
|
52
|
+
error: `No version provided and ${provider.displayName} does not support auto-detection`
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const docsPath = docsDir ?? fs.mkdtempSync(path.join(os.tmpdir(), "agdex-"));
|
|
56
|
+
const useTempDir = !docsDir;
|
|
57
|
+
try {
|
|
58
|
+
if (fs.existsSync(docsPath)) {
|
|
59
|
+
fs.rmSync(docsPath, { recursive: true });
|
|
60
|
+
}
|
|
61
|
+
const defaultVersionToTag = (v) => {
|
|
62
|
+
if (v.startsWith("v") || /^\d/.test(v)) {
|
|
63
|
+
return v.startsWith("v") ? v : `v${v}`;
|
|
64
|
+
}
|
|
65
|
+
return v;
|
|
66
|
+
};
|
|
67
|
+
const tag = provider.versionToTag ? provider.versionToTag(version) : defaultVersionToTag(version);
|
|
68
|
+
await cloneDocsFolder(provider.repo, provider.docsPath, tag, docsPath);
|
|
69
|
+
return {
|
|
70
|
+
success: true,
|
|
71
|
+
docsPath,
|
|
72
|
+
version
|
|
73
|
+
};
|
|
74
|
+
} catch (error) {
|
|
75
|
+
if (useTempDir && fs.existsSync(docsPath)) {
|
|
76
|
+
fs.rmSync(docsPath, { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
error: error instanceof Error ? error.message : String(error)
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
async function cloneDocsFolder(repo, docsFolder, tag, destDir) {
|
|
85
|
+
const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "agdex-clone-"));
|
|
86
|
+
try {
|
|
87
|
+
try {
|
|
88
|
+
execSync(`git clone --depth 1 --filter=blob:none --sparse --branch ${tag} https://github.com/${repo}.git .`, { cwd: tempDir, stdio: "pipe" });
|
|
89
|
+
} catch (error) {
|
|
90
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
91
|
+
if (message.includes("not found") || message.includes("did not match")) {
|
|
92
|
+
throw new Error(`Could not find documentation for tag ${tag}. This version may not exist on GitHub yet.`);
|
|
93
|
+
}
|
|
94
|
+
throw error;
|
|
95
|
+
}
|
|
96
|
+
execSync(`git sparse-checkout set ${docsFolder}`, { cwd: tempDir, stdio: "pipe" });
|
|
97
|
+
const sourceDocsDir = path.join(tempDir, docsFolder);
|
|
98
|
+
if (!fs.existsSync(sourceDocsDir)) {
|
|
99
|
+
throw new Error(`${docsFolder} folder not found in cloned repository`);
|
|
100
|
+
}
|
|
101
|
+
if (fs.existsSync(destDir)) {
|
|
102
|
+
fs.rmSync(destDir, { recursive: true });
|
|
103
|
+
}
|
|
104
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
105
|
+
fs.cpSync(sourceDocsDir, destDir, { recursive: true });
|
|
106
|
+
} finally {
|
|
107
|
+
if (fs.existsSync(tempDir)) {
|
|
108
|
+
fs.rmSync(tempDir, { recursive: true });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
function collectDocFiles(dir, options) {
|
|
113
|
+
const extensions = options?.extensions || [".mdx", ".md"];
|
|
114
|
+
const excludePatterns = options?.excludePatterns || [];
|
|
115
|
+
const files = fs.readdirSync(dir, { recursive: true });
|
|
116
|
+
return files.filter((f) => {
|
|
117
|
+
const hasValidExtension = extensions.some((ext) => f.endsWith(ext));
|
|
118
|
+
if (!hasValidExtension)
|
|
119
|
+
return false;
|
|
120
|
+
for (const pattern of excludePatterns) {
|
|
121
|
+
if (pattern.startsWith("**/") && pattern.endsWith("/**")) {
|
|
122
|
+
const dirName = pattern.slice(3, -3);
|
|
123
|
+
if (f.includes(`/${dirName}/`) || f.startsWith(`${dirName}/`))
|
|
124
|
+
return false;
|
|
125
|
+
} else if (pattern.startsWith("**/")) {
|
|
126
|
+
const suffix = pattern.slice(3);
|
|
127
|
+
if (f.endsWith(suffix) || f === suffix)
|
|
128
|
+
return false;
|
|
129
|
+
} else if (pattern.startsWith("*")) {
|
|
130
|
+
const suffix = pattern.slice(1);
|
|
131
|
+
if (f.endsWith(suffix))
|
|
132
|
+
return false;
|
|
133
|
+
} else if (f === pattern || f.endsWith("/" + pattern)) {
|
|
134
|
+
return false;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
if (f.endsWith("/index.mdx") || f.endsWith("/index.md") || f.startsWith("index.")) {
|
|
138
|
+
return false;
|
|
139
|
+
}
|
|
140
|
+
return true;
|
|
141
|
+
}).sort().map((f) => ({ relativePath: f }));
|
|
142
|
+
}
|
|
143
|
+
function buildDocTree(files) {
|
|
144
|
+
const sections = new Map;
|
|
145
|
+
for (const file of files) {
|
|
146
|
+
const parts = file.relativePath.split("/");
|
|
147
|
+
if (parts.length === 1) {
|
|
148
|
+
if (!sections.has(".")) {
|
|
149
|
+
sections.set(".", {
|
|
150
|
+
name: ".",
|
|
151
|
+
files: [],
|
|
152
|
+
subsections: []
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
sections.get(".").files.push({ relativePath: file.relativePath });
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
const topLevelDir = parts[0];
|
|
159
|
+
if (!sections.has(topLevelDir)) {
|
|
160
|
+
sections.set(topLevelDir, {
|
|
161
|
+
name: topLevelDir,
|
|
162
|
+
files: [],
|
|
163
|
+
subsections: []
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
const section = sections.get(topLevelDir);
|
|
167
|
+
if (parts.length === 2) {
|
|
168
|
+
section.files.push({ relativePath: file.relativePath });
|
|
169
|
+
} else {
|
|
170
|
+
const subsectionDir = parts[1];
|
|
171
|
+
let subsection = section.subsections.find((s) => s.name === subsectionDir);
|
|
172
|
+
if (!subsection) {
|
|
173
|
+
subsection = { name: subsectionDir, files: [], subsections: [] };
|
|
174
|
+
section.subsections.push(subsection);
|
|
175
|
+
}
|
|
176
|
+
if (parts.length === 3) {
|
|
177
|
+
subsection.files.push({ relativePath: file.relativePath });
|
|
178
|
+
} else {
|
|
179
|
+
const subSubDir = parts[2];
|
|
180
|
+
let subSubsection = subsection.subsections.find((s) => s.name === subSubDir);
|
|
181
|
+
if (!subSubsection) {
|
|
182
|
+
subSubsection = { name: subSubDir, files: [], subsections: [] };
|
|
183
|
+
subsection.subsections.push(subSubsection);
|
|
184
|
+
}
|
|
185
|
+
subSubsection.files.push({ relativePath: file.relativePath });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const sortedSections = Array.from(sections.values()).sort((a, b) => a.name.localeCompare(b.name));
|
|
190
|
+
for (const section of sortedSections) {
|
|
191
|
+
section.files.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
192
|
+
section.subsections.sort((a, b) => a.name.localeCompare(b.name));
|
|
193
|
+
for (const subsection of section.subsections) {
|
|
194
|
+
subsection.files.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
195
|
+
subsection.subsections.sort((a, b) => a.name.localeCompare(b.name));
|
|
196
|
+
for (const subSubsection of subsection.subsections) {
|
|
197
|
+
subSubsection.files.sort((a, b) => a.relativePath.localeCompare(b.relativePath));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
return sortedSections;
|
|
202
|
+
}
|
|
203
|
+
function generateIndex(options) {
|
|
204
|
+
const { docsPath, sections, outputFile, providerName, instruction, description, regenerateCommand } = options;
|
|
205
|
+
const parts = [];
|
|
206
|
+
const header = providerName ? `[${providerName} Docs Index]` : "[Docs Index]";
|
|
207
|
+
parts.push(header);
|
|
208
|
+
parts.push(`root: ${docsPath}`);
|
|
209
|
+
if (instruction) {
|
|
210
|
+
parts.push(instruction);
|
|
211
|
+
}
|
|
212
|
+
if (description) {
|
|
213
|
+
parts.push(description);
|
|
214
|
+
}
|
|
215
|
+
const targetFile = outputFile || "AGENTS.md";
|
|
216
|
+
const cmd = regenerateCommand || `npx agdex --output ${targetFile}`;
|
|
217
|
+
parts.push(`If docs missing, run: ${cmd}`);
|
|
218
|
+
const allFiles = collectAllFilesFromSections(sections);
|
|
219
|
+
const grouped = groupByDirectory(allFiles);
|
|
220
|
+
for (const [dir, files] of grouped) {
|
|
221
|
+
parts.push(`${dir}:{${files.join(",")}}`);
|
|
222
|
+
}
|
|
223
|
+
return parts.join("|");
|
|
224
|
+
}
|
|
225
|
+
function collectAllFilesFromSections(sections) {
|
|
226
|
+
const files = [];
|
|
227
|
+
for (const section of sections) {
|
|
228
|
+
for (const file of section.files) {
|
|
229
|
+
files.push(file.relativePath);
|
|
230
|
+
}
|
|
231
|
+
files.push(...collectAllFilesFromSections(section.subsections));
|
|
232
|
+
}
|
|
233
|
+
return files;
|
|
234
|
+
}
|
|
235
|
+
function groupByDirectory(files) {
|
|
236
|
+
const grouped = new Map;
|
|
237
|
+
for (const filePath of files) {
|
|
238
|
+
const lastSlash = filePath.lastIndexOf("/");
|
|
239
|
+
const dir = lastSlash === -1 ? "." : filePath.slice(0, lastSlash);
|
|
240
|
+
const fileName = lastSlash === -1 ? filePath : filePath.slice(lastSlash + 1);
|
|
241
|
+
const existing = grouped.get(dir);
|
|
242
|
+
if (existing) {
|
|
243
|
+
existing.push(fileName);
|
|
244
|
+
} else {
|
|
245
|
+
grouped.set(dir, [fileName]);
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
return grouped;
|
|
249
|
+
}
|
|
250
|
+
function hasExistingIndex(content, providerName) {
|
|
251
|
+
if (providerName) {
|
|
252
|
+
return content.includes(getStartMarker(providerName));
|
|
253
|
+
}
|
|
254
|
+
return content.includes(START_MARKER_PREFIX);
|
|
255
|
+
}
|
|
256
|
+
function removeDocsIndex(content, providerName) {
|
|
257
|
+
if (!hasExistingIndex(content, providerName)) {
|
|
258
|
+
return content;
|
|
259
|
+
}
|
|
260
|
+
let result = content;
|
|
261
|
+
if (providerName) {
|
|
262
|
+
const startMarker = getStartMarker(providerName);
|
|
263
|
+
const endMarker = getEndMarker(providerName);
|
|
264
|
+
const startIdx = result.indexOf(startMarker);
|
|
265
|
+
const endIdx = result.indexOf(endMarker) + endMarker.length;
|
|
266
|
+
if (startIdx !== -1 && endIdx > startIdx) {
|
|
267
|
+
result = result.slice(0, startIdx) + result.slice(endIdx);
|
|
268
|
+
}
|
|
269
|
+
} else {
|
|
270
|
+
let startIdx;
|
|
271
|
+
while ((startIdx = result.indexOf(START_MARKER_PREFIX)) !== -1) {
|
|
272
|
+
const startMarkerEnd = result.indexOf(MARKER_SUFFIX, startIdx) + MARKER_SUFFIX.length;
|
|
273
|
+
const startMarkerContent = result.slice(startIdx, startMarkerEnd);
|
|
274
|
+
const providerMatch = startMarkerContent.match(/:([^-\s]+)/);
|
|
275
|
+
const provider = providerMatch ? providerMatch[1] : undefined;
|
|
276
|
+
const endMarker = getEndMarker(provider);
|
|
277
|
+
const endIdx = result.indexOf(endMarker);
|
|
278
|
+
if (endIdx !== -1) {
|
|
279
|
+
result = result.slice(0, startIdx) + result.slice(endIdx + endMarker.length);
|
|
280
|
+
} else {
|
|
281
|
+
result = result.slice(0, startIdx) + result.slice(startMarkerEnd);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
result = result.replace(/\n{3,}/g, `
|
|
286
|
+
|
|
287
|
+
`);
|
|
288
|
+
result = result.trimEnd();
|
|
289
|
+
if (result.length > 0) {
|
|
290
|
+
result += `
|
|
291
|
+
`;
|
|
292
|
+
}
|
|
293
|
+
return result;
|
|
294
|
+
}
|
|
295
|
+
function wrapWithMarkers(content, providerName) {
|
|
296
|
+
const startMarker = getStartMarker(providerName);
|
|
297
|
+
const endMarker = getEndMarker(providerName);
|
|
298
|
+
return `${startMarker}
|
|
299
|
+
${content}
|
|
300
|
+
${endMarker}`;
|
|
301
|
+
}
|
|
302
|
+
function injectIndex(existingContent, indexContent, providerName) {
|
|
303
|
+
const wrappedContent = wrapWithMarkers(indexContent, providerName);
|
|
304
|
+
if (hasExistingIndex(existingContent, providerName)) {
|
|
305
|
+
const startMarker = getStartMarker(providerName);
|
|
306
|
+
const endMarker = getEndMarker(providerName);
|
|
307
|
+
const startIdx = existingContent.indexOf(startMarker);
|
|
308
|
+
const endIdx = existingContent.indexOf(endMarker) + endMarker.length;
|
|
309
|
+
return existingContent.slice(0, startIdx) + wrappedContent + existingContent.slice(endIdx);
|
|
310
|
+
}
|
|
311
|
+
const separator = existingContent.endsWith(`
|
|
312
|
+
`) ? `
|
|
313
|
+
` : `
|
|
314
|
+
|
|
315
|
+
`;
|
|
316
|
+
return existingContent + separator + wrappedContent + `
|
|
317
|
+
`;
|
|
318
|
+
}
|
|
319
|
+
function ensureGitignoreEntry(cwd, docsDir) {
|
|
320
|
+
const gitignorePath = path.join(cwd, ".gitignore");
|
|
321
|
+
const entry = docsDir.endsWith("/") ? docsDir : `${docsDir}/`;
|
|
322
|
+
const entryRegex = new RegExp(`^\\s*${docsDir.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}(?:/.*)?$`);
|
|
323
|
+
let content = "";
|
|
324
|
+
if (fs.existsSync(gitignorePath)) {
|
|
325
|
+
content = fs.readFileSync(gitignorePath, "utf-8");
|
|
326
|
+
}
|
|
327
|
+
const hasEntry = content.split(/\r?\n/).some((line) => entryRegex.test(line));
|
|
328
|
+
if (hasEntry) {
|
|
329
|
+
return { path: gitignorePath, updated: false, alreadyPresent: true };
|
|
330
|
+
}
|
|
331
|
+
const needsNewline = content.length > 0 && !content.endsWith(`
|
|
332
|
+
`);
|
|
333
|
+
const header = content.includes("# agdex") ? "" : `# agdex
|
|
334
|
+
`;
|
|
335
|
+
const newContent = content + (needsNewline ? `
|
|
336
|
+
` : "") + header + `${entry}
|
|
337
|
+
`;
|
|
338
|
+
fs.writeFileSync(gitignorePath, newContent, "utf-8");
|
|
339
|
+
return { path: gitignorePath, updated: true, alreadyPresent: false };
|
|
340
|
+
}
|
|
341
|
+
function getGlobalCacheDir() {
|
|
342
|
+
return path.join(os.homedir(), ".cache", "agdex");
|
|
343
|
+
}
|
|
344
|
+
function getLocalCacheDir(cwd) {
|
|
345
|
+
return path.join(cwd, ".agdex");
|
|
346
|
+
}
|
|
347
|
+
async function embed(options) {
|
|
348
|
+
const {
|
|
349
|
+
cwd,
|
|
350
|
+
provider,
|
|
351
|
+
version,
|
|
352
|
+
output = "AGENTS.md",
|
|
353
|
+
docsDir: customDocsDir,
|
|
354
|
+
globalCache = false,
|
|
355
|
+
description
|
|
356
|
+
} = options;
|
|
357
|
+
let docsPath;
|
|
358
|
+
let docsLinkPath;
|
|
359
|
+
let docsDir;
|
|
360
|
+
if (customDocsDir) {
|
|
361
|
+
docsDir = customDocsDir;
|
|
362
|
+
docsPath = path.isAbsolute(customDocsDir) ? customDocsDir : path.join(cwd, customDocsDir);
|
|
363
|
+
docsLinkPath = path.isAbsolute(customDocsDir) ? customDocsDir : `./${customDocsDir}`;
|
|
364
|
+
} else if (globalCache) {
|
|
365
|
+
const cacheBase = getGlobalCacheDir();
|
|
366
|
+
docsDir = path.join(cacheBase, provider.name);
|
|
367
|
+
docsPath = docsDir;
|
|
368
|
+
docsLinkPath = docsPath;
|
|
369
|
+
} else {
|
|
370
|
+
docsDir = `.agdex/${provider.name}`;
|
|
371
|
+
docsPath = path.join(cwd, docsDir);
|
|
372
|
+
docsLinkPath = `./${docsDir}`;
|
|
373
|
+
}
|
|
374
|
+
const targetPath = path.join(cwd, output);
|
|
375
|
+
let sizeBefore = 0;
|
|
376
|
+
let isNewFile = true;
|
|
377
|
+
let existingContent = "";
|
|
378
|
+
if (fs.existsSync(targetPath)) {
|
|
379
|
+
existingContent = fs.readFileSync(targetPath, "utf-8");
|
|
380
|
+
sizeBefore = Buffer.byteLength(existingContent, "utf-8");
|
|
381
|
+
isNewFile = false;
|
|
382
|
+
}
|
|
383
|
+
const pullResult = await pullDocs(provider, {
|
|
384
|
+
cwd,
|
|
385
|
+
version,
|
|
386
|
+
docsDir: docsPath
|
|
387
|
+
});
|
|
388
|
+
if (!pullResult.success) {
|
|
389
|
+
return {
|
|
390
|
+
success: false,
|
|
391
|
+
error: pullResult.error
|
|
392
|
+
};
|
|
393
|
+
}
|
|
394
|
+
const docFiles = collectDocFiles(docsPath, {
|
|
395
|
+
extensions: provider.extensions,
|
|
396
|
+
excludePatterns: provider.excludePatterns
|
|
397
|
+
});
|
|
398
|
+
const sections = buildDocTree(docFiles);
|
|
399
|
+
const globalFlag = globalCache ? " --global" : "";
|
|
400
|
+
const regenerateCommand = `npx agdex --provider ${provider.name} --output ${output}${globalFlag}`;
|
|
401
|
+
const indexContent = generateIndex({
|
|
402
|
+
docsPath: docsLinkPath,
|
|
403
|
+
sections,
|
|
404
|
+
outputFile: output,
|
|
405
|
+
providerName: provider.displayName,
|
|
406
|
+
instruction: provider.instruction,
|
|
407
|
+
description,
|
|
408
|
+
regenerateCommand
|
|
409
|
+
});
|
|
410
|
+
const newContent = injectIndex(existingContent, indexContent, provider.name);
|
|
411
|
+
fs.writeFileSync(targetPath, newContent, "utf-8");
|
|
412
|
+
const sizeAfter = Buffer.byteLength(newContent, "utf-8");
|
|
413
|
+
let gitignoreUpdated = false;
|
|
414
|
+
if (!globalCache && !customDocsDir) {
|
|
415
|
+
const gitignoreResult = ensureGitignoreEntry(cwd, ".agdex");
|
|
416
|
+
gitignoreUpdated = gitignoreResult.updated;
|
|
417
|
+
} else if (!globalCache && customDocsDir && !path.isAbsolute(customDocsDir)) {
|
|
418
|
+
const gitignoreResult = ensureGitignoreEntry(cwd, customDocsDir);
|
|
419
|
+
gitignoreUpdated = gitignoreResult.updated;
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
success: true,
|
|
423
|
+
targetFile: output,
|
|
424
|
+
docsPath: globalCache ? docsPath : docsDir,
|
|
425
|
+
version: pullResult.version,
|
|
426
|
+
sizeBefore,
|
|
427
|
+
sizeAfter,
|
|
428
|
+
isNewFile,
|
|
429
|
+
gitignoreUpdated
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// src/lib/providers/nextjs.ts
|
|
434
|
+
import fs2 from "fs";
|
|
435
|
+
import path2 from "path";
|
|
436
|
+
function detectWorkspace(cwd) {
|
|
437
|
+
const packageJsonPath = path2.join(cwd, "package.json");
|
|
438
|
+
const pnpmWorkspacePath = path2.join(cwd, "pnpm-workspace.yaml");
|
|
439
|
+
if (fs2.existsSync(pnpmWorkspacePath)) {
|
|
440
|
+
const packages = parsePnpmWorkspace(pnpmWorkspacePath);
|
|
441
|
+
if (packages.length > 0) {
|
|
442
|
+
return { isMonorepo: true, type: "pnpm", packages };
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
if (fs2.existsSync(packageJsonPath)) {
|
|
446
|
+
const packages = parsePackageJsonWorkspaces(packageJsonPath);
|
|
447
|
+
if (packages.length > 0) {
|
|
448
|
+
return { isMonorepo: true, type: "npm", packages };
|
|
449
|
+
}
|
|
450
|
+
}
|
|
451
|
+
return { isMonorepo: false, type: null, packages: [] };
|
|
452
|
+
}
|
|
453
|
+
function parsePnpmWorkspace(filePath) {
|
|
454
|
+
try {
|
|
455
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
456
|
+
const lines = content.split(`
|
|
457
|
+
`);
|
|
458
|
+
const packages = [];
|
|
459
|
+
let inPackages = false;
|
|
460
|
+
for (const line of lines) {
|
|
461
|
+
const trimmed = line.trim();
|
|
462
|
+
if (trimmed === "packages:") {
|
|
463
|
+
inPackages = true;
|
|
464
|
+
continue;
|
|
465
|
+
}
|
|
466
|
+
if (inPackages) {
|
|
467
|
+
if (trimmed && !trimmed.startsWith("-") && !trimmed.startsWith("#")) {
|
|
468
|
+
break;
|
|
469
|
+
}
|
|
470
|
+
const match = trimmed.match(/^-\s*['"]?([^'"]+)['"]?$/);
|
|
471
|
+
if (match) {
|
|
472
|
+
packages.push(match[1]);
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
return packages;
|
|
477
|
+
} catch {
|
|
478
|
+
return [];
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
function parsePackageJsonWorkspaces(filePath) {
|
|
482
|
+
try {
|
|
483
|
+
const content = fs2.readFileSync(filePath, "utf-8");
|
|
484
|
+
const pkg = JSON.parse(content);
|
|
485
|
+
if (Array.isArray(pkg.workspaces)) {
|
|
486
|
+
return pkg.workspaces;
|
|
487
|
+
}
|
|
488
|
+
if (pkg.workspaces?.packages && Array.isArray(pkg.workspaces.packages)) {
|
|
489
|
+
return pkg.workspaces.packages;
|
|
490
|
+
}
|
|
491
|
+
return [];
|
|
492
|
+
} catch {
|
|
493
|
+
return [];
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
function expandWorkspacePatterns(cwd, patterns) {
|
|
497
|
+
const packagePaths = [];
|
|
498
|
+
for (const pattern of patterns) {
|
|
499
|
+
if (pattern.startsWith("!"))
|
|
500
|
+
continue;
|
|
501
|
+
if (pattern.includes("*")) {
|
|
502
|
+
const basePath = path2.join(cwd, pattern.replace("/*", "").replace("/**", ""));
|
|
503
|
+
if (fs2.existsSync(basePath)) {
|
|
504
|
+
try {
|
|
505
|
+
const entries = fs2.readdirSync(basePath);
|
|
506
|
+
for (const entry of entries) {
|
|
507
|
+
const fullPath = path2.join(basePath, entry);
|
|
508
|
+
if (fs2.statSync(fullPath).isDirectory()) {
|
|
509
|
+
packagePaths.push(fullPath);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
} catch {}
|
|
513
|
+
}
|
|
514
|
+
} else {
|
|
515
|
+
const fullPath = path2.join(cwd, pattern);
|
|
516
|
+
if (fs2.existsSync(fullPath)) {
|
|
517
|
+
packagePaths.push(fullPath);
|
|
518
|
+
}
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
return [...new Set(packagePaths)];
|
|
522
|
+
}
|
|
523
|
+
function compareVersions(a, b) {
|
|
524
|
+
const parseVersion = (v) => {
|
|
525
|
+
const match = v.match(/^(\d+)\.(\d+)\.(\d+)/);
|
|
526
|
+
if (!match)
|
|
527
|
+
return [0, 0, 0];
|
|
528
|
+
return [parseInt(match[1]), parseInt(match[2]), parseInt(match[3])];
|
|
529
|
+
};
|
|
530
|
+
const [aMajor, aMinor, aPatch] = parseVersion(a);
|
|
531
|
+
const [bMajor, bMinor, bPatch] = parseVersion(b);
|
|
532
|
+
if (aMajor !== bMajor)
|
|
533
|
+
return aMajor - bMajor;
|
|
534
|
+
if (aMinor !== bMinor)
|
|
535
|
+
return aMinor - bMinor;
|
|
536
|
+
return aPatch - bPatch;
|
|
537
|
+
}
|
|
538
|
+
function findNextjsInWorkspace(cwd, patterns) {
|
|
539
|
+
const packagePaths = expandWorkspacePatterns(cwd, patterns);
|
|
540
|
+
const versions = [];
|
|
541
|
+
for (const pkgPath of packagePaths) {
|
|
542
|
+
const packageJsonPath = path2.join(pkgPath, "package.json");
|
|
543
|
+
if (!fs2.existsSync(packageJsonPath))
|
|
544
|
+
continue;
|
|
545
|
+
try {
|
|
546
|
+
const content = fs2.readFileSync(packageJsonPath, "utf-8");
|
|
547
|
+
const pkg = JSON.parse(content);
|
|
548
|
+
const nextVersion = pkg.dependencies?.next || pkg.devDependencies?.next;
|
|
549
|
+
if (nextVersion) {
|
|
550
|
+
versions.push(nextVersion.replace(/^[\^~>=<]+/, ""));
|
|
551
|
+
}
|
|
552
|
+
} catch {}
|
|
553
|
+
}
|
|
554
|
+
if (versions.length === 0)
|
|
555
|
+
return null;
|
|
556
|
+
if (versions.length === 1)
|
|
557
|
+
return versions[0];
|
|
558
|
+
return versions.reduce((highest, current) => {
|
|
559
|
+
return compareVersions(current, highest) > 0 ? current : highest;
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
function detectVersion(cwd) {
|
|
563
|
+
const packageJsonPath = path2.join(cwd, "package.json");
|
|
564
|
+
if (!fs2.existsSync(packageJsonPath)) {
|
|
565
|
+
return {
|
|
566
|
+
version: null,
|
|
567
|
+
error: "No package.json found in the current directory"
|
|
568
|
+
};
|
|
569
|
+
}
|
|
570
|
+
try {
|
|
571
|
+
const packageJson = JSON.parse(fs2.readFileSync(packageJsonPath, "utf-8"));
|
|
572
|
+
const dependencies = packageJson.dependencies || {};
|
|
573
|
+
const devDependencies = packageJson.devDependencies || {};
|
|
574
|
+
const nextVersion = dependencies.next || devDependencies.next;
|
|
575
|
+
if (nextVersion) {
|
|
576
|
+
const cleanVersion = nextVersion.replace(/^[\^~>=<]+/, "");
|
|
577
|
+
return { version: cleanVersion };
|
|
578
|
+
}
|
|
579
|
+
const workspace = detectWorkspace(cwd);
|
|
580
|
+
if (workspace.isMonorepo && workspace.packages.length > 0) {
|
|
581
|
+
const highestVersion = findNextjsInWorkspace(cwd, workspace.packages);
|
|
582
|
+
if (highestVersion) {
|
|
583
|
+
return { version: highestVersion };
|
|
584
|
+
}
|
|
585
|
+
return {
|
|
586
|
+
version: null,
|
|
587
|
+
error: `No Next.js found in ${workspace.type} workspace packages.`
|
|
588
|
+
};
|
|
589
|
+
}
|
|
590
|
+
return {
|
|
591
|
+
version: null,
|
|
592
|
+
error: "Next.js is not installed in this project."
|
|
593
|
+
};
|
|
594
|
+
} catch (err) {
|
|
595
|
+
return {
|
|
596
|
+
version: null,
|
|
597
|
+
error: `Failed to parse package.json: ${err instanceof Error ? err.message : String(err)}`
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
var nextjsProvider = {
|
|
602
|
+
name: "nextjs",
|
|
603
|
+
displayName: "Next.js",
|
|
604
|
+
repo: "vercel/next.js",
|
|
605
|
+
docsPath: "docs",
|
|
606
|
+
extensions: [".mdx", ".md"],
|
|
607
|
+
detectVersion,
|
|
608
|
+
versionToTag: (version) => version.startsWith("v") ? version : `v${version}`,
|
|
609
|
+
excludePatterns: ["**/index.mdx", "**/index.md"],
|
|
610
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any Next.js tasks."
|
|
611
|
+
};
|
|
612
|
+
|
|
613
|
+
// src/lib/providers/react.ts
|
|
614
|
+
import fs3 from "fs";
|
|
615
|
+
import path3 from "path";
|
|
616
|
+
function detectVersion2(cwd) {
|
|
617
|
+
const packageJsonPath = path3.join(cwd, "package.json");
|
|
618
|
+
if (!fs3.existsSync(packageJsonPath)) {
|
|
619
|
+
return {
|
|
620
|
+
version: null,
|
|
621
|
+
error: "No package.json found in the current directory"
|
|
622
|
+
};
|
|
623
|
+
}
|
|
624
|
+
try {
|
|
625
|
+
const packageJson = JSON.parse(fs3.readFileSync(packageJsonPath, "utf-8"));
|
|
626
|
+
const dependencies = packageJson.dependencies || {};
|
|
627
|
+
const devDependencies = packageJson.devDependencies || {};
|
|
628
|
+
const reactVersion = dependencies.react || devDependencies.react;
|
|
629
|
+
if (reactVersion) {
|
|
630
|
+
const cleanVersion = reactVersion.replace(/^[\^~>=<]+/, "");
|
|
631
|
+
return { version: cleanVersion };
|
|
632
|
+
}
|
|
633
|
+
return {
|
|
634
|
+
version: null,
|
|
635
|
+
error: "React is not installed in this project."
|
|
636
|
+
};
|
|
637
|
+
} catch (err) {
|
|
638
|
+
return {
|
|
639
|
+
version: null,
|
|
640
|
+
error: `Failed to parse package.json: ${err instanceof Error ? err.message : String(err)}`
|
|
641
|
+
};
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
var reactProvider = {
|
|
645
|
+
name: "react",
|
|
646
|
+
displayName: "React",
|
|
647
|
+
repo: "reactjs/react.dev",
|
|
648
|
+
docsPath: "src/content",
|
|
649
|
+
extensions: [".md", ".mdx"],
|
|
650
|
+
detectVersion: detectVersion2,
|
|
651
|
+
versionToTag: (version) => `v${version}`,
|
|
652
|
+
excludePatterns: ["**/index.md"],
|
|
653
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any React tasks."
|
|
654
|
+
};
|
|
655
|
+
|
|
656
|
+
// src/lib/providers/pixi.ts
|
|
657
|
+
import fs4 from "fs";
|
|
658
|
+
import path4 from "path";
|
|
659
|
+
function parsePixiVersion(content) {
|
|
660
|
+
const requiresPixiMatch = content.match(/requires-pixi\s*=\s*["']([^"']+)["']/);
|
|
661
|
+
if (requiresPixiMatch) {
|
|
662
|
+
const versionMatch = requiresPixiMatch[1].match(/[\d]+\.[\d]+\.[\d]+/);
|
|
663
|
+
if (versionMatch) {
|
|
664
|
+
return versionMatch[0];
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
return null;
|
|
668
|
+
}
|
|
669
|
+
function detectVersion3(cwd) {
|
|
670
|
+
const pixiTomlPath = path4.join(cwd, "pixi.toml");
|
|
671
|
+
if (fs4.existsSync(pixiTomlPath)) {
|
|
672
|
+
try {
|
|
673
|
+
const content = fs4.readFileSync(pixiTomlPath, "utf-8");
|
|
674
|
+
const version = parsePixiVersion(content);
|
|
675
|
+
if (version) {
|
|
676
|
+
return { version };
|
|
677
|
+
}
|
|
678
|
+
} catch {}
|
|
679
|
+
}
|
|
680
|
+
const pyprojectPath = path4.join(cwd, "pyproject.toml");
|
|
681
|
+
if (fs4.existsSync(pyprojectPath)) {
|
|
682
|
+
try {
|
|
683
|
+
const content = fs4.readFileSync(pyprojectPath, "utf-8");
|
|
684
|
+
if (content.includes("[tool.pixi")) {
|
|
685
|
+
const version = parsePixiVersion(content);
|
|
686
|
+
if (version) {
|
|
687
|
+
return { version };
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
} catch {}
|
|
691
|
+
}
|
|
692
|
+
try {
|
|
693
|
+
const { execSync: execSync2 } = __require("child_process");
|
|
694
|
+
const output = execSync2("pixi --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
695
|
+
const versionMatch = output.match(/pixi ([\d]+\.[\d]+\.[\d]+)/);
|
|
696
|
+
if (versionMatch) {
|
|
697
|
+
return { version: versionMatch[1] };
|
|
698
|
+
}
|
|
699
|
+
} catch {}
|
|
700
|
+
return {
|
|
701
|
+
version: null,
|
|
702
|
+
error: "Could not detect pixi version. Use --fw-version to specify."
|
|
703
|
+
};
|
|
704
|
+
}
|
|
705
|
+
var pixiProvider = {
|
|
706
|
+
name: "pixi",
|
|
707
|
+
displayName: "Pixi",
|
|
708
|
+
repo: "prefix-dev/pixi",
|
|
709
|
+
docsPath: "docs",
|
|
710
|
+
extensions: [".md"],
|
|
711
|
+
detectVersion: detectVersion3,
|
|
712
|
+
versionToTag: (version) => version.startsWith("v") ? version : `v${version}`,
|
|
713
|
+
excludePatterns: [
|
|
714
|
+
"**/index.md",
|
|
715
|
+
"**/__README.md",
|
|
716
|
+
"**/partials/**",
|
|
717
|
+
"**/assets/**",
|
|
718
|
+
"**/stylesheets/**",
|
|
719
|
+
"**/javascripts/**",
|
|
720
|
+
"**/overrides/**",
|
|
721
|
+
"**/layouts/**"
|
|
722
|
+
],
|
|
723
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any pixi tasks. Pixi is a cross-platform package manager for conda environments."
|
|
724
|
+
};
|
|
725
|
+
|
|
726
|
+
// src/lib/providers/rattler-build.ts
|
|
727
|
+
import fs5 from "fs";
|
|
728
|
+
import path5 from "path";
|
|
729
|
+
function detectVersion4(cwd) {
|
|
730
|
+
const pixiTomlPath = path5.join(cwd, "pixi.toml");
|
|
731
|
+
if (fs5.existsSync(pixiTomlPath)) {
|
|
732
|
+
try {
|
|
733
|
+
const content = fs5.readFileSync(pixiTomlPath, "utf-8");
|
|
734
|
+
const match = content.match(/rattler-build\s*=\s*["']([^"']+)["']/);
|
|
735
|
+
if (match) {
|
|
736
|
+
const versionMatch = match[1].match(/[\d]+\.[\d]+\.[\d]+/);
|
|
737
|
+
if (versionMatch) {
|
|
738
|
+
return { version: versionMatch[0] };
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
} catch {}
|
|
742
|
+
}
|
|
743
|
+
const pyprojectPath = path5.join(cwd, "pyproject.toml");
|
|
744
|
+
if (fs5.existsSync(pyprojectPath)) {
|
|
745
|
+
try {
|
|
746
|
+
const content = fs5.readFileSync(pyprojectPath, "utf-8");
|
|
747
|
+
const match = content.match(/rattler-build\s*=\s*["']([^"']+)["']/);
|
|
748
|
+
if (match) {
|
|
749
|
+
const versionMatch = match[1].match(/[\d]+\.[\d]+\.[\d]+/);
|
|
750
|
+
if (versionMatch) {
|
|
751
|
+
return { version: versionMatch[0] };
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
} catch {}
|
|
755
|
+
}
|
|
756
|
+
try {
|
|
757
|
+
const { execSync: execSync2 } = __require("child_process");
|
|
758
|
+
const output = execSync2("rattler-build --version", {
|
|
759
|
+
encoding: "utf-8",
|
|
760
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
761
|
+
});
|
|
762
|
+
const versionMatch = output.match(/rattler-build ([\d]+\.[\d]+\.[\d]+)/);
|
|
763
|
+
if (versionMatch) {
|
|
764
|
+
return { version: versionMatch[1] };
|
|
765
|
+
}
|
|
766
|
+
} catch {}
|
|
767
|
+
return {
|
|
768
|
+
version: null,
|
|
769
|
+
error: "Could not detect rattler-build version. Use --fw-version to specify."
|
|
770
|
+
};
|
|
771
|
+
}
|
|
772
|
+
var rattlerBuildProvider = {
|
|
773
|
+
name: "rattler-build",
|
|
774
|
+
displayName: "rattler-build",
|
|
775
|
+
repo: "prefix-dev/rattler-build",
|
|
776
|
+
docsPath: "docs",
|
|
777
|
+
extensions: [".md"],
|
|
778
|
+
detectVersion: detectVersion4,
|
|
779
|
+
versionToTag: (version) => version.startsWith("v") ? version : `v${version}`,
|
|
780
|
+
excludePatterns: [
|
|
781
|
+
"**/index.md",
|
|
782
|
+
"**/assets/**",
|
|
783
|
+
"**/stylesheets/**",
|
|
784
|
+
"**/layouts/**",
|
|
785
|
+
"**/overrides/**",
|
|
786
|
+
"**/generator/**"
|
|
787
|
+
],
|
|
788
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any rattler-build tasks. rattler-build is a tool for building conda packages from recipe.yaml files."
|
|
789
|
+
};
|
|
790
|
+
|
|
791
|
+
// src/lib/providers/tauri.ts
|
|
792
|
+
import fs6 from "fs";
|
|
793
|
+
import path6 from "path";
|
|
794
|
+
function detectVersion5(cwd) {
|
|
795
|
+
const packageJsonPath = path6.join(cwd, "package.json");
|
|
796
|
+
if (fs6.existsSync(packageJsonPath)) {
|
|
797
|
+
try {
|
|
798
|
+
const content = fs6.readFileSync(packageJsonPath, "utf-8");
|
|
799
|
+
const pkg = JSON.parse(content);
|
|
800
|
+
const deps = { ...pkg.dependencies, ...pkg.devDependencies };
|
|
801
|
+
const tauriApi = deps["@tauri-apps/api"];
|
|
802
|
+
const tauriCli = deps["@tauri-apps/cli"];
|
|
803
|
+
if (tauriApi || tauriCli) {
|
|
804
|
+
const version = tauriApi || tauriCli;
|
|
805
|
+
const versionMatch = version.match(/(\d+)\./);
|
|
806
|
+
if (versionMatch) {
|
|
807
|
+
const major = parseInt(versionMatch[1]);
|
|
808
|
+
return { version: major >= 2 ? "v2" : "v1" };
|
|
809
|
+
}
|
|
810
|
+
return { version: "v2" };
|
|
811
|
+
}
|
|
812
|
+
} catch {}
|
|
813
|
+
}
|
|
814
|
+
const tauriConfPath = path6.join(cwd, "src-tauri", "tauri.conf.json");
|
|
815
|
+
if (fs6.existsSync(tauriConfPath)) {
|
|
816
|
+
return { version: "v2" };
|
|
817
|
+
}
|
|
818
|
+
const cargoTomlPath = path6.join(cwd, "src-tauri", "Cargo.toml");
|
|
819
|
+
if (fs6.existsSync(cargoTomlPath)) {
|
|
820
|
+
try {
|
|
821
|
+
const content = fs6.readFileSync(cargoTomlPath, "utf-8");
|
|
822
|
+
const match = content.match(/tauri\s*=\s*.*?"(\d+)\./);
|
|
823
|
+
if (match) {
|
|
824
|
+
const major = parseInt(match[1]);
|
|
825
|
+
return { version: major >= 2 ? "v2" : "v1" };
|
|
826
|
+
}
|
|
827
|
+
return { version: "v2" };
|
|
828
|
+
} catch {}
|
|
829
|
+
}
|
|
830
|
+
return {
|
|
831
|
+
version: null,
|
|
832
|
+
error: "Could not detect Tauri version. Use --fw-version to specify (v1 or v2)."
|
|
833
|
+
};
|
|
834
|
+
}
|
|
835
|
+
var tauriProvider = {
|
|
836
|
+
name: "tauri",
|
|
837
|
+
displayName: "Tauri",
|
|
838
|
+
repo: "tauri-apps/tauri-docs",
|
|
839
|
+
docsPath: "src/content/docs",
|
|
840
|
+
extensions: [".md", ".mdx"],
|
|
841
|
+
detectVersion: detectVersion5,
|
|
842
|
+
versionToTag: (version) => version,
|
|
843
|
+
excludePatterns: [
|
|
844
|
+
"**/index.mdx",
|
|
845
|
+
"**/index.md",
|
|
846
|
+
"**/_fragments/**",
|
|
847
|
+
"**/_it/**",
|
|
848
|
+
"**/es/**",
|
|
849
|
+
"**/fr/**",
|
|
850
|
+
"**/it/**",
|
|
851
|
+
"**/ja/**",
|
|
852
|
+
"**/ko/**",
|
|
853
|
+
"**/zh-cn/**",
|
|
854
|
+
"**/404.md",
|
|
855
|
+
"**/rss.mdx"
|
|
856
|
+
],
|
|
857
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any Tauri tasks. Tauri is a framework for building desktop and mobile apps with web frontends."
|
|
858
|
+
};
|
|
859
|
+
|
|
860
|
+
// src/lib/providers/conda-forge.ts
|
|
861
|
+
function detectVersion6(_cwd) {
|
|
862
|
+
return { version: "main" };
|
|
863
|
+
}
|
|
864
|
+
var condaForgeProvider = {
|
|
865
|
+
name: "conda-forge",
|
|
866
|
+
displayName: "conda-forge",
|
|
867
|
+
repo: "conda-forge/conda-forge.github.io",
|
|
868
|
+
docsPath: "docs",
|
|
869
|
+
extensions: [".md", ".mdx"],
|
|
870
|
+
detectVersion: detectVersion6,
|
|
871
|
+
versionToTag: (version) => version,
|
|
872
|
+
excludePatterns: [
|
|
873
|
+
"**/index.md",
|
|
874
|
+
"**/_sidebar.js",
|
|
875
|
+
"**/_sidebar.json",
|
|
876
|
+
"**/_sidebar_diataxis.json"
|
|
877
|
+
],
|
|
878
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any conda-forge tasks. conda-forge is a community-led collection of recipes, build infrastructure, and packages for conda."
|
|
879
|
+
};
|
|
880
|
+
|
|
881
|
+
// src/lib/providers/bun.ts
|
|
882
|
+
import fs7 from "fs";
|
|
883
|
+
import path7 from "path";
|
|
884
|
+
import { execSync as execSync2 } from "child_process";
|
|
885
|
+
function detectVersion7(cwd) {
|
|
886
|
+
const bunLockPath = path7.join(cwd, "bun.lockb");
|
|
887
|
+
const hasBunLock = fs7.existsSync(bunLockPath);
|
|
888
|
+
const bunfigPath = path7.join(cwd, "bunfig.toml");
|
|
889
|
+
const hasBunfig = fs7.existsSync(bunfigPath);
|
|
890
|
+
if (!hasBunLock && !hasBunfig) {
|
|
891
|
+
return {
|
|
892
|
+
version: null,
|
|
893
|
+
error: "No bun.lockb or bunfig.toml found in the current directory"
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
try {
|
|
897
|
+
const output = execSync2("bun --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
898
|
+
const versionMatch = output.match(/([\d]+\.[\d]+\.[\d]+)/);
|
|
899
|
+
if (versionMatch) {
|
|
900
|
+
return { version: versionMatch[1] };
|
|
901
|
+
}
|
|
902
|
+
} catch {}
|
|
903
|
+
return {
|
|
904
|
+
version: null,
|
|
905
|
+
error: "Bun project detected but could not determine version. Please specify with --fw-version."
|
|
906
|
+
};
|
|
907
|
+
}
|
|
908
|
+
var bunProvider = {
|
|
909
|
+
name: "bun",
|
|
910
|
+
displayName: "Bun",
|
|
911
|
+
repo: "oven-sh/bun",
|
|
912
|
+
docsPath: "docs",
|
|
913
|
+
extensions: [".md", ".mdx"],
|
|
914
|
+
detectVersion: detectVersion7,
|
|
915
|
+
versionToTag: (version) => `bun-v${version}`,
|
|
916
|
+
excludePatterns: ["**/README.md"],
|
|
917
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any Bun tasks."
|
|
918
|
+
};
|
|
919
|
+
|
|
920
|
+
// src/lib/providers/svelte.ts
|
|
921
|
+
import fs8 from "fs";
|
|
922
|
+
import path8 from "path";
|
|
923
|
+
function detectVersion8(cwd) {
|
|
924
|
+
const packageJsonPath = path8.join(cwd, "package.json");
|
|
925
|
+
if (!fs8.existsSync(packageJsonPath)) {
|
|
926
|
+
return {
|
|
927
|
+
version: null,
|
|
928
|
+
error: "No package.json found in the current directory"
|
|
929
|
+
};
|
|
930
|
+
}
|
|
931
|
+
try {
|
|
932
|
+
const packageJson = JSON.parse(fs8.readFileSync(packageJsonPath, "utf-8"));
|
|
933
|
+
const dependencies = packageJson.dependencies || {};
|
|
934
|
+
const devDependencies = packageJson.devDependencies || {};
|
|
935
|
+
const svelteVersion = dependencies.svelte || devDependencies.svelte;
|
|
936
|
+
if (svelteVersion) {
|
|
937
|
+
const cleanVersion = svelteVersion.replace(/^[\^~>=<]+/, "");
|
|
938
|
+
return { version: cleanVersion };
|
|
939
|
+
}
|
|
940
|
+
return {
|
|
941
|
+
version: null,
|
|
942
|
+
error: "Svelte is not installed in this project."
|
|
943
|
+
};
|
|
944
|
+
} catch (err) {
|
|
945
|
+
return {
|
|
946
|
+
version: null,
|
|
947
|
+
error: `Failed to parse package.json: ${err instanceof Error ? err.message : String(err)}`
|
|
948
|
+
};
|
|
949
|
+
}
|
|
950
|
+
}
|
|
951
|
+
var svelteProvider = {
|
|
952
|
+
name: "svelte",
|
|
953
|
+
displayName: "Svelte",
|
|
954
|
+
repo: "sveltejs/svelte",
|
|
955
|
+
docsPath: "documentation/docs",
|
|
956
|
+
extensions: [".md", ".mdx"],
|
|
957
|
+
detectVersion: detectVersion8,
|
|
958
|
+
versionToTag: (version) => {
|
|
959
|
+
const major = parseInt(version.split(".")[0], 10);
|
|
960
|
+
if (major >= 5) {
|
|
961
|
+
return `svelte@${version}`;
|
|
962
|
+
}
|
|
963
|
+
return version.startsWith("v") ? version : `v${version}`;
|
|
964
|
+
},
|
|
965
|
+
excludePatterns: ["**/index.md"],
|
|
966
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any Svelte tasks."
|
|
967
|
+
};
|
|
968
|
+
|
|
969
|
+
// src/lib/providers/tailwind.ts
|
|
970
|
+
import fs9 from "fs";
|
|
971
|
+
import path9 from "path";
|
|
972
|
+
function detectVersion9(cwd) {
|
|
973
|
+
const packageJsonPath = path9.join(cwd, "package.json");
|
|
974
|
+
if (!fs9.existsSync(packageJsonPath)) {
|
|
975
|
+
return {
|
|
976
|
+
version: null,
|
|
977
|
+
error: "No package.json found in the current directory"
|
|
978
|
+
};
|
|
979
|
+
}
|
|
980
|
+
try {
|
|
981
|
+
const packageJson = JSON.parse(fs9.readFileSync(packageJsonPath, "utf-8"));
|
|
982
|
+
const dependencies = packageJson.dependencies || {};
|
|
983
|
+
const devDependencies = packageJson.devDependencies || {};
|
|
984
|
+
const tailwindVersion = dependencies.tailwindcss || devDependencies.tailwindcss;
|
|
985
|
+
if (tailwindVersion) {
|
|
986
|
+
const cleanVersion = tailwindVersion.replace(/^[\^~>=<]+/, "");
|
|
987
|
+
return { version: cleanVersion };
|
|
988
|
+
}
|
|
989
|
+
return {
|
|
990
|
+
version: null,
|
|
991
|
+
error: "Tailwind CSS is not installed in this project."
|
|
992
|
+
};
|
|
993
|
+
} catch (err) {
|
|
994
|
+
return {
|
|
995
|
+
version: null,
|
|
996
|
+
error: `Failed to parse package.json: ${err instanceof Error ? err.message : String(err)}`
|
|
997
|
+
};
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
var tailwindProvider = {
|
|
1001
|
+
name: "tailwind",
|
|
1002
|
+
displayName: "Tailwind CSS",
|
|
1003
|
+
repo: "tailwindlabs/tailwindcss.com",
|
|
1004
|
+
docsPath: "src/docs",
|
|
1005
|
+
extensions: [".md", ".mdx"],
|
|
1006
|
+
detectVersion: detectVersion9,
|
|
1007
|
+
versionToTag: (version) => version.startsWith("v") ? version : `v${version}`,
|
|
1008
|
+
excludePatterns: ["**/index.md"],
|
|
1009
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any Tailwind CSS tasks."
|
|
1010
|
+
};
|
|
1011
|
+
|
|
1012
|
+
// src/lib/providers/ruff.ts
|
|
1013
|
+
import fs10 from "fs";
|
|
1014
|
+
import path10 from "path";
|
|
1015
|
+
function parseRuffVersion(content) {
|
|
1016
|
+
const patterns = [
|
|
1017
|
+
/ruff\s*=\s*["']([^"']+)["']/,
|
|
1018
|
+
/["']ruff([><=!~]+[\d.]+)["']/,
|
|
1019
|
+
/["']ruff\s*([><=!~]*[\d.]+)["']/
|
|
1020
|
+
];
|
|
1021
|
+
for (const pattern of patterns) {
|
|
1022
|
+
const match = content.match(pattern);
|
|
1023
|
+
if (match) {
|
|
1024
|
+
const versionMatch = match[1].match(/[\d]+\.[\d]+\.[\d]+/);
|
|
1025
|
+
if (versionMatch) {
|
|
1026
|
+
return versionMatch[0];
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
return null;
|
|
1031
|
+
}
|
|
1032
|
+
function detectVersion10(cwd) {
|
|
1033
|
+
const pyprojectPath = path10.join(cwd, "pyproject.toml");
|
|
1034
|
+
if (fs10.existsSync(pyprojectPath)) {
|
|
1035
|
+
try {
|
|
1036
|
+
const content = fs10.readFileSync(pyprojectPath, "utf-8");
|
|
1037
|
+
if (content.includes("ruff")) {
|
|
1038
|
+
const version = parseRuffVersion(content);
|
|
1039
|
+
if (version) {
|
|
1040
|
+
return { version };
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
} catch {}
|
|
1044
|
+
}
|
|
1045
|
+
const ruffTomlPath = path10.join(cwd, "ruff.toml");
|
|
1046
|
+
if (fs10.existsSync(ruffTomlPath)) {}
|
|
1047
|
+
try {
|
|
1048
|
+
const { execSync: execSync3 } = __require("child_process");
|
|
1049
|
+
const output = execSync3("ruff --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
1050
|
+
const versionMatch = output.match(/ruff ([\d]+\.[\d]+\.[\d]+)/);
|
|
1051
|
+
if (versionMatch) {
|
|
1052
|
+
return { version: versionMatch[1] };
|
|
1053
|
+
}
|
|
1054
|
+
} catch {}
|
|
1055
|
+
return {
|
|
1056
|
+
version: null,
|
|
1057
|
+
error: "Could not detect ruff version. Use --fw-version to specify."
|
|
1058
|
+
};
|
|
1059
|
+
}
|
|
1060
|
+
var ruffProvider = {
|
|
1061
|
+
name: "ruff",
|
|
1062
|
+
displayName: "Ruff",
|
|
1063
|
+
repo: "astral-sh/ruff",
|
|
1064
|
+
docsPath: "docs",
|
|
1065
|
+
extensions: [".md", ".mdx"],
|
|
1066
|
+
detectVersion: detectVersion10,
|
|
1067
|
+
versionToTag: (version) => version.replace(/^v/, ""),
|
|
1068
|
+
excludePatterns: [
|
|
1069
|
+
"**/index.md",
|
|
1070
|
+
"**/assets/**",
|
|
1071
|
+
"**/stylesheets/**",
|
|
1072
|
+
"**/javascripts/**"
|
|
1073
|
+
],
|
|
1074
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any Ruff tasks. Ruff is an extremely fast Python linter and formatter."
|
|
1075
|
+
};
|
|
1076
|
+
|
|
1077
|
+
// src/lib/providers/ty.ts
|
|
1078
|
+
import fs11 from "fs";
|
|
1079
|
+
import path11 from "path";
|
|
1080
|
+
function parseTyVersion(content) {
|
|
1081
|
+
const patterns = [
|
|
1082
|
+
/\bty\s*=\s*["']([^"']+)["']/,
|
|
1083
|
+
/["']ty([><=!~]+[\d.]+)["']/,
|
|
1084
|
+
/["']ty\s*([><=!~]*[\d.]+)["']/
|
|
1085
|
+
];
|
|
1086
|
+
for (const pattern of patterns) {
|
|
1087
|
+
const match = content.match(pattern);
|
|
1088
|
+
if (match) {
|
|
1089
|
+
const versionMatch = match[1].match(/[\d]+\.[\d]+\.[\d]+/);
|
|
1090
|
+
if (versionMatch) {
|
|
1091
|
+
return versionMatch[0];
|
|
1092
|
+
}
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
return null;
|
|
1096
|
+
}
|
|
1097
|
+
function detectVersion11(cwd) {
|
|
1098
|
+
const pyprojectPath = path11.join(cwd, "pyproject.toml");
|
|
1099
|
+
if (fs11.existsSync(pyprojectPath)) {
|
|
1100
|
+
try {
|
|
1101
|
+
const content = fs11.readFileSync(pyprojectPath, "utf-8");
|
|
1102
|
+
if (content.includes("[tool.ty]") || /["']ty[>=<]/.test(content) || /\bty\s*=/.test(content)) {
|
|
1103
|
+
const version = parseTyVersion(content);
|
|
1104
|
+
if (version) {
|
|
1105
|
+
return { version };
|
|
1106
|
+
}
|
|
1107
|
+
}
|
|
1108
|
+
} catch {}
|
|
1109
|
+
}
|
|
1110
|
+
try {
|
|
1111
|
+
const { execSync: execSync3 } = __require("child_process");
|
|
1112
|
+
const output = execSync3("ty --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
1113
|
+
const versionMatch = output.match(/ty ([\d]+\.[\d]+\.[\d]+)/);
|
|
1114
|
+
if (versionMatch) {
|
|
1115
|
+
return { version: versionMatch[1] };
|
|
1116
|
+
}
|
|
1117
|
+
} catch {}
|
|
1118
|
+
return {
|
|
1119
|
+
version: null,
|
|
1120
|
+
error: "Could not detect ty version. Use --fw-version to specify."
|
|
1121
|
+
};
|
|
1122
|
+
}
|
|
1123
|
+
var tyProvider = {
|
|
1124
|
+
name: "ty",
|
|
1125
|
+
displayName: "ty",
|
|
1126
|
+
repo: "astral-sh/ty",
|
|
1127
|
+
docsPath: "docs",
|
|
1128
|
+
extensions: [".md", ".mdx"],
|
|
1129
|
+
detectVersion: detectVersion11,
|
|
1130
|
+
versionToTag: (version) => version.replace(/^v/, ""),
|
|
1131
|
+
excludePatterns: [
|
|
1132
|
+
"**/index.md",
|
|
1133
|
+
"**/assets/**",
|
|
1134
|
+
"**/stylesheets/**",
|
|
1135
|
+
"**/javascripts/**"
|
|
1136
|
+
],
|
|
1137
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any ty tasks. ty is an extremely fast Python type checker from Astral."
|
|
1138
|
+
};
|
|
1139
|
+
|
|
1140
|
+
// src/lib/providers/basedpyright.ts
|
|
1141
|
+
import fs12 from "fs";
|
|
1142
|
+
import path12 from "path";
|
|
1143
|
+
function parseBasedpyrightVersion(content) {
|
|
1144
|
+
const patterns = [
|
|
1145
|
+
/basedpyright\s*=\s*["']([^"']+)["']/,
|
|
1146
|
+
/["']basedpyright([><=!~]+[\d.]+)["']/,
|
|
1147
|
+
/["']basedpyright\s*([><=!~]*[\d.]+)["']/
|
|
1148
|
+
];
|
|
1149
|
+
for (const pattern of patterns) {
|
|
1150
|
+
const match = content.match(pattern);
|
|
1151
|
+
if (match) {
|
|
1152
|
+
const versionMatch = match[1].match(/[\d]+\.[\d]+\.[\d]+/);
|
|
1153
|
+
if (versionMatch) {
|
|
1154
|
+
return versionMatch[0];
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
return null;
|
|
1159
|
+
}
|
|
1160
|
+
function detectVersion12(cwd) {
|
|
1161
|
+
const pyprojectPath = path12.join(cwd, "pyproject.toml");
|
|
1162
|
+
if (fs12.existsSync(pyprojectPath)) {
|
|
1163
|
+
try {
|
|
1164
|
+
const content = fs12.readFileSync(pyprojectPath, "utf-8");
|
|
1165
|
+
if (content.includes("basedpyright")) {
|
|
1166
|
+
const version = parseBasedpyrightVersion(content);
|
|
1167
|
+
if (version) {
|
|
1168
|
+
return { version };
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
} catch {}
|
|
1172
|
+
}
|
|
1173
|
+
try {
|
|
1174
|
+
const { execSync: execSync3 } = __require("child_process");
|
|
1175
|
+
const output = execSync3("basedpyright --version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
1176
|
+
const versionMatch = output.match(/basedpyright ([\d]+\.[\d]+\.[\d]+)/);
|
|
1177
|
+
if (versionMatch) {
|
|
1178
|
+
return { version: versionMatch[1] };
|
|
1179
|
+
}
|
|
1180
|
+
} catch {}
|
|
1181
|
+
return {
|
|
1182
|
+
version: null,
|
|
1183
|
+
error: "Could not detect basedpyright version. Use --fw-version to specify."
|
|
1184
|
+
};
|
|
1185
|
+
}
|
|
1186
|
+
var basedpyrightProvider = {
|
|
1187
|
+
name: "basedpyright",
|
|
1188
|
+
displayName: "basedpyright",
|
|
1189
|
+
repo: "DetachHead/basedpyright",
|
|
1190
|
+
docsPath: "docs",
|
|
1191
|
+
extensions: [".md", ".mdx"],
|
|
1192
|
+
detectVersion: detectVersion12,
|
|
1193
|
+
versionToTag: (version) => version.startsWith("v") ? version : `v${version}`,
|
|
1194
|
+
excludePatterns: [
|
|
1195
|
+
"**/index.md",
|
|
1196
|
+
"**/assets/**",
|
|
1197
|
+
"**/stylesheets/**",
|
|
1198
|
+
"**/javascripts/**"
|
|
1199
|
+
],
|
|
1200
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any basedpyright tasks. basedpyright is a fork of pyright with various improvements."
|
|
1201
|
+
};
|
|
1202
|
+
|
|
1203
|
+
// src/lib/providers/convex.ts
|
|
1204
|
+
import fs13 from "fs";
|
|
1205
|
+
import path13 from "path";
|
|
1206
|
+
function detectVersion13(cwd) {
|
|
1207
|
+
const packageJsonPath = path13.join(cwd, "package.json");
|
|
1208
|
+
if (!fs13.existsSync(packageJsonPath)) {
|
|
1209
|
+
return {
|
|
1210
|
+
version: null,
|
|
1211
|
+
error: "No package.json found in the current directory"
|
|
1212
|
+
};
|
|
1213
|
+
}
|
|
1214
|
+
try {
|
|
1215
|
+
const packageJson = JSON.parse(fs13.readFileSync(packageJsonPath, "utf-8"));
|
|
1216
|
+
const dependencies = packageJson.dependencies || {};
|
|
1217
|
+
const devDependencies = packageJson.devDependencies || {};
|
|
1218
|
+
const convexVersion = dependencies.convex || devDependencies.convex;
|
|
1219
|
+
if (convexVersion) {
|
|
1220
|
+
const cleanVersion = convexVersion.replace(/^[\^~>=<]+/, "");
|
|
1221
|
+
return { version: cleanVersion };
|
|
1222
|
+
}
|
|
1223
|
+
return {
|
|
1224
|
+
version: null,
|
|
1225
|
+
error: "Convex is not installed in this project."
|
|
1226
|
+
};
|
|
1227
|
+
} catch (err) {
|
|
1228
|
+
return {
|
|
1229
|
+
version: null,
|
|
1230
|
+
error: `Failed to parse package.json: ${err instanceof Error ? err.message : String(err)}`
|
|
1231
|
+
};
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
var convexProvider = {
|
|
1235
|
+
name: "convex",
|
|
1236
|
+
displayName: "Convex",
|
|
1237
|
+
repo: "get-convex/convex-backend",
|
|
1238
|
+
docsPath: "npm-packages/docs/docs",
|
|
1239
|
+
extensions: [".md", ".mdx"],
|
|
1240
|
+
detectVersion: detectVersion13,
|
|
1241
|
+
versionToTag: (version) => `precompiled-${version}`,
|
|
1242
|
+
excludePatterns: ["**/index.md"],
|
|
1243
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any Convex tasks. Convex is a backend platform for web applications."
|
|
1244
|
+
};
|
|
1245
|
+
|
|
1246
|
+
// src/lib/providers/polars.ts
|
|
1247
|
+
import fs14 from "fs";
|
|
1248
|
+
import path14 from "path";
|
|
1249
|
+
function parsePolarsVersion(content) {
|
|
1250
|
+
const patterns = [
|
|
1251
|
+
/polars\s*=\s*["']([^"']+)["']/,
|
|
1252
|
+
/["']polars([><=!~]+[\d.]+)["']/,
|
|
1253
|
+
/["']polars\s*([><=!~]*[\d.]+)["']/
|
|
1254
|
+
];
|
|
1255
|
+
for (const pattern of patterns) {
|
|
1256
|
+
const match = content.match(pattern);
|
|
1257
|
+
if (match) {
|
|
1258
|
+
const versionMatch = match[1].match(/[\d]+\.[\d]+\.[\d]+/);
|
|
1259
|
+
if (versionMatch) {
|
|
1260
|
+
return versionMatch[0];
|
|
1261
|
+
}
|
|
1262
|
+
}
|
|
1263
|
+
}
|
|
1264
|
+
return null;
|
|
1265
|
+
}
|
|
1266
|
+
function parseRequirementsVersion(content) {
|
|
1267
|
+
const match = content.match(/polars[><=!~]*([\d]+\.[\d]+\.[\d]+)/);
|
|
1268
|
+
if (match) {
|
|
1269
|
+
return match[1];
|
|
1270
|
+
}
|
|
1271
|
+
return null;
|
|
1272
|
+
}
|
|
1273
|
+
function detectVersion14(cwd) {
|
|
1274
|
+
const pyprojectPath = path14.join(cwd, "pyproject.toml");
|
|
1275
|
+
if (fs14.existsSync(pyprojectPath)) {
|
|
1276
|
+
try {
|
|
1277
|
+
const content = fs14.readFileSync(pyprojectPath, "utf-8");
|
|
1278
|
+
if (content.includes("polars")) {
|
|
1279
|
+
const version = parsePolarsVersion(content);
|
|
1280
|
+
if (version) {
|
|
1281
|
+
return { version };
|
|
1282
|
+
}
|
|
1283
|
+
}
|
|
1284
|
+
} catch {}
|
|
1285
|
+
}
|
|
1286
|
+
const requirementsPath = path14.join(cwd, "requirements.txt");
|
|
1287
|
+
if (fs14.existsSync(requirementsPath)) {
|
|
1288
|
+
try {
|
|
1289
|
+
const content = fs14.readFileSync(requirementsPath, "utf-8");
|
|
1290
|
+
if (content.includes("polars")) {
|
|
1291
|
+
const version = parseRequirementsVersion(content);
|
|
1292
|
+
if (version) {
|
|
1293
|
+
return { version };
|
|
1294
|
+
}
|
|
1295
|
+
}
|
|
1296
|
+
} catch {}
|
|
1297
|
+
}
|
|
1298
|
+
try {
|
|
1299
|
+
const { execSync: execSync3 } = __require("child_process");
|
|
1300
|
+
const output = execSync3("pip show polars", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
1301
|
+
const versionMatch = output.match(/Version:\s*([\d]+\.[\d]+\.[\d]+)/);
|
|
1302
|
+
if (versionMatch) {
|
|
1303
|
+
return { version: versionMatch[1] };
|
|
1304
|
+
}
|
|
1305
|
+
} catch {}
|
|
1306
|
+
return {
|
|
1307
|
+
version: null,
|
|
1308
|
+
error: "Could not detect polars version. Use --fw-version to specify."
|
|
1309
|
+
};
|
|
1310
|
+
}
|
|
1311
|
+
var polarsProvider = {
|
|
1312
|
+
name: "polars",
|
|
1313
|
+
displayName: "Polars",
|
|
1314
|
+
repo: "pola-rs/polars",
|
|
1315
|
+
docsPath: "docs",
|
|
1316
|
+
extensions: [".md", ".mdx"],
|
|
1317
|
+
detectVersion: detectVersion14,
|
|
1318
|
+
versionToTag: (version) => `py-${version}`,
|
|
1319
|
+
excludePatterns: [
|
|
1320
|
+
"**/index.md",
|
|
1321
|
+
"**/assets/**",
|
|
1322
|
+
"**/stylesheets/**",
|
|
1323
|
+
"**/javascripts/**"
|
|
1324
|
+
],
|
|
1325
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any Polars tasks. Polars is a blazingly fast DataFrame library."
|
|
1326
|
+
};
|
|
1327
|
+
|
|
1328
|
+
// src/lib/providers/delta-rs.ts
|
|
1329
|
+
import fs15 from "fs";
|
|
1330
|
+
import path15 from "path";
|
|
1331
|
+
function parseDeltaLakeVersion(content) {
|
|
1332
|
+
const patterns = [
|
|
1333
|
+
/deltalake\s*=\s*["']([^"']+)["']/,
|
|
1334
|
+
/["']deltalake([><=!~]+[\d.]+)["']/,
|
|
1335
|
+
/["']deltalake\s*([><=!~]*[\d.]+)["']/
|
|
1336
|
+
];
|
|
1337
|
+
for (const pattern of patterns) {
|
|
1338
|
+
const match = content.match(pattern);
|
|
1339
|
+
if (match) {
|
|
1340
|
+
const versionMatch = match[1].match(/[\d]+\.[\d]+\.[\d]+/);
|
|
1341
|
+
if (versionMatch) {
|
|
1342
|
+
return versionMatch[0];
|
|
1343
|
+
}
|
|
1344
|
+
}
|
|
1345
|
+
}
|
|
1346
|
+
return null;
|
|
1347
|
+
}
|
|
1348
|
+
function parseRequirementsVersion2(content) {
|
|
1349
|
+
const match = content.match(/deltalake[><=!~]*([\d]+\.[\d]+\.[\d]+)/);
|
|
1350
|
+
if (match) {
|
|
1351
|
+
return match[1];
|
|
1352
|
+
}
|
|
1353
|
+
return null;
|
|
1354
|
+
}
|
|
1355
|
+
function detectVersion15(cwd) {
|
|
1356
|
+
const pyprojectPath = path15.join(cwd, "pyproject.toml");
|
|
1357
|
+
if (fs15.existsSync(pyprojectPath)) {
|
|
1358
|
+
try {
|
|
1359
|
+
const content = fs15.readFileSync(pyprojectPath, "utf-8");
|
|
1360
|
+
if (content.includes("deltalake")) {
|
|
1361
|
+
const version = parseDeltaLakeVersion(content);
|
|
1362
|
+
if (version) {
|
|
1363
|
+
return { version };
|
|
1364
|
+
}
|
|
1365
|
+
}
|
|
1366
|
+
} catch {}
|
|
1367
|
+
}
|
|
1368
|
+
const requirementsPath = path15.join(cwd, "requirements.txt");
|
|
1369
|
+
if (fs15.existsSync(requirementsPath)) {
|
|
1370
|
+
try {
|
|
1371
|
+
const content = fs15.readFileSync(requirementsPath, "utf-8");
|
|
1372
|
+
if (content.includes("deltalake")) {
|
|
1373
|
+
const version = parseRequirementsVersion2(content);
|
|
1374
|
+
if (version) {
|
|
1375
|
+
return { version };
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
} catch {}
|
|
1379
|
+
}
|
|
1380
|
+
try {
|
|
1381
|
+
const { execSync: execSync3 } = __require("child_process");
|
|
1382
|
+
const output = execSync3("pip show deltalake", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
1383
|
+
const versionMatch = output.match(/Version:\s*([\d]+\.[\d]+\.[\d]+)/);
|
|
1384
|
+
if (versionMatch) {
|
|
1385
|
+
return { version: versionMatch[1] };
|
|
1386
|
+
}
|
|
1387
|
+
} catch {}
|
|
1388
|
+
return {
|
|
1389
|
+
version: null,
|
|
1390
|
+
error: "Could not detect deltalake version. Use --fw-version to specify."
|
|
1391
|
+
};
|
|
1392
|
+
}
|
|
1393
|
+
var deltaRsProvider = {
|
|
1394
|
+
name: "delta-rs",
|
|
1395
|
+
displayName: "delta-rs",
|
|
1396
|
+
repo: "delta-io/delta-rs",
|
|
1397
|
+
docsPath: "docs",
|
|
1398
|
+
extensions: [".md", ".mdx"],
|
|
1399
|
+
detectVersion: detectVersion15,
|
|
1400
|
+
versionToTag: (version) => `python-v${version}`,
|
|
1401
|
+
excludePatterns: [
|
|
1402
|
+
"**/index.md",
|
|
1403
|
+
"**/assets/**",
|
|
1404
|
+
"**/stylesheets/**",
|
|
1405
|
+
"**/javascripts/**"
|
|
1406
|
+
],
|
|
1407
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any delta-rs/deltalake tasks. delta-rs is a native Rust implementation of Delta Lake."
|
|
1408
|
+
};
|
|
1409
|
+
|
|
1410
|
+
// src/lib/providers/obsidian.ts
|
|
1411
|
+
import fs16 from "fs";
|
|
1412
|
+
import path16 from "path";
|
|
1413
|
+
function detectVersion16(cwd) {
|
|
1414
|
+
const packageJsonPath = path16.join(cwd, "package.json");
|
|
1415
|
+
if (!fs16.existsSync(packageJsonPath)) {
|
|
1416
|
+
return {
|
|
1417
|
+
version: null,
|
|
1418
|
+
error: "No package.json found in the current directory"
|
|
1419
|
+
};
|
|
1420
|
+
}
|
|
1421
|
+
try {
|
|
1422
|
+
const packageJson = JSON.parse(fs16.readFileSync(packageJsonPath, "utf-8"));
|
|
1423
|
+
const dependencies = packageJson.dependencies || {};
|
|
1424
|
+
const devDependencies = packageJson.devDependencies || {};
|
|
1425
|
+
const obsidianVersion = dependencies.obsidian || devDependencies.obsidian;
|
|
1426
|
+
if (obsidianVersion) {
|
|
1427
|
+
const cleanVersion = obsidianVersion.replace(/^[\^~>=<]+/, "");
|
|
1428
|
+
return { version: cleanVersion };
|
|
1429
|
+
}
|
|
1430
|
+
return {
|
|
1431
|
+
version: null,
|
|
1432
|
+
error: "Obsidian is not installed in this project."
|
|
1433
|
+
};
|
|
1434
|
+
} catch (err) {
|
|
1435
|
+
return {
|
|
1436
|
+
version: null,
|
|
1437
|
+
error: `Failed to parse package.json: ${err instanceof Error ? err.message : String(err)}`
|
|
1438
|
+
};
|
|
1439
|
+
}
|
|
1440
|
+
}
|
|
1441
|
+
var obsidianProvider = {
|
|
1442
|
+
name: "obsidian",
|
|
1443
|
+
displayName: "Obsidian",
|
|
1444
|
+
repo: "obsidianmd/obsidian-developer-docs",
|
|
1445
|
+
docsPath: "en",
|
|
1446
|
+
extensions: [".md", ".mdx"],
|
|
1447
|
+
detectVersion: detectVersion16,
|
|
1448
|
+
versionToTag: (version) => version.startsWith("v") ? version : `v${version}`,
|
|
1449
|
+
excludePatterns: ["**/index.md"],
|
|
1450
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any Obsidian plugin development tasks."
|
|
1451
|
+
};
|
|
1452
|
+
|
|
1453
|
+
// src/lib/providers/obsidian-excalidraw.ts
|
|
1454
|
+
import fs17 from "fs";
|
|
1455
|
+
import path17 from "path";
|
|
1456
|
+
function detectVersion17(cwd) {
|
|
1457
|
+
const manifestPath = path17.join(cwd, "manifest.json");
|
|
1458
|
+
if (fs17.existsSync(manifestPath)) {
|
|
1459
|
+
try {
|
|
1460
|
+
const manifest = JSON.parse(fs17.readFileSync(manifestPath, "utf-8"));
|
|
1461
|
+
if (manifest.id === "obsidian-excalidraw-plugin" && manifest.version) {
|
|
1462
|
+
return { version: manifest.version };
|
|
1463
|
+
}
|
|
1464
|
+
} catch {}
|
|
1465
|
+
}
|
|
1466
|
+
return {
|
|
1467
|
+
version: null,
|
|
1468
|
+
error: "Could not detect obsidian-excalidraw-plugin version. Use --fw-version to specify."
|
|
1469
|
+
};
|
|
1470
|
+
}
|
|
1471
|
+
var obsidianExcalidrawProvider = {
|
|
1472
|
+
name: "obsidian-excalidraw",
|
|
1473
|
+
displayName: "Obsidian Excalidraw",
|
|
1474
|
+
repo: "zsviczian/obsidian-excalidraw-plugin",
|
|
1475
|
+
docsPath: "docs",
|
|
1476
|
+
extensions: [".md", ".mdx"],
|
|
1477
|
+
detectVersion: detectVersion17,
|
|
1478
|
+
versionToTag: (version) => version,
|
|
1479
|
+
excludePatterns: ["**/index.md"],
|
|
1480
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any Obsidian Excalidraw plugin tasks."
|
|
1481
|
+
};
|
|
1482
|
+
|
|
1483
|
+
// src/lib/providers/ffmpeg.ts
|
|
1484
|
+
function detectVersion18() {
|
|
1485
|
+
try {
|
|
1486
|
+
const { execSync: execSync3 } = __require("child_process");
|
|
1487
|
+
const output = execSync3("ffmpeg -version", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
1488
|
+
const versionMatch = output.match(/ffmpeg version (\d+\.\d+(?:\.\d+)?)/);
|
|
1489
|
+
if (versionMatch) {
|
|
1490
|
+
return { version: versionMatch[1] };
|
|
1491
|
+
}
|
|
1492
|
+
} catch {}
|
|
1493
|
+
return {
|
|
1494
|
+
version: null,
|
|
1495
|
+
error: "Could not detect FFmpeg version. Use --fw-version to specify."
|
|
1496
|
+
};
|
|
1497
|
+
}
|
|
1498
|
+
var ffmpegProvider = {
|
|
1499
|
+
name: "ffmpeg",
|
|
1500
|
+
displayName: "FFmpeg",
|
|
1501
|
+
repo: "FFmpeg/FFmpeg",
|
|
1502
|
+
docsPath: "doc",
|
|
1503
|
+
extensions: [".txt", ".md", ".texi"],
|
|
1504
|
+
detectVersion: detectVersion18,
|
|
1505
|
+
versionToTag: (version) => `n${version}`,
|
|
1506
|
+
excludePatterns: [
|
|
1507
|
+
"**/Makefile",
|
|
1508
|
+
"**/*.mak",
|
|
1509
|
+
"**/*.sh",
|
|
1510
|
+
"**/*.pl",
|
|
1511
|
+
"**/*.py"
|
|
1512
|
+
],
|
|
1513
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any FFmpeg tasks. FFmpeg is a multimedia framework for audio/video processing."
|
|
1514
|
+
};
|
|
1515
|
+
|
|
1516
|
+
// src/lib/providers/manim.ts
|
|
1517
|
+
import fs18 from "fs";
|
|
1518
|
+
import path18 from "path";
|
|
1519
|
+
function parseManimVersion(content) {
|
|
1520
|
+
const patterns = [
|
|
1521
|
+
/manim\s*=\s*["']([^"']+)["']/,
|
|
1522
|
+
/["']manim([><=!~]+[\d.]+)["']/,
|
|
1523
|
+
/["']manim\s*([><=!~]*[\d.]+)["']/
|
|
1524
|
+
];
|
|
1525
|
+
for (const pattern of patterns) {
|
|
1526
|
+
const match = content.match(pattern);
|
|
1527
|
+
if (match) {
|
|
1528
|
+
const versionMatch = match[1].match(/[\d]+\.[\d]+\.[\d]+/);
|
|
1529
|
+
if (versionMatch) {
|
|
1530
|
+
return versionMatch[0];
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
return null;
|
|
1535
|
+
}
|
|
1536
|
+
function parseRequirementsVersion3(content) {
|
|
1537
|
+
const match = content.match(/manim[><=!~]*([\d]+\.[\d]+\.[\d]+)/);
|
|
1538
|
+
if (match) {
|
|
1539
|
+
return match[1];
|
|
1540
|
+
}
|
|
1541
|
+
return null;
|
|
1542
|
+
}
|
|
1543
|
+
function detectVersion19(cwd) {
|
|
1544
|
+
const pyprojectPath = path18.join(cwd, "pyproject.toml");
|
|
1545
|
+
if (fs18.existsSync(pyprojectPath)) {
|
|
1546
|
+
try {
|
|
1547
|
+
const content = fs18.readFileSync(pyprojectPath, "utf-8");
|
|
1548
|
+
if (content.includes("manim")) {
|
|
1549
|
+
const version = parseManimVersion(content);
|
|
1550
|
+
if (version) {
|
|
1551
|
+
return { version };
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
} catch {}
|
|
1555
|
+
}
|
|
1556
|
+
const requirementsPath = path18.join(cwd, "requirements.txt");
|
|
1557
|
+
if (fs18.existsSync(requirementsPath)) {
|
|
1558
|
+
try {
|
|
1559
|
+
const content = fs18.readFileSync(requirementsPath, "utf-8");
|
|
1560
|
+
if (content.includes("manim")) {
|
|
1561
|
+
const version = parseRequirementsVersion3(content);
|
|
1562
|
+
if (version) {
|
|
1563
|
+
return { version };
|
|
1564
|
+
}
|
|
1565
|
+
}
|
|
1566
|
+
} catch {}
|
|
1567
|
+
}
|
|
1568
|
+
try {
|
|
1569
|
+
const { execSync: execSync3 } = __require("child_process");
|
|
1570
|
+
const output = execSync3("pip show manim", { encoding: "utf-8", stdio: ["pipe", "pipe", "pipe"] });
|
|
1571
|
+
const versionMatch = output.match(/Version:\s*([\d]+\.[\d]+\.[\d]+)/);
|
|
1572
|
+
if (versionMatch) {
|
|
1573
|
+
return { version: versionMatch[1] };
|
|
1574
|
+
}
|
|
1575
|
+
} catch {}
|
|
1576
|
+
return {
|
|
1577
|
+
version: null,
|
|
1578
|
+
error: "Could not detect manim version. Use --fw-version to specify."
|
|
1579
|
+
};
|
|
1580
|
+
}
|
|
1581
|
+
var manimProvider = {
|
|
1582
|
+
name: "manim",
|
|
1583
|
+
displayName: "Manim",
|
|
1584
|
+
repo: "ManimCommunity/manim",
|
|
1585
|
+
docsPath: "docs",
|
|
1586
|
+
extensions: [".md", ".rst", ".py"],
|
|
1587
|
+
detectVersion: detectVersion19,
|
|
1588
|
+
versionToTag: (version) => version.startsWith("v") ? version : `v${version}`,
|
|
1589
|
+
excludePatterns: [
|
|
1590
|
+
"**/index.md",
|
|
1591
|
+
"**/conf.py",
|
|
1592
|
+
"**/Makefile",
|
|
1593
|
+
"**/_static/**",
|
|
1594
|
+
"**/_templates/**"
|
|
1595
|
+
],
|
|
1596
|
+
instruction: "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any Manim tasks. Manim is a Python library for mathematical animations."
|
|
1597
|
+
};
|
|
1598
|
+
|
|
1599
|
+
// src/lib/providers/generic.ts
|
|
1600
|
+
import fs19 from "fs";
|
|
1601
|
+
import path19 from "path";
|
|
1602
|
+
function createProvider(options) {
|
|
1603
|
+
const {
|
|
1604
|
+
name,
|
|
1605
|
+
displayName,
|
|
1606
|
+
repo,
|
|
1607
|
+
docsPath,
|
|
1608
|
+
extensions = [".mdx", ".md"],
|
|
1609
|
+
packageName,
|
|
1610
|
+
versionToTag = (v) => {
|
|
1611
|
+
if (v.startsWith("v") || /^\d/.test(v)) {
|
|
1612
|
+
return v.startsWith("v") ? v : `v${v}`;
|
|
1613
|
+
}
|
|
1614
|
+
return v;
|
|
1615
|
+
},
|
|
1616
|
+
excludePatterns = ["**/index.mdx", "**/index.md"],
|
|
1617
|
+
instruction
|
|
1618
|
+
} = options;
|
|
1619
|
+
const detectVersion20 = packageName ? (cwd) => {
|
|
1620
|
+
const packageJsonPath = path19.join(cwd, "package.json");
|
|
1621
|
+
if (!fs19.existsSync(packageJsonPath)) {
|
|
1622
|
+
return {
|
|
1623
|
+
version: null,
|
|
1624
|
+
error: "No package.json found in the current directory"
|
|
1625
|
+
};
|
|
1626
|
+
}
|
|
1627
|
+
try {
|
|
1628
|
+
const packageJson = JSON.parse(fs19.readFileSync(packageJsonPath, "utf-8"));
|
|
1629
|
+
const dependencies = packageJson.dependencies || {};
|
|
1630
|
+
const devDependencies = packageJson.devDependencies || {};
|
|
1631
|
+
const version = dependencies[packageName] || devDependencies[packageName];
|
|
1632
|
+
if (version) {
|
|
1633
|
+
const cleanVersion = version.replace(/^[\^~>=<]+/, "");
|
|
1634
|
+
return { version: cleanVersion };
|
|
1635
|
+
}
|
|
1636
|
+
return {
|
|
1637
|
+
version: null,
|
|
1638
|
+
error: `${displayName} (${packageName}) is not installed in this project.`
|
|
1639
|
+
};
|
|
1640
|
+
} catch (err) {
|
|
1641
|
+
return {
|
|
1642
|
+
version: null,
|
|
1643
|
+
error: `Failed to parse package.json: ${err instanceof Error ? err.message : String(err)}`
|
|
1644
|
+
};
|
|
1645
|
+
}
|
|
1646
|
+
} : undefined;
|
|
1647
|
+
return {
|
|
1648
|
+
name,
|
|
1649
|
+
displayName,
|
|
1650
|
+
repo,
|
|
1651
|
+
docsPath,
|
|
1652
|
+
extensions,
|
|
1653
|
+
detectVersion: detectVersion20,
|
|
1654
|
+
versionToTag,
|
|
1655
|
+
excludePatterns,
|
|
1656
|
+
instruction: instruction || `IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any ${displayName} tasks.`
|
|
1657
|
+
};
|
|
1658
|
+
}
|
|
1659
|
+
function createLocalProvider(options) {
|
|
1660
|
+
return {
|
|
1661
|
+
name: options.name,
|
|
1662
|
+
displayName: options.displayName,
|
|
1663
|
+
repo: "",
|
|
1664
|
+
docsPath: options.localPath,
|
|
1665
|
+
extensions: options.extensions || [".mdx", ".md"],
|
|
1666
|
+
excludePatterns: options.excludePatterns || ["**/index.mdx", "**/index.md"],
|
|
1667
|
+
instruction: options.instruction || `IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any ${options.displayName} tasks.`
|
|
1668
|
+
};
|
|
1669
|
+
}
|
|
1670
|
+
// src/lib/providers/index.ts
|
|
1671
|
+
function getProvider(preset) {
|
|
1672
|
+
switch (preset) {
|
|
1673
|
+
case "nextjs":
|
|
1674
|
+
return nextjsProvider;
|
|
1675
|
+
case "react":
|
|
1676
|
+
return reactProvider;
|
|
1677
|
+
case "pixi":
|
|
1678
|
+
return pixiProvider;
|
|
1679
|
+
case "rattler-build":
|
|
1680
|
+
return rattlerBuildProvider;
|
|
1681
|
+
case "tauri":
|
|
1682
|
+
return tauriProvider;
|
|
1683
|
+
case "conda-forge":
|
|
1684
|
+
return condaForgeProvider;
|
|
1685
|
+
case "bun":
|
|
1686
|
+
return bunProvider;
|
|
1687
|
+
case "svelte":
|
|
1688
|
+
return svelteProvider;
|
|
1689
|
+
case "tailwind":
|
|
1690
|
+
return tailwindProvider;
|
|
1691
|
+
case "ruff":
|
|
1692
|
+
return ruffProvider;
|
|
1693
|
+
case "ty":
|
|
1694
|
+
return tyProvider;
|
|
1695
|
+
case "basedpyright":
|
|
1696
|
+
return basedpyrightProvider;
|
|
1697
|
+
case "convex":
|
|
1698
|
+
return convexProvider;
|
|
1699
|
+
case "polars":
|
|
1700
|
+
return polarsProvider;
|
|
1701
|
+
case "delta-rs":
|
|
1702
|
+
return deltaRsProvider;
|
|
1703
|
+
case "obsidian":
|
|
1704
|
+
return obsidianProvider;
|
|
1705
|
+
case "obsidian-excalidraw":
|
|
1706
|
+
return obsidianExcalidrawProvider;
|
|
1707
|
+
case "ffmpeg":
|
|
1708
|
+
return ffmpegProvider;
|
|
1709
|
+
case "manim":
|
|
1710
|
+
return manimProvider;
|
|
1711
|
+
case "vue":
|
|
1712
|
+
case "astro":
|
|
1713
|
+
return null;
|
|
1714
|
+
default:
|
|
1715
|
+
return null;
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
function listProviders() {
|
|
1719
|
+
return ["nextjs", "react", "pixi", "rattler-build", "tauri", "conda-forge", "bun", "vue", "svelte", "astro", "tailwind", "ruff", "ty", "basedpyright", "convex", "polars", "delta-rs", "obsidian", "obsidian-excalidraw", "ffmpeg", "manim"];
|
|
1720
|
+
}
|
|
1721
|
+
function isProviderAvailable(preset) {
|
|
1722
|
+
return getProvider(preset) !== null;
|
|
1723
|
+
}
|
|
1724
|
+
|
|
1725
|
+
// src/lib/skills.ts
|
|
1726
|
+
import fs20 from "fs";
|
|
1727
|
+
import path20 from "path";
|
|
1728
|
+
import os2 from "os";
|
|
1729
|
+
var SKILLS_START_MARKER = "<!-- AGENTS-MD-SKILLS-START -->";
|
|
1730
|
+
var SKILLS_END_MARKER = "<!-- AGENTS-MD-SKILLS-END -->";
|
|
1731
|
+
function parseEnabledPlugins(settingsPath) {
|
|
1732
|
+
if (!fs20.existsSync(settingsPath))
|
|
1733
|
+
return [];
|
|
1734
|
+
try {
|
|
1735
|
+
const content = fs20.readFileSync(settingsPath, "utf-8");
|
|
1736
|
+
const settings = JSON.parse(content);
|
|
1737
|
+
const enabledPlugins = settings.enabledPlugins || {};
|
|
1738
|
+
const plugins = [];
|
|
1739
|
+
for (const [key, enabled] of Object.entries(enabledPlugins)) {
|
|
1740
|
+
if (!enabled)
|
|
1741
|
+
continue;
|
|
1742
|
+
const match = key.match(/^(.+)@(.+)$/);
|
|
1743
|
+
if (match) {
|
|
1744
|
+
plugins.push({
|
|
1745
|
+
skillName: match[1],
|
|
1746
|
+
pluginRepo: match[2]
|
|
1747
|
+
});
|
|
1748
|
+
}
|
|
1749
|
+
}
|
|
1750
|
+
return plugins;
|
|
1751
|
+
} catch {
|
|
1752
|
+
return [];
|
|
1753
|
+
}
|
|
1754
|
+
}
|
|
1755
|
+
function findPluginSkillsPath(pluginRepo, skillName) {
|
|
1756
|
+
const cacheDir = path20.join(os2.homedir(), ".claude", "plugins", "cache", pluginRepo, skillName);
|
|
1757
|
+
if (!fs20.existsSync(cacheDir))
|
|
1758
|
+
return null;
|
|
1759
|
+
try {
|
|
1760
|
+
const entries = fs20.readdirSync(cacheDir, { withFileTypes: true });
|
|
1761
|
+
const hashDirs = entries.filter((e) => e.isDirectory() && !e.name.startsWith("."));
|
|
1762
|
+
if (hashDirs.length === 0)
|
|
1763
|
+
return null;
|
|
1764
|
+
const hashDir = hashDirs[0].name;
|
|
1765
|
+
const skillsPath = path20.join(cacheDir, hashDir, "skills");
|
|
1766
|
+
if (fs20.existsSync(skillsPath)) {
|
|
1767
|
+
return skillsPath;
|
|
1768
|
+
}
|
|
1769
|
+
return null;
|
|
1770
|
+
} catch {
|
|
1771
|
+
return null;
|
|
1772
|
+
}
|
|
1773
|
+
}
|
|
1774
|
+
function getEnabledPluginSources(cwd) {
|
|
1775
|
+
const sources = [];
|
|
1776
|
+
const seenPlugins = new Set;
|
|
1777
|
+
const settingsPaths = [
|
|
1778
|
+
path20.join(os2.homedir(), ".claude", "settings.json"),
|
|
1779
|
+
path20.join(cwd, ".claude", "settings.json")
|
|
1780
|
+
];
|
|
1781
|
+
for (const settingsPath of settingsPaths) {
|
|
1782
|
+
const plugins = parseEnabledPlugins(settingsPath);
|
|
1783
|
+
for (const { skillName, pluginRepo } of plugins) {
|
|
1784
|
+
const key = `${skillName}@${pluginRepo}`;
|
|
1785
|
+
if (seenPlugins.has(key))
|
|
1786
|
+
continue;
|
|
1787
|
+
seenPlugins.add(key);
|
|
1788
|
+
const skillsPath = findPluginSkillsPath(pluginRepo, skillName);
|
|
1789
|
+
if (skillsPath) {
|
|
1790
|
+
sources.push({
|
|
1791
|
+
type: "plugin",
|
|
1792
|
+
path: skillsPath,
|
|
1793
|
+
label: `${skillName}@${pluginRepo}`
|
|
1794
|
+
});
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
}
|
|
1798
|
+
return sources;
|
|
1799
|
+
}
|
|
1800
|
+
function parseSkillFrontmatter(content) {
|
|
1801
|
+
const match = content.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
1802
|
+
if (!match)
|
|
1803
|
+
return null;
|
|
1804
|
+
const frontmatter = match[1];
|
|
1805
|
+
const result = { name: "", description: "" };
|
|
1806
|
+
const nameMatch = frontmatter.match(/^name:\s*(.+)$/m);
|
|
1807
|
+
const descMatch = frontmatter.match(/^description:\s*(.+)$/m);
|
|
1808
|
+
if (nameMatch) {
|
|
1809
|
+
result.name = nameMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
1810
|
+
}
|
|
1811
|
+
if (descMatch) {
|
|
1812
|
+
result.description = descMatch[1].trim().replace(/^["']|["']$/g, "");
|
|
1813
|
+
}
|
|
1814
|
+
if (!result.name || !result.description) {
|
|
1815
|
+
return null;
|
|
1816
|
+
}
|
|
1817
|
+
return result;
|
|
1818
|
+
}
|
|
1819
|
+
function getFilesRecursively(dir, baseDir) {
|
|
1820
|
+
const files = [];
|
|
1821
|
+
try {
|
|
1822
|
+
const entries = fs20.readdirSync(dir, { withFileTypes: true });
|
|
1823
|
+
for (const entry of entries) {
|
|
1824
|
+
if (entry.name.startsWith(".") || entry.name === "SKILL.md")
|
|
1825
|
+
continue;
|
|
1826
|
+
const fullPath = path20.join(dir, entry.name);
|
|
1827
|
+
const relativePath = path20.relative(baseDir, fullPath);
|
|
1828
|
+
if (entry.isDirectory()) {
|
|
1829
|
+
files.push(...getFilesRecursively(fullPath, baseDir));
|
|
1830
|
+
} else {
|
|
1831
|
+
files.push(relativePath);
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
} catch {}
|
|
1835
|
+
return files;
|
|
1836
|
+
}
|
|
1837
|
+
function getSiblingFiles(skillMdPath) {
|
|
1838
|
+
const dir = path20.dirname(skillMdPath);
|
|
1839
|
+
if (!fs20.existsSync(dir))
|
|
1840
|
+
return [];
|
|
1841
|
+
return getFilesRecursively(dir, dir).sort();
|
|
1842
|
+
}
|
|
1843
|
+
function discoverPluginSkills(pluginsPath, label) {
|
|
1844
|
+
const skills = [];
|
|
1845
|
+
const pluginsDir = path20.join(pluginsPath, "plugins");
|
|
1846
|
+
if (!fs20.existsSync(pluginsDir)) {
|
|
1847
|
+
return skills;
|
|
1848
|
+
}
|
|
1849
|
+
const plugins = fs20.readdirSync(pluginsDir, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
1850
|
+
for (const plugin of plugins) {
|
|
1851
|
+
const skillsDir = path20.join(pluginsDir, plugin.name, "skills");
|
|
1852
|
+
if (!fs20.existsSync(skillsDir))
|
|
1853
|
+
continue;
|
|
1854
|
+
const skillDirs = fs20.readdirSync(skillsDir, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
1855
|
+
for (const skillDir of skillDirs) {
|
|
1856
|
+
const skillMdPath = path20.join(skillsDir, skillDir.name, "SKILL.md");
|
|
1857
|
+
if (!fs20.existsSync(skillMdPath))
|
|
1858
|
+
continue;
|
|
1859
|
+
try {
|
|
1860
|
+
const content = fs20.readFileSync(skillMdPath, "utf-8");
|
|
1861
|
+
const frontmatter = parseSkillFrontmatter(content);
|
|
1862
|
+
if (!frontmatter)
|
|
1863
|
+
continue;
|
|
1864
|
+
skills.push({
|
|
1865
|
+
name: frontmatter.name,
|
|
1866
|
+
description: frontmatter.description,
|
|
1867
|
+
skillMdPath,
|
|
1868
|
+
siblingFiles: getSiblingFiles(skillMdPath),
|
|
1869
|
+
source: "plugin",
|
|
1870
|
+
pluginName: plugin.name
|
|
1871
|
+
});
|
|
1872
|
+
} catch {}
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
return skills;
|
|
1876
|
+
}
|
|
1877
|
+
function discoverFlatSkills(skillsPath, source, label) {
|
|
1878
|
+
const skills = [];
|
|
1879
|
+
if (!fs20.existsSync(skillsPath)) {
|
|
1880
|
+
return skills;
|
|
1881
|
+
}
|
|
1882
|
+
const skillDirs = fs20.readdirSync(skillsPath, { withFileTypes: true }).filter((d) => d.isDirectory());
|
|
1883
|
+
for (const skillDir of skillDirs) {
|
|
1884
|
+
const skillMdPath = path20.join(skillsPath, skillDir.name, "SKILL.md");
|
|
1885
|
+
if (!fs20.existsSync(skillMdPath))
|
|
1886
|
+
continue;
|
|
1887
|
+
try {
|
|
1888
|
+
const content = fs20.readFileSync(skillMdPath, "utf-8");
|
|
1889
|
+
const frontmatter = parseSkillFrontmatter(content);
|
|
1890
|
+
if (!frontmatter)
|
|
1891
|
+
continue;
|
|
1892
|
+
skills.push({
|
|
1893
|
+
name: frontmatter.name,
|
|
1894
|
+
description: frontmatter.description,
|
|
1895
|
+
skillMdPath,
|
|
1896
|
+
siblingFiles: getSiblingFiles(skillMdPath),
|
|
1897
|
+
source
|
|
1898
|
+
});
|
|
1899
|
+
} catch {}
|
|
1900
|
+
}
|
|
1901
|
+
return skills;
|
|
1902
|
+
}
|
|
1903
|
+
function collectAllSkills(sources) {
|
|
1904
|
+
const allSkills = [];
|
|
1905
|
+
for (const source of sources) {
|
|
1906
|
+
if (source.type === "plugin") {
|
|
1907
|
+
const isCachePath = source.path.includes("/plugins/cache/");
|
|
1908
|
+
if (isCachePath) {
|
|
1909
|
+
const skills = discoverFlatSkills(source.path, "plugin", source.label);
|
|
1910
|
+
for (const skill of skills) {
|
|
1911
|
+
skill.pluginName = source.label;
|
|
1912
|
+
}
|
|
1913
|
+
allSkills.push(...skills);
|
|
1914
|
+
} else {
|
|
1915
|
+
allSkills.push(...discoverPluginSkills(source.path, source.label));
|
|
1916
|
+
}
|
|
1917
|
+
} else {
|
|
1918
|
+
allSkills.push(...discoverFlatSkills(source.path, source.type, source.label));
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
return allSkills;
|
|
1922
|
+
}
|
|
1923
|
+
function generateSkillsIndex(skills, options = {}) {
|
|
1924
|
+
const parts = ["[Skills Index]"];
|
|
1925
|
+
const pluginSkills = new Map;
|
|
1926
|
+
const userSkills = [];
|
|
1927
|
+
const projectSkills = [];
|
|
1928
|
+
for (const skill of skills) {
|
|
1929
|
+
if (skill.source === "plugin" && skill.pluginName) {
|
|
1930
|
+
const existing = pluginSkills.get(skill.pluginName) || [];
|
|
1931
|
+
existing.push(skill);
|
|
1932
|
+
pluginSkills.set(skill.pluginName, existing);
|
|
1933
|
+
} else if (skill.source === "user") {
|
|
1934
|
+
userSkills.push(skill);
|
|
1935
|
+
} else if (skill.source === "project") {
|
|
1936
|
+
projectSkills.push(skill);
|
|
1937
|
+
}
|
|
1938
|
+
}
|
|
1939
|
+
for (const [pluginName, entries] of pluginSkills) {
|
|
1940
|
+
const skillParts = entries.map((s) => formatSkillEntry(s)).join(";");
|
|
1941
|
+
parts.push(`plugin:${pluginName}:{${skillParts}}`);
|
|
1942
|
+
}
|
|
1943
|
+
if (userSkills.length > 0) {
|
|
1944
|
+
const skillParts = userSkills.map((s) => formatSkillEntry(s)).join(";");
|
|
1945
|
+
parts.push(`user:{${skillParts}}`);
|
|
1946
|
+
}
|
|
1947
|
+
if (projectSkills.length > 0) {
|
|
1948
|
+
const skillParts = projectSkills.map((s) => formatSkillEntry(s)).join(";");
|
|
1949
|
+
parts.push(`project:{${skillParts}}`);
|
|
1950
|
+
}
|
|
1951
|
+
const cmd = options.regenerateCommand || "npx agdex skills embed";
|
|
1952
|
+
parts.push(`Regen: ${cmd}`);
|
|
1953
|
+
return parts.join("|");
|
|
1954
|
+
}
|
|
1955
|
+
function formatSkillEntry(skill) {
|
|
1956
|
+
const desc = skill.description.replace(/\|/g, "\\|").replace(/;/g, "\\;").replace(/:/g, "\\:").replace(/\[/g, "\\[").replace(/\]/g, "\\]").replace(/\{/g, "\\{").replace(/\}/g, "\\}");
|
|
1957
|
+
if (skill.siblingFiles.length > 0) {
|
|
1958
|
+
return `${skill.name}:${desc}[${skill.siblingFiles.join(",")}]`;
|
|
1959
|
+
}
|
|
1960
|
+
return `${skill.name}:${desc}`;
|
|
1961
|
+
}
|
|
1962
|
+
function hasExistingSkillsIndex(content) {
|
|
1963
|
+
return content.includes(SKILLS_START_MARKER);
|
|
1964
|
+
}
|
|
1965
|
+
function removeSkillsIndex(content) {
|
|
1966
|
+
if (!hasExistingSkillsIndex(content)) {
|
|
1967
|
+
return content;
|
|
1968
|
+
}
|
|
1969
|
+
const startIdx = content.indexOf(SKILLS_START_MARKER);
|
|
1970
|
+
const endIdx = content.indexOf(SKILLS_END_MARKER) + SKILLS_END_MARKER.length;
|
|
1971
|
+
let result = content.slice(0, startIdx) + content.slice(endIdx);
|
|
1972
|
+
result = result.replace(/\n{3,}/g, `
|
|
1973
|
+
|
|
1974
|
+
`);
|
|
1975
|
+
result = result.trimEnd();
|
|
1976
|
+
if (result.length > 0) {
|
|
1977
|
+
result += `
|
|
1978
|
+
`;
|
|
1979
|
+
}
|
|
1980
|
+
return result;
|
|
1981
|
+
}
|
|
1982
|
+
function injectSkillsIndex(existingContent, indexContent) {
|
|
1983
|
+
const wrappedContent = `${SKILLS_START_MARKER}
|
|
1984
|
+
${indexContent}
|
|
1985
|
+
${SKILLS_END_MARKER}`;
|
|
1986
|
+
if (hasExistingSkillsIndex(existingContent)) {
|
|
1987
|
+
const startIdx = existingContent.indexOf(SKILLS_START_MARKER);
|
|
1988
|
+
const endIdx = existingContent.indexOf(SKILLS_END_MARKER) + SKILLS_END_MARKER.length;
|
|
1989
|
+
return existingContent.slice(0, startIdx) + wrappedContent + existingContent.slice(endIdx);
|
|
1990
|
+
}
|
|
1991
|
+
const separator = existingContent.endsWith(`
|
|
1992
|
+
`) ? `
|
|
1993
|
+
` : `
|
|
1994
|
+
|
|
1995
|
+
`;
|
|
1996
|
+
return existingContent + separator + wrappedContent + `
|
|
1997
|
+
`;
|
|
1998
|
+
}
|
|
1999
|
+
function getDefaultSkillSources(cwd, options = {}) {
|
|
2000
|
+
const sources = [];
|
|
2001
|
+
const {
|
|
2002
|
+
includeUser = true,
|
|
2003
|
+
includeProject = true,
|
|
2004
|
+
includeEnabledPlugins = true,
|
|
2005
|
+
pluginPaths = []
|
|
2006
|
+
} = options;
|
|
2007
|
+
for (const pluginPath of pluginPaths) {
|
|
2008
|
+
sources.push({
|
|
2009
|
+
type: "plugin",
|
|
2010
|
+
path: pluginPath,
|
|
2011
|
+
label: path20.basename(pluginPath)
|
|
2012
|
+
});
|
|
2013
|
+
}
|
|
2014
|
+
if (includeEnabledPlugins) {
|
|
2015
|
+
const enabledPluginSources = getEnabledPluginSources(cwd);
|
|
2016
|
+
sources.push(...enabledPluginSources);
|
|
2017
|
+
}
|
|
2018
|
+
if (includeUser) {
|
|
2019
|
+
const userSkillsPath = path20.join(os2.homedir(), ".claude", "skills");
|
|
2020
|
+
sources.push({
|
|
2021
|
+
type: "user",
|
|
2022
|
+
path: userSkillsPath,
|
|
2023
|
+
label: "user"
|
|
2024
|
+
});
|
|
2025
|
+
}
|
|
2026
|
+
if (includeProject) {
|
|
2027
|
+
const projectSkillsPath = path20.join(cwd, ".claude", "skills");
|
|
2028
|
+
sources.push({
|
|
2029
|
+
type: "project",
|
|
2030
|
+
path: projectSkillsPath,
|
|
2031
|
+
label: "project"
|
|
2032
|
+
});
|
|
2033
|
+
}
|
|
2034
|
+
return sources;
|
|
2035
|
+
}
|
|
2036
|
+
async function embedSkills(options) {
|
|
2037
|
+
const { cwd, sources, output = "AGENTS.md" } = options;
|
|
2038
|
+
const targetPath = path20.join(cwd, output);
|
|
2039
|
+
let sizeBefore = 0;
|
|
2040
|
+
let isNewFile = true;
|
|
2041
|
+
let existingContent = "";
|
|
2042
|
+
if (fs20.existsSync(targetPath)) {
|
|
2043
|
+
existingContent = fs20.readFileSync(targetPath, "utf-8");
|
|
2044
|
+
sizeBefore = Buffer.byteLength(existingContent, "utf-8");
|
|
2045
|
+
isNewFile = false;
|
|
2046
|
+
}
|
|
2047
|
+
const skills = collectAllSkills(sources);
|
|
2048
|
+
if (skills.length === 0) {
|
|
2049
|
+
return {
|
|
2050
|
+
success: false,
|
|
2051
|
+
error: "No skills found in any of the specified sources"
|
|
2052
|
+
};
|
|
2053
|
+
}
|
|
2054
|
+
const sourceBreakdown = {
|
|
2055
|
+
plugin: 0,
|
|
2056
|
+
user: 0,
|
|
2057
|
+
project: 0
|
|
2058
|
+
};
|
|
2059
|
+
for (const skill of skills) {
|
|
2060
|
+
sourceBreakdown[skill.source]++;
|
|
2061
|
+
}
|
|
2062
|
+
const indexContent = generateSkillsIndex(skills, {
|
|
2063
|
+
regenerateCommand: `npx agdex skills embed`
|
|
2064
|
+
});
|
|
2065
|
+
const newContent = injectSkillsIndex(existingContent, indexContent);
|
|
2066
|
+
fs20.writeFileSync(targetPath, newContent, "utf-8");
|
|
2067
|
+
const sizeAfter = Buffer.byteLength(newContent, "utf-8");
|
|
2068
|
+
return {
|
|
2069
|
+
success: true,
|
|
2070
|
+
targetFile: output,
|
|
2071
|
+
skillCount: skills.length,
|
|
2072
|
+
sizeBefore,
|
|
2073
|
+
sizeAfter,
|
|
2074
|
+
isNewFile,
|
|
2075
|
+
sourceBreakdown
|
|
2076
|
+
};
|
|
2077
|
+
}
|
|
2078
|
+
|
|
2079
|
+
// src/lib/config.ts
|
|
2080
|
+
import fs21 from "fs";
|
|
2081
|
+
import path21 from "path";
|
|
2082
|
+
var DEFAULT_CONFIG = {
|
|
2083
|
+
output: "CLAUDE.md"
|
|
2084
|
+
};
|
|
2085
|
+
function loadConfig(cwd = process.cwd()) {
|
|
2086
|
+
const rcPath = path21.join(cwd, ".agdexrc.json");
|
|
2087
|
+
if (fs21.existsSync(rcPath)) {
|
|
2088
|
+
try {
|
|
2089
|
+
const content = fs21.readFileSync(rcPath, "utf-8");
|
|
2090
|
+
const config = JSON.parse(content);
|
|
2091
|
+
return { ...DEFAULT_CONFIG, ...config };
|
|
2092
|
+
} catch {}
|
|
2093
|
+
}
|
|
2094
|
+
const packageJsonPath = path21.join(cwd, "package.json");
|
|
2095
|
+
if (fs21.existsSync(packageJsonPath)) {
|
|
2096
|
+
try {
|
|
2097
|
+
const content = fs21.readFileSync(packageJsonPath, "utf-8");
|
|
2098
|
+
const packageJson = JSON.parse(content);
|
|
2099
|
+
if (packageJson.agdex && typeof packageJson.agdex === "object") {
|
|
2100
|
+
return { ...DEFAULT_CONFIG, ...packageJson.agdex };
|
|
2101
|
+
}
|
|
2102
|
+
} catch {}
|
|
2103
|
+
}
|
|
2104
|
+
return DEFAULT_CONFIG;
|
|
2105
|
+
}
|
|
2106
|
+
function getDefaultOutput(cwd = process.cwd()) {
|
|
2107
|
+
const config = loadConfig(cwd);
|
|
2108
|
+
return config.output || "AGENTS.md";
|
|
2109
|
+
}
|
|
2110
|
+
|
|
2111
|
+
export { __toESM, __commonJS, __require, pullDocs, collectDocFiles, buildDocTree, generateIndex, hasExistingIndex, removeDocsIndex, injectIndex, ensureGitignoreEntry, getGlobalCacheDir, getLocalCacheDir, embed, nextjsProvider, reactProvider, pixiProvider, rattlerBuildProvider, tauriProvider, condaForgeProvider, bunProvider, svelteProvider, tailwindProvider, ruffProvider, tyProvider, basedpyrightProvider, convexProvider, polarsProvider, deltaRsProvider, obsidianProvider, obsidianExcalidrawProvider, ffmpegProvider, manimProvider, createProvider, createLocalProvider, getProvider, listProviders, isProviderAvailable, getEnabledPluginSources, parseSkillFrontmatter, discoverPluginSkills, discoverFlatSkills, collectAllSkills, generateSkillsIndex, hasExistingSkillsIndex, removeSkillsIndex, injectSkillsIndex, getDefaultSkillSources, embedSkills, loadConfig, getDefaultOutput };
|