skilldex 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/cli/index.js +701 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/index.cjs +510 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +148 -0
- package/dist/index.d.ts +148 -0
- package/dist/index.js +458 -0
- package/dist/index.js.map +1 -0
- package/package.json +74 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
// src/lib/config.ts
|
|
2
|
+
import { writeFile } from "fs/promises";
|
|
3
|
+
import { join as join2 } from "path";
|
|
4
|
+
|
|
5
|
+
// src/lib/constants.ts
|
|
6
|
+
var START_TAG = "<!-- skilldex:start (auto-generated, do not edit) -->";
|
|
7
|
+
var END_TAG = "<!-- skilldex:end -->";
|
|
8
|
+
var TARGET_FILE = "AGENTS.md";
|
|
9
|
+
var CONFIG_FILENAME = "skilldex.config.json";
|
|
10
|
+
var SKILL_META_FILE = "SKILL.md";
|
|
11
|
+
var INDEX_HEADER = "[Skills Index]";
|
|
12
|
+
var INDEX_INSTRUCTION = "IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any tasks covered by indexed skills.";
|
|
13
|
+
var CONTEXT_BUDGET_WARN_KB = 20;
|
|
14
|
+
var CONTEXT_BUDGET_DANGER_KB = 40;
|
|
15
|
+
function compareByNameThenPath(a, b) {
|
|
16
|
+
return a.name.localeCompare(b.name) || a.path.localeCompare(b.path);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// src/lib/scanner.ts
|
|
20
|
+
import { lstat, readdir, readFile } from "fs/promises";
|
|
21
|
+
import { join, relative } from "path";
|
|
22
|
+
|
|
23
|
+
// src/lib/agents.ts
|
|
24
|
+
var AGENT_SOURCES = [
|
|
25
|
+
{ name: "universal", displayName: "Universal", skillsDir: ".agents/skills" },
|
|
26
|
+
{ name: "antigravity", displayName: "Antigravity", skillsDir: ".agent/skills" },
|
|
27
|
+
{ name: "claude-code", displayName: "Claude Code", skillsDir: ".claude/skills" },
|
|
28
|
+
{ name: "codex", displayName: "Codex", skillsDir: ".agents/skills" },
|
|
29
|
+
{ name: "cursor", displayName: "Cursor", skillsDir: ".cursor/skills" },
|
|
30
|
+
{ name: "github-copilot", displayName: "GitHub Copilot", skillsDir: ".agents/skills" },
|
|
31
|
+
{ name: "opencode", displayName: "OpenCode", skillsDir: ".agents/skills" },
|
|
32
|
+
{ name: "openclaw", displayName: "OpenClaw", skillsDir: "skills" },
|
|
33
|
+
{ name: "windsurf", displayName: "Windsurf", skillsDir: ".windsurf/skills" }
|
|
34
|
+
];
|
|
35
|
+
function getUniqueSkillsDirs() {
|
|
36
|
+
return [...new Set(AGENT_SOURCES.map((a) => a.skillsDir))];
|
|
37
|
+
}
|
|
38
|
+
function getAgentDisplayName(relativePath) {
|
|
39
|
+
for (const agent of AGENT_SOURCES) {
|
|
40
|
+
if (relativePath.startsWith(`${agent.skillsDir}/`) || relativePath === agent.skillsDir) {
|
|
41
|
+
return agent.displayName;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return void 0;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// src/lib/scanner.ts
|
|
48
|
+
function parseFrontmatter(content) {
|
|
49
|
+
const result = {};
|
|
50
|
+
if (!content.startsWith("---")) return result;
|
|
51
|
+
const endIndex = content.indexOf("\n---", 3);
|
|
52
|
+
if (endIndex === -1) return result;
|
|
53
|
+
const block = content.slice(4, endIndex);
|
|
54
|
+
for (const line of block.split("\n")) {
|
|
55
|
+
const colonIndex = line.indexOf(":");
|
|
56
|
+
if (colonIndex === -1) continue;
|
|
57
|
+
const key = line.slice(0, colonIndex).trim();
|
|
58
|
+
const value = line.slice(colonIndex + 1).trim();
|
|
59
|
+
if (key) result[key] = value;
|
|
60
|
+
}
|
|
61
|
+
return result;
|
|
62
|
+
}
|
|
63
|
+
async function safeReaddir(dir) {
|
|
64
|
+
try {
|
|
65
|
+
return await readdir(dir, { withFileTypes: true });
|
|
66
|
+
} catch {
|
|
67
|
+
return [];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function safeReadFile(path) {
|
|
71
|
+
try {
|
|
72
|
+
return await readFile(path, "utf-8");
|
|
73
|
+
} catch {
|
|
74
|
+
return void 0;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
async function collectMdFiles(dir, skillRoot) {
|
|
78
|
+
const entries = await safeReaddir(dir);
|
|
79
|
+
const files = [];
|
|
80
|
+
for (const entry of entries) {
|
|
81
|
+
const fullPath = join(dir, entry.name);
|
|
82
|
+
if (entry.isDirectory()) {
|
|
83
|
+
const nested = await collectMdFiles(fullPath, skillRoot);
|
|
84
|
+
files.push(...nested);
|
|
85
|
+
} else if (entry.isFile() && entry.name.endsWith(".md") && entry.name !== SKILL_META_FILE) {
|
|
86
|
+
files.push({
|
|
87
|
+
relativePath: relative(skillRoot, fullPath),
|
|
88
|
+
name: entry.name.replace(/\.md$/, "")
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return files;
|
|
93
|
+
}
|
|
94
|
+
async function isSymlink(path) {
|
|
95
|
+
try {
|
|
96
|
+
const stats = await lstat(path);
|
|
97
|
+
return stats.isSymbolicLink();
|
|
98
|
+
} catch {
|
|
99
|
+
return false;
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function scanDirectory(dir, projectRoot) {
|
|
103
|
+
const entries = await safeReaddir(dir);
|
|
104
|
+
const skills = [];
|
|
105
|
+
for (const entry of entries) {
|
|
106
|
+
if (!entry.isDirectory()) continue;
|
|
107
|
+
const skillPath = join(dir, entry.name);
|
|
108
|
+
if (await isSymlink(skillPath)) continue;
|
|
109
|
+
let description = "";
|
|
110
|
+
const skillMd = await safeReadFile(join(skillPath, SKILL_META_FILE));
|
|
111
|
+
if (skillMd !== void 0) {
|
|
112
|
+
const frontmatter = parseFrontmatter(skillMd);
|
|
113
|
+
description = frontmatter.description ?? "";
|
|
114
|
+
}
|
|
115
|
+
const files = await collectMdFiles(skillPath, skillPath);
|
|
116
|
+
skills.push({
|
|
117
|
+
name: entry.name,
|
|
118
|
+
description,
|
|
119
|
+
path: skillPath,
|
|
120
|
+
relativePath: relative(projectRoot, skillPath),
|
|
121
|
+
files
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
return skills;
|
|
125
|
+
}
|
|
126
|
+
async function scanForSkills(projectRoot) {
|
|
127
|
+
const results = await Promise.all(
|
|
128
|
+
getUniqueSkillsDirs().map((d) => scanDirectory(join(projectRoot, d), projectRoot))
|
|
129
|
+
);
|
|
130
|
+
return results.flat().sort(compareByNameThenPath);
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// src/lib/config.ts
|
|
134
|
+
function getDefaultConfig() {
|
|
135
|
+
return {
|
|
136
|
+
version: 1,
|
|
137
|
+
targets: [TARGET_FILE],
|
|
138
|
+
skills: []
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
async function readConfig(projectRoot) {
|
|
142
|
+
const configPath = join2(projectRoot, CONFIG_FILENAME);
|
|
143
|
+
const content = await safeReadFile(configPath);
|
|
144
|
+
if (content === void 0) {
|
|
145
|
+
return getDefaultConfig();
|
|
146
|
+
}
|
|
147
|
+
try {
|
|
148
|
+
const parsed = JSON.parse(content);
|
|
149
|
+
if (typeof parsed.version !== "number" || !Array.isArray(parsed.targets) || !Array.isArray(parsed.skills)) {
|
|
150
|
+
return getDefaultConfig();
|
|
151
|
+
}
|
|
152
|
+
return parsed;
|
|
153
|
+
} catch {
|
|
154
|
+
return getDefaultConfig();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
async function writeConfig(projectRoot, config) {
|
|
158
|
+
const configPath = join2(projectRoot, CONFIG_FILENAME);
|
|
159
|
+
const sorted = {
|
|
160
|
+
...config,
|
|
161
|
+
skills: [...config.skills].sort(compareByNameThenPath)
|
|
162
|
+
};
|
|
163
|
+
const content = JSON.stringify(sorted, null, 2);
|
|
164
|
+
await writeFile(configPath, content, "utf-8");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// src/lib/writer.ts
|
|
168
|
+
import { stat, writeFile as writeFile2 } from "fs/promises";
|
|
169
|
+
import { basename, join as join3 } from "path";
|
|
170
|
+
|
|
171
|
+
// src/lib/indexer.ts
|
|
172
|
+
import { dirname } from "path";
|
|
173
|
+
function groupFilesBySubdir(files) {
|
|
174
|
+
const groups = /* @__PURE__ */ new Map();
|
|
175
|
+
for (const file of files) {
|
|
176
|
+
const dir = dirname(file.relativePath);
|
|
177
|
+
const key = dir === "." ? "" : dir;
|
|
178
|
+
const existing = groups.get(key);
|
|
179
|
+
if (existing) {
|
|
180
|
+
existing.push(`${file.name}.md`);
|
|
181
|
+
} else {
|
|
182
|
+
groups.set(key, [`${file.name}.md`]);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return groups;
|
|
186
|
+
}
|
|
187
|
+
function generateIndex(skills) {
|
|
188
|
+
const segments = [INDEX_HEADER, INDEX_INSTRUCTION];
|
|
189
|
+
for (const skill of skills) {
|
|
190
|
+
segments.push(`[${skill.name}]`);
|
|
191
|
+
segments.push(`root:./${skill.relativePath}`);
|
|
192
|
+
if (skill.description) {
|
|
193
|
+
segments.push(`desc:${skill.description}`);
|
|
194
|
+
}
|
|
195
|
+
const groups = groupFilesBySubdir(skill.files);
|
|
196
|
+
for (const [subdir, fileNames] of groups) {
|
|
197
|
+
const fileList = `{${fileNames.join(",")}}`;
|
|
198
|
+
if (subdir) {
|
|
199
|
+
segments.push(`${subdir}:${fileList}`);
|
|
200
|
+
} else {
|
|
201
|
+
segments.push(fileList);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return segments.join("|");
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
// src/lib/writer.ts
|
|
209
|
+
async function getTargetInfos(projectRoot, targets) {
|
|
210
|
+
return Promise.all(
|
|
211
|
+
targets.map(async (t) => {
|
|
212
|
+
const p = join3(projectRoot, t);
|
|
213
|
+
const s = await stat(p);
|
|
214
|
+
return { file: basename(t), path: p, totalSize: s.size };
|
|
215
|
+
})
|
|
216
|
+
);
|
|
217
|
+
}
|
|
218
|
+
function buildManagedSection(indexContent) {
|
|
219
|
+
return `${START_TAG}
|
|
220
|
+
${indexContent}
|
|
221
|
+
${END_TAG}`;
|
|
222
|
+
}
|
|
223
|
+
async function writeTargetFile(projectRoot, indexContent, targetFile = TARGET_FILE) {
|
|
224
|
+
const targetPath = join3(projectRoot, targetFile);
|
|
225
|
+
const section = buildManagedSection(indexContent);
|
|
226
|
+
const existing = await safeReadFile(targetPath);
|
|
227
|
+
let output;
|
|
228
|
+
if (existing === void 0) {
|
|
229
|
+
output = `${section}
|
|
230
|
+
`;
|
|
231
|
+
} else if (existing.includes(START_TAG) && existing.includes(END_TAG)) {
|
|
232
|
+
const startIdx = existing.indexOf(START_TAG);
|
|
233
|
+
const endIdx = existing.indexOf(END_TAG) + END_TAG.length;
|
|
234
|
+
output = existing.slice(0, startIdx) + section + existing.slice(endIdx);
|
|
235
|
+
} else {
|
|
236
|
+
output = `${existing.trimEnd()}
|
|
237
|
+
|
|
238
|
+
${section}
|
|
239
|
+
`;
|
|
240
|
+
}
|
|
241
|
+
await writeFile2(targetPath, output, "utf-8");
|
|
242
|
+
return output;
|
|
243
|
+
}
|
|
244
|
+
async function regenerateFromConfig(projectRoot) {
|
|
245
|
+
const config = await readConfig(projectRoot);
|
|
246
|
+
const allSkills = await scanForSkills(projectRoot);
|
|
247
|
+
const skillMap = new Map(allSkills.map((s) => [s.relativePath, s]));
|
|
248
|
+
const skills = config.skills.map((entry) => skillMap.get(entry.path)).filter((s) => s !== void 0);
|
|
249
|
+
const index = generateIndex(skills);
|
|
250
|
+
const writtenContent = /* @__PURE__ */ new Map();
|
|
251
|
+
for (const target of config.targets) {
|
|
252
|
+
const content = await writeTargetFile(projectRoot, index, target);
|
|
253
|
+
writtenContent.set(join3(projectRoot, target), content);
|
|
254
|
+
}
|
|
255
|
+
const managedSize = Buffer.byteLength(buildManagedSection(index));
|
|
256
|
+
const targets = await getTargetInfos(projectRoot, config.targets);
|
|
257
|
+
return {
|
|
258
|
+
skillCount: skills.length,
|
|
259
|
+
managedSize,
|
|
260
|
+
targets,
|
|
261
|
+
writtenContent
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// src/lib/add.ts
|
|
266
|
+
async function addSkill(projectRoot, skillName) {
|
|
267
|
+
const config = await readConfig(projectRoot);
|
|
268
|
+
const allSkills = await scanForSkills(projectRoot);
|
|
269
|
+
const isPath = skillName.includes("/");
|
|
270
|
+
let targetSkill;
|
|
271
|
+
if (isPath) {
|
|
272
|
+
targetSkill = allSkills.find((s) => s.relativePath === skillName);
|
|
273
|
+
if (!targetSkill) {
|
|
274
|
+
throw new Error(`Skill "${skillName}" not found. Did you create the skill directory?`);
|
|
275
|
+
}
|
|
276
|
+
} else {
|
|
277
|
+
const matches = allSkills.filter((s) => s.name === skillName);
|
|
278
|
+
if (matches.length === 0) {
|
|
279
|
+
throw new Error(`Skill "${skillName}" not found. Did you create the skill directory?`);
|
|
280
|
+
}
|
|
281
|
+
if (matches.length > 1) {
|
|
282
|
+
const paths = matches.map((s) => ` ${s.relativePath}`).join("\n");
|
|
283
|
+
throw new Error(`Multiple skills named "${skillName}" found. Specify the path:
|
|
284
|
+
${paths}`);
|
|
285
|
+
}
|
|
286
|
+
targetSkill = matches[0];
|
|
287
|
+
}
|
|
288
|
+
if (config.skills.some((s) => s.path === targetSkill.relativePath)) {
|
|
289
|
+
throw new Error(`Skill "${targetSkill.relativePath}" is already indexed`);
|
|
290
|
+
}
|
|
291
|
+
config.skills.push({
|
|
292
|
+
name: targetSkill.name,
|
|
293
|
+
path: targetSkill.relativePath
|
|
294
|
+
});
|
|
295
|
+
await writeConfig(projectRoot, config);
|
|
296
|
+
const result = await regenerateFromConfig(projectRoot);
|
|
297
|
+
return {
|
|
298
|
+
skillName: targetSkill.name,
|
|
299
|
+
managedSize: result.managedSize,
|
|
300
|
+
targets: result.targets
|
|
301
|
+
};
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
// src/lib/init.ts
|
|
305
|
+
async function init(options) {
|
|
306
|
+
const { projectRoot, selectedSkills } = options;
|
|
307
|
+
const discovered = await scanForSkills(projectRoot);
|
|
308
|
+
let skills;
|
|
309
|
+
if (selectedSkills) {
|
|
310
|
+
const selected = new Set(selectedSkills);
|
|
311
|
+
skills = discovered.filter((s) => selected.has(s.name) || selected.has(s.relativePath));
|
|
312
|
+
} else {
|
|
313
|
+
skills = discovered;
|
|
314
|
+
}
|
|
315
|
+
return initWithSkills(projectRoot, skills);
|
|
316
|
+
}
|
|
317
|
+
async function initWithSkills(projectRoot, skills, targets = [TARGET_FILE]) {
|
|
318
|
+
const config = {
|
|
319
|
+
version: 1,
|
|
320
|
+
targets,
|
|
321
|
+
skills: skills.map((skill) => ({
|
|
322
|
+
name: skill.name,
|
|
323
|
+
path: skill.relativePath
|
|
324
|
+
}))
|
|
325
|
+
};
|
|
326
|
+
await writeConfig(projectRoot, config);
|
|
327
|
+
return regenerateFromConfig(projectRoot);
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
// src/lib/list.ts
|
|
331
|
+
async function listSkills(projectRoot) {
|
|
332
|
+
const config = await readConfig(projectRoot);
|
|
333
|
+
const allSkills = await scanForSkills(projectRoot);
|
|
334
|
+
const indexedPaths = new Set(config.skills.map((s) => s.path));
|
|
335
|
+
const skillMap = new Map(allSkills.map((s) => [s.relativePath, s]));
|
|
336
|
+
const indexed = config.skills.map((entry) => {
|
|
337
|
+
const discovered = skillMap.get(entry.path);
|
|
338
|
+
if (!discovered) return void 0;
|
|
339
|
+
return {
|
|
340
|
+
name: entry.name,
|
|
341
|
+
path: entry.path,
|
|
342
|
+
description: discovered.description
|
|
343
|
+
};
|
|
344
|
+
}).filter((s) => s !== void 0);
|
|
345
|
+
const available = allSkills.filter((s) => !indexedPaths.has(s.relativePath)).map((s) => ({
|
|
346
|
+
name: s.name,
|
|
347
|
+
description: s.description,
|
|
348
|
+
path: s.relativePath
|
|
349
|
+
}));
|
|
350
|
+
return { indexed, available };
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
// src/lib/remove.ts
|
|
354
|
+
import { rm } from "fs/promises";
|
|
355
|
+
import { join as join4 } from "path";
|
|
356
|
+
async function removeSkill(projectRoot, skillName, deleteFiles = false) {
|
|
357
|
+
const config = await readConfig(projectRoot);
|
|
358
|
+
const isPath = skillName.includes("/");
|
|
359
|
+
let skillIndex;
|
|
360
|
+
if (isPath) {
|
|
361
|
+
skillIndex = config.skills.findIndex((s) => s.path === skillName);
|
|
362
|
+
if (skillIndex === -1) {
|
|
363
|
+
throw new Error(`Skill "${skillName}" is not indexed`);
|
|
364
|
+
}
|
|
365
|
+
} else {
|
|
366
|
+
const matches = config.skills.map((s, i) => ({ entry: s, index: i })).filter(({ entry: entry2 }) => entry2.name === skillName);
|
|
367
|
+
if (matches.length === 0) {
|
|
368
|
+
throw new Error(`Skill "${skillName}" is not indexed`);
|
|
369
|
+
}
|
|
370
|
+
if (matches.length > 1) {
|
|
371
|
+
const paths = matches.map(({ entry: entry2 }) => ` ${entry2.path}`).join("\n");
|
|
372
|
+
throw new Error(`Multiple skills named "${skillName}" indexed. Specify the path:
|
|
373
|
+
${paths}`);
|
|
374
|
+
}
|
|
375
|
+
skillIndex = matches[0].index;
|
|
376
|
+
}
|
|
377
|
+
const entry = config.skills[skillIndex];
|
|
378
|
+
const skillPath = join4(projectRoot, entry.path);
|
|
379
|
+
config.skills.splice(skillIndex, 1);
|
|
380
|
+
await writeConfig(projectRoot, config);
|
|
381
|
+
if (deleteFiles) {
|
|
382
|
+
await rm(skillPath, { recursive: true, force: true });
|
|
383
|
+
}
|
|
384
|
+
const result = await regenerateFromConfig(projectRoot);
|
|
385
|
+
return {
|
|
386
|
+
skillName: entry.name,
|
|
387
|
+
wasDeleted: deleteFiles,
|
|
388
|
+
managedSize: result.managedSize,
|
|
389
|
+
targets: result.targets
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
// src/lib/sync.ts
|
|
394
|
+
import { join as join5 } from "path";
|
|
395
|
+
async function syncSkills(projectRoot) {
|
|
396
|
+
const config = await readConfig(projectRoot);
|
|
397
|
+
const onDisk = await scanForSkills(projectRoot);
|
|
398
|
+
const diskPaths = new Set(onDisk.map((s) => s.relativePath));
|
|
399
|
+
const stale = config.skills.filter((s) => !diskPaths.has(s.path));
|
|
400
|
+
const removed = stale.map((s) => s.name);
|
|
401
|
+
if (stale.length > 0) {
|
|
402
|
+
const stalePaths = new Set(stale.map((s) => s.path));
|
|
403
|
+
config.skills = config.skills.filter((s) => !stalePaths.has(s.path));
|
|
404
|
+
await writeConfig(projectRoot, config);
|
|
405
|
+
}
|
|
406
|
+
const targetPaths = config.targets.map((t) => join5(projectRoot, t));
|
|
407
|
+
const beforeMap = /* @__PURE__ */ new Map();
|
|
408
|
+
for (const targetPath of targetPaths) {
|
|
409
|
+
beforeMap.set(targetPath, await safeReadFile(targetPath));
|
|
410
|
+
}
|
|
411
|
+
if (config.skills.length === 0 && [...beforeMap.values()].every((v) => v === void 0)) {
|
|
412
|
+
return { removed, changed: false, managedSize: 0, targets: [] };
|
|
413
|
+
}
|
|
414
|
+
const result = await regenerateFromConfig(projectRoot);
|
|
415
|
+
let changed = false;
|
|
416
|
+
for (const targetPath of targetPaths) {
|
|
417
|
+
const after = result.writtenContent.get(targetPath);
|
|
418
|
+
if (beforeMap.get(targetPath) !== after) {
|
|
419
|
+
changed = true;
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
return {
|
|
424
|
+
removed,
|
|
425
|
+
changed,
|
|
426
|
+
managedSize: result.managedSize,
|
|
427
|
+
targets: result.targets
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
export {
|
|
431
|
+
AGENT_SOURCES,
|
|
432
|
+
CONFIG_FILENAME,
|
|
433
|
+
CONTEXT_BUDGET_DANGER_KB,
|
|
434
|
+
CONTEXT_BUDGET_WARN_KB,
|
|
435
|
+
END_TAG,
|
|
436
|
+
INDEX_HEADER,
|
|
437
|
+
INDEX_INSTRUCTION,
|
|
438
|
+
SKILL_META_FILE,
|
|
439
|
+
START_TAG,
|
|
440
|
+
TARGET_FILE,
|
|
441
|
+
addSkill,
|
|
442
|
+
buildManagedSection,
|
|
443
|
+
compareByNameThenPath,
|
|
444
|
+
generateIndex,
|
|
445
|
+
getAgentDisplayName,
|
|
446
|
+
getUniqueSkillsDirs,
|
|
447
|
+
init,
|
|
448
|
+
initWithSkills,
|
|
449
|
+
listSkills,
|
|
450
|
+
readConfig,
|
|
451
|
+
regenerateFromConfig,
|
|
452
|
+
removeSkill,
|
|
453
|
+
scanForSkills,
|
|
454
|
+
syncSkills,
|
|
455
|
+
writeConfig,
|
|
456
|
+
writeTargetFile
|
|
457
|
+
};
|
|
458
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/config.ts","../src/lib/constants.ts","../src/lib/scanner.ts","../src/lib/agents.ts","../src/lib/writer.ts","../src/lib/indexer.ts","../src/lib/add.ts","../src/lib/init.ts","../src/lib/list.ts","../src/lib/remove.ts","../src/lib/sync.ts"],"sourcesContent":["import { writeFile } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { CONFIG_FILENAME, compareByNameThenPath, TARGET_FILE } from \"./constants.js\";\nimport { safeReadFile } from \"./scanner.js\";\nimport type { Config } from \"./types.js\";\n\nfunction getDefaultConfig(): Config {\n return {\n version: 1,\n targets: [TARGET_FILE],\n skills: [],\n };\n}\n\nexport async function readConfig(projectRoot: string): Promise<Config> {\n const configPath = join(projectRoot, CONFIG_FILENAME);\n const content = await safeReadFile(configPath);\n\n if (content === undefined) {\n return getDefaultConfig();\n }\n\n try {\n const parsed = JSON.parse(content) as Config;\n // Validate structure\n if (\n typeof parsed.version !== \"number\" ||\n !Array.isArray(parsed.targets) ||\n !Array.isArray(parsed.skills)\n ) {\n return getDefaultConfig();\n }\n return parsed;\n } catch {\n return getDefaultConfig();\n }\n}\n\nexport async function writeConfig(projectRoot: string, config: Config): Promise<void> {\n const configPath = join(projectRoot, CONFIG_FILENAME);\n const sorted = {\n ...config,\n skills: [...config.skills].sort(compareByNameThenPath),\n };\n const content = JSON.stringify(sorted, null, 2);\n await writeFile(configPath, content, \"utf-8\");\n}\n","export const START_TAG = \"<!-- skilldex:start (auto-generated, do not edit) -->\";\nexport const END_TAG = \"<!-- skilldex:end -->\";\n\nexport const TARGET_FILE = \"AGENTS.md\";\nexport const CONFIG_FILENAME = \"skilldex.config.json\";\nexport const SKILL_META_FILE = \"SKILL.md\";\nexport const SKILLS_DIR_SEGMENTS = [\".agents\", \"skills\"] as const;\nexport const INDEX_HEADER = \"[Skills Index]\";\nexport const INDEX_INSTRUCTION =\n \"IMPORTANT: Prefer retrieval-led reasoning over pre-training-led reasoning for any tasks covered by indexed skills.\";\nexport const CONTEXT_BUDGET_WARN_KB = 20;\nexport const CONTEXT_BUDGET_DANGER_KB = 40;\n\nexport function compareByNameThenPath(\n a: { name: string; path: string },\n b: { name: string; path: string },\n): number {\n return a.name.localeCompare(b.name) || a.path.localeCompare(b.path);\n}\n","import type { Dirent } from \"node:fs\";\nimport { lstat, readdir, readFile } from \"node:fs/promises\";\nimport { join, relative } from \"node:path\";\nimport { getUniqueSkillsDirs } from \"./agents.js\";\nimport { compareByNameThenPath, SKILL_META_FILE } from \"./constants.js\";\nimport type { DiscoveredSkill, SkillFile } from \"./types.js\";\n\n/** Extract key-value pairs from YAML frontmatter (between `---` fences). */\nexport function parseFrontmatter(content: string): Record<string, string> {\n const result: Record<string, string> = {};\n if (!content.startsWith(\"---\")) return result;\n\n const endIndex = content.indexOf(\"\\n---\", 3);\n if (endIndex === -1) return result;\n\n const block = content.slice(4, endIndex);\n for (const line of block.split(\"\\n\")) {\n const colonIndex = line.indexOf(\":\");\n if (colonIndex === -1) continue;\n const key = line.slice(0, colonIndex).trim();\n const value = line.slice(colonIndex + 1).trim();\n if (key) result[key] = value;\n }\n return result;\n}\n\nasync function safeReaddir(dir: string): Promise<Dirent[]> {\n try {\n return await readdir(dir, { withFileTypes: true });\n } catch {\n return [];\n }\n}\n\nexport async function safeReadFile(path: string): Promise<string | undefined> {\n try {\n return await readFile(path, \"utf-8\");\n } catch {\n return undefined;\n }\n}\n\nasync function collectMdFiles(dir: string, skillRoot: string): Promise<SkillFile[]> {\n const entries = await safeReaddir(dir);\n const files: SkillFile[] = [];\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n if (entry.isDirectory()) {\n const nested = await collectMdFiles(fullPath, skillRoot);\n files.push(...nested);\n } else if (entry.isFile() && entry.name.endsWith(\".md\") && entry.name !== SKILL_META_FILE) {\n files.push({\n relativePath: relative(skillRoot, fullPath),\n name: entry.name.replace(/\\.md$/, \"\"),\n });\n }\n }\n return files;\n}\n\nasync function isSymlink(path: string): Promise<boolean> {\n try {\n const stats = await lstat(path);\n return stats.isSymbolicLink();\n } catch {\n return false;\n }\n}\n\n/** Scan a single skills directory and return discovered skills. */\nasync function scanDirectory(dir: string, projectRoot: string): Promise<DiscoveredSkill[]> {\n const entries = await safeReaddir(dir);\n const skills: DiscoveredSkill[] = [];\n\n for (const entry of entries) {\n if (!entry.isDirectory()) continue;\n\n const skillPath = join(dir, entry.name);\n\n if (await isSymlink(skillPath)) continue;\n\n let description = \"\";\n const skillMd = await safeReadFile(join(skillPath, SKILL_META_FILE));\n if (skillMd !== undefined) {\n const frontmatter = parseFrontmatter(skillMd);\n description = frontmatter.description ?? \"\";\n }\n\n const files = await collectMdFiles(skillPath, skillPath);\n skills.push({\n name: entry.name,\n description,\n path: skillPath,\n relativePath: relative(projectRoot, skillPath),\n files,\n });\n }\n return skills;\n}\n\n/** Scan all agent source directories for skills. Same-named skills in different directories are all included. */\nexport async function scanForSkills(projectRoot: string): Promise<DiscoveredSkill[]> {\n const results = await Promise.all(\n getUniqueSkillsDirs().map((d) => scanDirectory(join(projectRoot, d), projectRoot)),\n );\n return results.flat().sort(compareByNameThenPath);\n}\n","export interface AgentSource {\n /** Agent identifier, e.g. \"claude-code\" */\n name: string;\n /** Human-readable name, e.g. \"Claude Code\" */\n displayName: string;\n /** Skills directory relative to project root, e.g. \".claude/skills\" */\n skillsDir: string;\n}\n\n/**\n * Known agent source directories (project-scoped).\n * Sourced from the Vercel Skills CLI agent conventions.\n */\nexport const AGENT_SOURCES: AgentSource[] = [\n { name: \"universal\", displayName: \"Universal\", skillsDir: \".agents/skills\" },\n { name: \"antigravity\", displayName: \"Antigravity\", skillsDir: \".agent/skills\" },\n { name: \"claude-code\", displayName: \"Claude Code\", skillsDir: \".claude/skills\" },\n { name: \"codex\", displayName: \"Codex\", skillsDir: \".agents/skills\" },\n { name: \"cursor\", displayName: \"Cursor\", skillsDir: \".cursor/skills\" },\n { name: \"github-copilot\", displayName: \"GitHub Copilot\", skillsDir: \".agents/skills\" },\n { name: \"opencode\", displayName: \"OpenCode\", skillsDir: \".agents/skills\" },\n { name: \"openclaw\", displayName: \"OpenClaw\", skillsDir: \"skills\" },\n { name: \"windsurf\", displayName: \"Windsurf\", skillsDir: \".windsurf/skills\" },\n];\n\n/** Unique skills directories to scan (deduplicates agents sharing the same dir). */\nexport function getUniqueSkillsDirs(): string[] {\n return [...new Set(AGENT_SOURCES.map((a) => a.skillsDir))];\n}\n\n/** Maps a skill's relative path to the first matching agent's display name. */\nexport function getAgentDisplayName(relativePath: string): string | undefined {\n for (const agent of AGENT_SOURCES) {\n if (relativePath.startsWith(`${agent.skillsDir}/`) || relativePath === agent.skillsDir) {\n return agent.displayName;\n }\n }\n return undefined;\n}\n","import { stat, writeFile } from \"node:fs/promises\";\nimport { basename, join } from \"node:path\";\nimport { readConfig } from \"./config.js\";\nimport { END_TAG, START_TAG, TARGET_FILE } from \"./constants.js\";\nimport { generateIndex } from \"./indexer.js\";\nimport { safeReadFile, scanForSkills } from \"./scanner.js\";\nimport type { InitResult, TargetFileInfo } from \"./types.js\";\n\nexport async function getTargetInfos(\n projectRoot: string,\n targets: string[],\n): Promise<TargetFileInfo[]> {\n return Promise.all(\n targets.map(async (t) => {\n const p = join(projectRoot, t);\n const s = await stat(p);\n return { file: basename(t), path: p, totalSize: s.size };\n }),\n );\n}\n\nexport function buildManagedSection(indexContent: string): string {\n return `${START_TAG}\\n${indexContent}\\n${END_TAG}`;\n}\n\n/** Write or update the managed skilldex section in a target file. Creates, appends, or replaces as needed. Returns the written file content. */\nexport async function writeTargetFile(\n projectRoot: string,\n indexContent: string,\n targetFile: string = TARGET_FILE,\n): Promise<string> {\n const targetPath = join(projectRoot, targetFile);\n const section = buildManagedSection(indexContent);\n\n const existing = await safeReadFile(targetPath);\n\n let output: string;\n if (existing === undefined) {\n output = `${section}\\n`;\n } else if (existing.includes(START_TAG) && existing.includes(END_TAG)) {\n const startIdx = existing.indexOf(START_TAG);\n const endIdx = existing.indexOf(END_TAG) + END_TAG.length;\n output = existing.slice(0, startIdx) + section + existing.slice(endIdx);\n } else {\n output = `${existing.trimEnd()}\\n\\n${section}\\n`;\n }\n\n await writeFile(targetPath, output, \"utf-8\");\n return output;\n}\n\nexport interface RegenerateResult extends InitResult {\n /** Map of target file path → written content */\n writtenContent: Map<string, string>;\n}\n\n/** Regenerate all target files from config (reads config, scans indexed skills, writes targets). */\nexport async function regenerateFromConfig(projectRoot: string): Promise<RegenerateResult> {\n const config = await readConfig(projectRoot);\n const allSkills = await scanForSkills(projectRoot);\n\n // Map skill paths from config to DiscoveredSkill objects\n const skillMap = new Map(allSkills.map((s) => [s.relativePath, s]));\n const skills = config.skills\n .map((entry) => skillMap.get(entry.path))\n .filter((s) => s !== undefined);\n\n const index = generateIndex(skills);\n\n const writtenContent = new Map<string, string>();\n for (const target of config.targets) {\n const content = await writeTargetFile(projectRoot, index, target);\n writtenContent.set(join(projectRoot, target), content);\n }\n\n const managedSize = Buffer.byteLength(buildManagedSection(index));\n const targets = await getTargetInfos(projectRoot, config.targets);\n\n return {\n skillCount: skills.length,\n managedSize,\n targets,\n writtenContent,\n };\n}\n","import { dirname } from \"node:path\";\nimport { INDEX_HEADER, INDEX_INSTRUCTION } from \"./constants.js\";\nimport type { DiscoveredSkill } from \"./types.js\";\n\nfunction groupFilesBySubdir(\n files: { relativePath: string; name: string }[],\n): Map<string, string[]> {\n const groups = new Map<string, string[]>();\n for (const file of files) {\n const dir = dirname(file.relativePath);\n const key = dir === \".\" ? \"\" : dir;\n const existing = groups.get(key);\n if (existing) {\n existing.push(`${file.name}.md`);\n } else {\n groups.set(key, [`${file.name}.md`]);\n }\n }\n return groups;\n}\n\nexport function generateIndex(skills: DiscoveredSkill[]): string {\n const segments: string[] = [INDEX_HEADER, INDEX_INSTRUCTION];\n\n for (const skill of skills) {\n segments.push(`[${skill.name}]`);\n segments.push(`root:./${skill.relativePath}`);\n\n if (skill.description) {\n segments.push(`desc:${skill.description}`);\n }\n\n const groups = groupFilesBySubdir(skill.files);\n for (const [subdir, fileNames] of groups) {\n const fileList = `{${fileNames.join(\",\")}}`;\n if (subdir) {\n segments.push(`${subdir}:${fileList}`);\n } else {\n segments.push(fileList);\n }\n }\n }\n\n return segments.join(\"|\");\n}\n","import { readConfig, writeConfig } from \"./config.js\";\nimport { scanForSkills } from \"./scanner.js\";\nimport type { AddResult, DiscoveredSkill } from \"./types.js\";\nimport { regenerateFromConfig } from \"./writer.js\";\n\nexport async function addSkill(projectRoot: string, skillName: string): Promise<AddResult> {\n const config = await readConfig(projectRoot);\n const allSkills = await scanForSkills(projectRoot);\n\n const isPath = skillName.includes(\"/\");\n\n let targetSkill: DiscoveredSkill | undefined;\n\n if (isPath) {\n // Path-based lookup for disambiguation\n targetSkill = allSkills.find((s) => s.relativePath === skillName);\n if (!targetSkill) {\n throw new Error(`Skill \"${skillName}\" not found. Did you create the skill directory?`);\n }\n } else {\n // Name-based lookup\n const matches = allSkills.filter((s) => s.name === skillName);\n if (matches.length === 0) {\n throw new Error(`Skill \"${skillName}\" not found. Did you create the skill directory?`);\n }\n if (matches.length > 1) {\n const paths = matches.map((s) => ` ${s.relativePath}`).join(\"\\n\");\n throw new Error(`Multiple skills named \"${skillName}\" found. Specify the path:\\n${paths}`);\n }\n targetSkill = matches[0];\n }\n\n if (config.skills.some((s) => s.path === targetSkill.relativePath)) {\n throw new Error(`Skill \"${targetSkill.relativePath}\" is already indexed`);\n }\n\n config.skills.push({\n name: targetSkill.name,\n path: targetSkill.relativePath,\n });\n\n await writeConfig(projectRoot, config);\n\n const result = await regenerateFromConfig(projectRoot);\n\n return {\n skillName: targetSkill.name,\n managedSize: result.managedSize,\n targets: result.targets,\n };\n}\n","import { writeConfig } from \"./config.js\";\nimport { TARGET_FILE } from \"./constants.js\";\nimport { scanForSkills } from \"./scanner.js\";\nimport type { DiscoveredSkill, InitResult } from \"./types.js\";\nimport { regenerateFromConfig } from \"./writer.js\";\n\n/** Scan for skills, filter by selection, and write the index to target files. */\nexport async function init(options: {\n projectRoot: string;\n selectedSkills?: string[];\n yes?: boolean;\n}): Promise<InitResult> {\n const { projectRoot, selectedSkills } = options;\n\n const discovered = await scanForSkills(projectRoot);\n\n let skills: DiscoveredSkill[];\n if (selectedSkills) {\n const selected = new Set(selectedSkills);\n skills = discovered.filter((s) => selected.has(s.name) || selected.has(s.relativePath));\n } else {\n skills = discovered;\n }\n\n return initWithSkills(projectRoot, skills);\n}\n\n/** Index a specific set of skills and write to target file(s). */\nexport async function initWithSkills(\n projectRoot: string,\n skills: DiscoveredSkill[],\n targets: string[] = [TARGET_FILE],\n): Promise<InitResult> {\n const config = {\n version: 1 as const,\n targets,\n skills: skills.map((skill) => ({\n name: skill.name,\n path: skill.relativePath,\n })),\n };\n await writeConfig(projectRoot, config);\n\n return regenerateFromConfig(projectRoot);\n}\n","import { readConfig } from \"./config.js\";\nimport { scanForSkills } from \"./scanner.js\";\nimport type { ListResult, SkillInfo } from \"./types.js\";\n\nexport async function listSkills(projectRoot: string): Promise<ListResult> {\n const config = await readConfig(projectRoot);\n const allSkills = await scanForSkills(projectRoot);\n\n const indexedPaths = new Set(config.skills.map((s) => s.path));\n const skillMap = new Map(allSkills.map((s) => [s.relativePath, s]));\n\n const indexed: SkillInfo[] = config.skills\n .map((entry) => {\n const discovered = skillMap.get(entry.path);\n if (!discovered) return undefined;\n return {\n name: entry.name,\n path: entry.path,\n description: discovered.description,\n };\n })\n .filter((s) => s !== undefined);\n\n const available: SkillInfo[] = allSkills\n .filter((s) => !indexedPaths.has(s.relativePath))\n .map((s) => ({\n name: s.name,\n description: s.description,\n path: s.relativePath,\n }));\n\n return { indexed, available };\n}\n","import { rm } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { readConfig, writeConfig } from \"./config.js\";\nimport type { RemoveResult } from \"./types.js\";\nimport { regenerateFromConfig } from \"./writer.js\";\n\nexport async function removeSkill(\n projectRoot: string,\n skillName: string,\n deleteFiles = false,\n): Promise<RemoveResult> {\n const config = await readConfig(projectRoot);\n\n const isPath = skillName.includes(\"/\");\n\n let skillIndex: number;\n\n if (isPath) {\n skillIndex = config.skills.findIndex((s) => s.path === skillName);\n if (skillIndex === -1) {\n throw new Error(`Skill \"${skillName}\" is not indexed`);\n }\n } else {\n const matches = config.skills\n .map((s, i) => ({ entry: s, index: i }))\n .filter(({ entry }) => entry.name === skillName);\n\n if (matches.length === 0) {\n throw new Error(`Skill \"${skillName}\" is not indexed`);\n }\n if (matches.length > 1) {\n const paths = matches.map(({ entry }) => ` ${entry.path}`).join(\"\\n\");\n throw new Error(`Multiple skills named \"${skillName}\" indexed. Specify the path:\\n${paths}`);\n }\n skillIndex = matches[0].index;\n }\n\n const entry = config.skills[skillIndex];\n const skillPath = join(projectRoot, entry.path);\n\n config.skills.splice(skillIndex, 1);\n await writeConfig(projectRoot, config);\n\n if (deleteFiles) {\n await rm(skillPath, { recursive: true, force: true });\n }\n\n const result = await regenerateFromConfig(projectRoot);\n\n return {\n skillName: entry.name,\n wasDeleted: deleteFiles,\n managedSize: result.managedSize,\n targets: result.targets,\n };\n}\n","import { join } from \"node:path\";\nimport { readConfig, writeConfig } from \"./config.js\";\nimport { safeReadFile, scanForSkills } from \"./scanner.js\";\nimport type { SyncResult } from \"./types.js\";\nimport { regenerateFromConfig } from \"./writer.js\";\n\nexport async function syncSkills(projectRoot: string): Promise<SyncResult> {\n const config = await readConfig(projectRoot);\n const onDisk = await scanForSkills(projectRoot);\n const diskPaths = new Set(onDisk.map((s) => s.relativePath));\n\n // Find stale entries: in config but not on disk\n const stale = config.skills.filter((s) => !diskPaths.has(s.path));\n const removed = stale.map((s) => s.name);\n\n if (stale.length > 0) {\n const stalePaths = new Set(stale.map((s) => s.path));\n config.skills = config.skills.filter((s) => !stalePaths.has(s.path));\n await writeConfig(projectRoot, config);\n }\n\n const targetPaths = config.targets.map((t) => join(projectRoot, t));\n\n // Read before-state for all targets\n const beforeMap = new Map<string, string | undefined>();\n for (const targetPath of targetPaths) {\n beforeMap.set(targetPath, await safeReadFile(targetPath));\n }\n\n // Nothing to do if no skills indexed and no target files exist\n if (config.skills.length === 0 && [...beforeMap.values()].every((v) => v === undefined)) {\n return { removed, changed: false, managedSize: 0, targets: [] };\n }\n\n const result = await regenerateFromConfig(projectRoot);\n\n // Check if ANY target changed using the written content from regeneration\n let changed = false;\n for (const targetPath of targetPaths) {\n const after = result.writtenContent.get(targetPath);\n if (beforeMap.get(targetPath) !== after) {\n changed = true;\n break;\n }\n }\n\n return {\n removed,\n changed,\n managedSize: result.managedSize,\n targets: result.targets,\n };\n}\n"],"mappings":";AAAA,SAAS,iBAAiB;AAC1B,SAAS,QAAAA,aAAY;;;ACDd,IAAM,YAAY;AAClB,IAAM,UAAU;AAEhB,IAAM,cAAc;AACpB,IAAM,kBAAkB;AACxB,IAAM,kBAAkB;AAExB,IAAM,eAAe;AACrB,IAAM,oBACX;AACK,IAAM,yBAAyB;AAC/B,IAAM,2BAA2B;AAEjC,SAAS,sBACd,GACA,GACQ;AACR,SAAO,EAAE,KAAK,cAAc,EAAE,IAAI,KAAK,EAAE,KAAK,cAAc,EAAE,IAAI;AACpE;;;ACjBA,SAAS,OAAO,SAAS,gBAAgB;AACzC,SAAS,MAAM,gBAAgB;;;ACWxB,IAAM,gBAA+B;AAAA,EAC1C,EAAE,MAAM,aAAa,aAAa,aAAa,WAAW,iBAAiB;AAAA,EAC3E,EAAE,MAAM,eAAe,aAAa,eAAe,WAAW,gBAAgB;AAAA,EAC9E,EAAE,MAAM,eAAe,aAAa,eAAe,WAAW,iBAAiB;AAAA,EAC/E,EAAE,MAAM,SAAS,aAAa,SAAS,WAAW,iBAAiB;AAAA,EACnE,EAAE,MAAM,UAAU,aAAa,UAAU,WAAW,iBAAiB;AAAA,EACrE,EAAE,MAAM,kBAAkB,aAAa,kBAAkB,WAAW,iBAAiB;AAAA,EACrF,EAAE,MAAM,YAAY,aAAa,YAAY,WAAW,iBAAiB;AAAA,EACzE,EAAE,MAAM,YAAY,aAAa,YAAY,WAAW,SAAS;AAAA,EACjE,EAAE,MAAM,YAAY,aAAa,YAAY,WAAW,mBAAmB;AAC7E;AAGO,SAAS,sBAAgC;AAC9C,SAAO,CAAC,GAAG,IAAI,IAAI,cAAc,IAAI,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;AAC3D;AAGO,SAAS,oBAAoB,cAA0C;AAC5E,aAAW,SAAS,eAAe;AACjC,QAAI,aAAa,WAAW,GAAG,MAAM,SAAS,GAAG,KAAK,iBAAiB,MAAM,WAAW;AACtF,aAAO,MAAM;AAAA,IACf;AAAA,EACF;AACA,SAAO;AACT;;;AD9BO,SAAS,iBAAiB,SAAyC;AACxE,QAAM,SAAiC,CAAC;AACxC,MAAI,CAAC,QAAQ,WAAW,KAAK,EAAG,QAAO;AAEvC,QAAM,WAAW,QAAQ,QAAQ,SAAS,CAAC;AAC3C,MAAI,aAAa,GAAI,QAAO;AAE5B,QAAM,QAAQ,QAAQ,MAAM,GAAG,QAAQ;AACvC,aAAW,QAAQ,MAAM,MAAM,IAAI,GAAG;AACpC,UAAM,aAAa,KAAK,QAAQ,GAAG;AACnC,QAAI,eAAe,GAAI;AACvB,UAAM,MAAM,KAAK,MAAM,GAAG,UAAU,EAAE,KAAK;AAC3C,UAAM,QAAQ,KAAK,MAAM,aAAa,CAAC,EAAE,KAAK;AAC9C,QAAI,IAAK,QAAO,GAAG,IAAI;AAAA,EACzB;AACA,SAAO;AACT;AAEA,eAAe,YAAY,KAAgC;AACzD,MAAI;AACF,WAAO,MAAM,QAAQ,KAAK,EAAE,eAAe,KAAK,CAAC;AAAA,EACnD,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,eAAsB,aAAa,MAA2C;AAC5E,MAAI;AACF,WAAO,MAAM,SAAS,MAAM,OAAO;AAAA,EACrC,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAEA,eAAe,eAAe,KAAa,WAAyC;AAClF,QAAM,UAAU,MAAM,YAAY,GAAG;AACrC,QAAM,QAAqB,CAAC;AAE5B,aAAW,SAAS,SAAS;AAC3B,UAAM,WAAW,KAAK,KAAK,MAAM,IAAI;AACrC,QAAI,MAAM,YAAY,GAAG;AACvB,YAAM,SAAS,MAAM,eAAe,UAAU,SAAS;AACvD,YAAM,KAAK,GAAG,MAAM;AAAA,IACtB,WAAW,MAAM,OAAO,KAAK,MAAM,KAAK,SAAS,KAAK,KAAK,MAAM,SAAS,iBAAiB;AACzF,YAAM,KAAK;AAAA,QACT,cAAc,SAAS,WAAW,QAAQ;AAAA,QAC1C,MAAM,MAAM,KAAK,QAAQ,SAAS,EAAE;AAAA,MACtC,CAAC;AAAA,IACH;AAAA,EACF;AACA,SAAO;AACT;AAEA,eAAe,UAAU,MAAgC;AACvD,MAAI;AACF,UAAM,QAAQ,MAAM,MAAM,IAAI;AAC9B,WAAO,MAAM,eAAe;AAAA,EAC9B,QAAQ;AACN,WAAO;AAAA,EACT;AACF;AAGA,eAAe,cAAc,KAAa,aAAiD;AACzF,QAAM,UAAU,MAAM,YAAY,GAAG;AACrC,QAAM,SAA4B,CAAC;AAEnC,aAAW,SAAS,SAAS;AAC3B,QAAI,CAAC,MAAM,YAAY,EAAG;AAE1B,UAAM,YAAY,KAAK,KAAK,MAAM,IAAI;AAEtC,QAAI,MAAM,UAAU,SAAS,EAAG;AAEhC,QAAI,cAAc;AAClB,UAAM,UAAU,MAAM,aAAa,KAAK,WAAW,eAAe,CAAC;AACnE,QAAI,YAAY,QAAW;AACzB,YAAM,cAAc,iBAAiB,OAAO;AAC5C,oBAAc,YAAY,eAAe;AAAA,IAC3C;AAEA,UAAM,QAAQ,MAAM,eAAe,WAAW,SAAS;AACvD,WAAO,KAAK;AAAA,MACV,MAAM,MAAM;AAAA,MACZ;AAAA,MACA,MAAM;AAAA,MACN,cAAc,SAAS,aAAa,SAAS;AAAA,MAC7C;AAAA,IACF,CAAC;AAAA,EACH;AACA,SAAO;AACT;AAGA,eAAsB,cAAc,aAAiD;AACnF,QAAM,UAAU,MAAM,QAAQ;AAAA,IAC5B,oBAAoB,EAAE,IAAI,CAAC,MAAM,cAAc,KAAK,aAAa,CAAC,GAAG,WAAW,CAAC;AAAA,EACnF;AACA,SAAO,QAAQ,KAAK,EAAE,KAAK,qBAAqB;AAClD;;;AFrGA,SAAS,mBAA2B;AAClC,SAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS,CAAC,WAAW;AAAA,IACrB,QAAQ,CAAC;AAAA,EACX;AACF;AAEA,eAAsB,WAAW,aAAsC;AACrE,QAAM,aAAaC,MAAK,aAAa,eAAe;AACpD,QAAM,UAAU,MAAM,aAAa,UAAU;AAE7C,MAAI,YAAY,QAAW;AACzB,WAAO,iBAAiB;AAAA,EAC1B;AAEA,MAAI;AACF,UAAM,SAAS,KAAK,MAAM,OAAO;AAEjC,QACE,OAAO,OAAO,YAAY,YAC1B,CAAC,MAAM,QAAQ,OAAO,OAAO,KAC7B,CAAC,MAAM,QAAQ,OAAO,MAAM,GAC5B;AACA,aAAO,iBAAiB;AAAA,IAC1B;AACA,WAAO;AAAA,EACT,QAAQ;AACN,WAAO,iBAAiB;AAAA,EAC1B;AACF;AAEA,eAAsB,YAAY,aAAqB,QAA+B;AACpF,QAAM,aAAaA,MAAK,aAAa,eAAe;AACpD,QAAM,SAAS;AAAA,IACb,GAAG;AAAA,IACH,QAAQ,CAAC,GAAG,OAAO,MAAM,EAAE,KAAK,qBAAqB;AAAA,EACvD;AACA,QAAM,UAAU,KAAK,UAAU,QAAQ,MAAM,CAAC;AAC9C,QAAM,UAAU,YAAY,SAAS,OAAO;AAC9C;;;AI9CA,SAAS,MAAM,aAAAC,kBAAiB;AAChC,SAAS,UAAU,QAAAC,aAAY;;;ACD/B,SAAS,eAAe;AAIxB,SAAS,mBACP,OACuB;AACvB,QAAM,SAAS,oBAAI,IAAsB;AACzC,aAAW,QAAQ,OAAO;AACxB,UAAM,MAAM,QAAQ,KAAK,YAAY;AACrC,UAAM,MAAM,QAAQ,MAAM,KAAK;AAC/B,UAAM,WAAW,OAAO,IAAI,GAAG;AAC/B,QAAI,UAAU;AACZ,eAAS,KAAK,GAAG,KAAK,IAAI,KAAK;AAAA,IACjC,OAAO;AACL,aAAO,IAAI,KAAK,CAAC,GAAG,KAAK,IAAI,KAAK,CAAC;AAAA,IACrC;AAAA,EACF;AACA,SAAO;AACT;AAEO,SAAS,cAAc,QAAmC;AAC/D,QAAM,WAAqB,CAAC,cAAc,iBAAiB;AAE3D,aAAW,SAAS,QAAQ;AAC1B,aAAS,KAAK,IAAI,MAAM,IAAI,GAAG;AAC/B,aAAS,KAAK,UAAU,MAAM,YAAY,EAAE;AAE5C,QAAI,MAAM,aAAa;AACrB,eAAS,KAAK,QAAQ,MAAM,WAAW,EAAE;AAAA,IAC3C;AAEA,UAAM,SAAS,mBAAmB,MAAM,KAAK;AAC7C,eAAW,CAAC,QAAQ,SAAS,KAAK,QAAQ;AACxC,YAAM,WAAW,IAAI,UAAU,KAAK,GAAG,CAAC;AACxC,UAAI,QAAQ;AACV,iBAAS,KAAK,GAAG,MAAM,IAAI,QAAQ,EAAE;AAAA,MACvC,OAAO;AACL,iBAAS,KAAK,QAAQ;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAEA,SAAO,SAAS,KAAK,GAAG;AAC1B;;;ADpCA,eAAsB,eACpB,aACA,SAC2B;AAC3B,SAAO,QAAQ;AAAA,IACb,QAAQ,IAAI,OAAO,MAAM;AACvB,YAAM,IAAIC,MAAK,aAAa,CAAC;AAC7B,YAAM,IAAI,MAAM,KAAK,CAAC;AACtB,aAAO,EAAE,MAAM,SAAS,CAAC,GAAG,MAAM,GAAG,WAAW,EAAE,KAAK;AAAA,IACzD,CAAC;AAAA,EACH;AACF;AAEO,SAAS,oBAAoB,cAA8B;AAChE,SAAO,GAAG,SAAS;AAAA,EAAK,YAAY;AAAA,EAAK,OAAO;AAClD;AAGA,eAAsB,gBACpB,aACA,cACA,aAAqB,aACJ;AACjB,QAAM,aAAaA,MAAK,aAAa,UAAU;AAC/C,QAAM,UAAU,oBAAoB,YAAY;AAEhD,QAAM,WAAW,MAAM,aAAa,UAAU;AAE9C,MAAI;AACJ,MAAI,aAAa,QAAW;AAC1B,aAAS,GAAG,OAAO;AAAA;AAAA,EACrB,WAAW,SAAS,SAAS,SAAS,KAAK,SAAS,SAAS,OAAO,GAAG;AACrE,UAAM,WAAW,SAAS,QAAQ,SAAS;AAC3C,UAAM,SAAS,SAAS,QAAQ,OAAO,IAAI,QAAQ;AACnD,aAAS,SAAS,MAAM,GAAG,QAAQ,IAAI,UAAU,SAAS,MAAM,MAAM;AAAA,EACxE,OAAO;AACL,aAAS,GAAG,SAAS,QAAQ,CAAC;AAAA;AAAA,EAAO,OAAO;AAAA;AAAA,EAC9C;AAEA,QAAMC,WAAU,YAAY,QAAQ,OAAO;AAC3C,SAAO;AACT;AAQA,eAAsB,qBAAqB,aAAgD;AACzF,QAAM,SAAS,MAAM,WAAW,WAAW;AAC3C,QAAM,YAAY,MAAM,cAAc,WAAW;AAGjD,QAAM,WAAW,IAAI,IAAI,UAAU,IAAI,CAAC,MAAM,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC;AAClE,QAAM,SAAS,OAAO,OACnB,IAAI,CAAC,UAAU,SAAS,IAAI,MAAM,IAAI,CAAC,EACvC,OAAO,CAAC,MAAM,MAAM,MAAS;AAEhC,QAAM,QAAQ,cAAc,MAAM;AAElC,QAAM,iBAAiB,oBAAI,IAAoB;AAC/C,aAAW,UAAU,OAAO,SAAS;AACnC,UAAM,UAAU,MAAM,gBAAgB,aAAa,OAAO,MAAM;AAChE,mBAAe,IAAID,MAAK,aAAa,MAAM,GAAG,OAAO;AAAA,EACvD;AAEA,QAAM,cAAc,OAAO,WAAW,oBAAoB,KAAK,CAAC;AAChE,QAAM,UAAU,MAAM,eAAe,aAAa,OAAO,OAAO;AAEhE,SAAO;AAAA,IACL,YAAY,OAAO;AAAA,IACnB;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;;;AE/EA,eAAsB,SAAS,aAAqB,WAAuC;AACzF,QAAM,SAAS,MAAM,WAAW,WAAW;AAC3C,QAAM,YAAY,MAAM,cAAc,WAAW;AAEjD,QAAM,SAAS,UAAU,SAAS,GAAG;AAErC,MAAI;AAEJ,MAAI,QAAQ;AAEV,kBAAc,UAAU,KAAK,CAAC,MAAM,EAAE,iBAAiB,SAAS;AAChE,QAAI,CAAC,aAAa;AAChB,YAAM,IAAI,MAAM,UAAU,SAAS,kDAAkD;AAAA,IACvF;AAAA,EACF,OAAO;AAEL,UAAM,UAAU,UAAU,OAAO,CAAC,MAAM,EAAE,SAAS,SAAS;AAC5D,QAAI,QAAQ,WAAW,GAAG;AACxB,YAAM,IAAI,MAAM,UAAU,SAAS,kDAAkD;AAAA,IACvF;AACA,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,QAAQ,QAAQ,IAAI,CAAC,MAAM,KAAK,EAAE,YAAY,EAAE,EAAE,KAAK,IAAI;AACjE,YAAM,IAAI,MAAM,0BAA0B,SAAS;AAAA,EAA+B,KAAK,EAAE;AAAA,IAC3F;AACA,kBAAc,QAAQ,CAAC;AAAA,EACzB;AAEA,MAAI,OAAO,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,YAAY,YAAY,GAAG;AAClE,UAAM,IAAI,MAAM,UAAU,YAAY,YAAY,sBAAsB;AAAA,EAC1E;AAEA,SAAO,OAAO,KAAK;AAAA,IACjB,MAAM,YAAY;AAAA,IAClB,MAAM,YAAY;AAAA,EACpB,CAAC;AAED,QAAM,YAAY,aAAa,MAAM;AAErC,QAAM,SAAS,MAAM,qBAAqB,WAAW;AAErD,SAAO;AAAA,IACL,WAAW,YAAY;AAAA,IACvB,aAAa,OAAO;AAAA,IACpB,SAAS,OAAO;AAAA,EAClB;AACF;;;AC3CA,eAAsB,KAAK,SAIH;AACtB,QAAM,EAAE,aAAa,eAAe,IAAI;AAExC,QAAM,aAAa,MAAM,cAAc,WAAW;AAElD,MAAI;AACJ,MAAI,gBAAgB;AAClB,UAAM,WAAW,IAAI,IAAI,cAAc;AACvC,aAAS,WAAW,OAAO,CAAC,MAAM,SAAS,IAAI,EAAE,IAAI,KAAK,SAAS,IAAI,EAAE,YAAY,CAAC;AAAA,EACxF,OAAO;AACL,aAAS;AAAA,EACX;AAEA,SAAO,eAAe,aAAa,MAAM;AAC3C;AAGA,eAAsB,eACpB,aACA,QACA,UAAoB,CAAC,WAAW,GACX;AACrB,QAAM,SAAS;AAAA,IACb,SAAS;AAAA,IACT;AAAA,IACA,QAAQ,OAAO,IAAI,CAAC,WAAW;AAAA,MAC7B,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,IACd,EAAE;AAAA,EACJ;AACA,QAAM,YAAY,aAAa,MAAM;AAErC,SAAO,qBAAqB,WAAW;AACzC;;;ACxCA,eAAsB,WAAW,aAA0C;AACzE,QAAM,SAAS,MAAM,WAAW,WAAW;AAC3C,QAAM,YAAY,MAAM,cAAc,WAAW;AAEjD,QAAM,eAAe,IAAI,IAAI,OAAO,OAAO,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AAC7D,QAAM,WAAW,IAAI,IAAI,UAAU,IAAI,CAAC,MAAM,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC;AAElE,QAAM,UAAuB,OAAO,OACjC,IAAI,CAAC,UAAU;AACd,UAAM,aAAa,SAAS,IAAI,MAAM,IAAI;AAC1C,QAAI,CAAC,WAAY,QAAO;AACxB,WAAO;AAAA,MACL,MAAM,MAAM;AAAA,MACZ,MAAM,MAAM;AAAA,MACZ,aAAa,WAAW;AAAA,IAC1B;AAAA,EACF,CAAC,EACA,OAAO,CAAC,MAAM,MAAM,MAAS;AAEhC,QAAM,YAAyB,UAC5B,OAAO,CAAC,MAAM,CAAC,aAAa,IAAI,EAAE,YAAY,CAAC,EAC/C,IAAI,CAAC,OAAO;AAAA,IACX,MAAM,EAAE;AAAA,IACR,aAAa,EAAE;AAAA,IACf,MAAM,EAAE;AAAA,EACV,EAAE;AAEJ,SAAO,EAAE,SAAS,UAAU;AAC9B;;;AChCA,SAAS,UAAU;AACnB,SAAS,QAAAE,aAAY;AAKrB,eAAsB,YACpB,aACA,WACA,cAAc,OACS;AACvB,QAAM,SAAS,MAAM,WAAW,WAAW;AAE3C,QAAM,SAAS,UAAU,SAAS,GAAG;AAErC,MAAI;AAEJ,MAAI,QAAQ;AACV,iBAAa,OAAO,OAAO,UAAU,CAAC,MAAM,EAAE,SAAS,SAAS;AAChE,QAAI,eAAe,IAAI;AACrB,YAAM,IAAI,MAAM,UAAU,SAAS,kBAAkB;AAAA,IACvD;AAAA,EACF,OAAO;AACL,UAAM,UAAU,OAAO,OACpB,IAAI,CAAC,GAAG,OAAO,EAAE,OAAO,GAAG,OAAO,EAAE,EAAE,EACtC,OAAO,CAAC,EAAE,OAAAC,OAAM,MAAMA,OAAM,SAAS,SAAS;AAEjD,QAAI,QAAQ,WAAW,GAAG;AACxB,YAAM,IAAI,MAAM,UAAU,SAAS,kBAAkB;AAAA,IACvD;AACA,QAAI,QAAQ,SAAS,GAAG;AACtB,YAAM,QAAQ,QAAQ,IAAI,CAAC,EAAE,OAAAA,OAAM,MAAM,KAAKA,OAAM,IAAI,EAAE,EAAE,KAAK,IAAI;AACrE,YAAM,IAAI,MAAM,0BAA0B,SAAS;AAAA,EAAiC,KAAK,EAAE;AAAA,IAC7F;AACA,iBAAa,QAAQ,CAAC,EAAE;AAAA,EAC1B;AAEA,QAAM,QAAQ,OAAO,OAAO,UAAU;AACtC,QAAM,YAAYC,MAAK,aAAa,MAAM,IAAI;AAE9C,SAAO,OAAO,OAAO,YAAY,CAAC;AAClC,QAAM,YAAY,aAAa,MAAM;AAErC,MAAI,aAAa;AACf,UAAM,GAAG,WAAW,EAAE,WAAW,MAAM,OAAO,KAAK,CAAC;AAAA,EACtD;AAEA,QAAM,SAAS,MAAM,qBAAqB,WAAW;AAErD,SAAO;AAAA,IACL,WAAW,MAAM;AAAA,IACjB,YAAY;AAAA,IACZ,aAAa,OAAO;AAAA,IACpB,SAAS,OAAO;AAAA,EAClB;AACF;;;ACvDA,SAAS,QAAAC,aAAY;AAMrB,eAAsB,WAAW,aAA0C;AACzE,QAAM,SAAS,MAAM,WAAW,WAAW;AAC3C,QAAM,SAAS,MAAM,cAAc,WAAW;AAC9C,QAAM,YAAY,IAAI,IAAI,OAAO,IAAI,CAAC,MAAM,EAAE,YAAY,CAAC;AAG3D,QAAM,QAAQ,OAAO,OAAO,OAAO,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,IAAI,CAAC;AAChE,QAAM,UAAU,MAAM,IAAI,CAAC,MAAM,EAAE,IAAI;AAEvC,MAAI,MAAM,SAAS,GAAG;AACpB,UAAM,aAAa,IAAI,IAAI,MAAM,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC;AACnD,WAAO,SAAS,OAAO,OAAO,OAAO,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,IAAI,CAAC;AACnE,UAAM,YAAY,aAAa,MAAM;AAAA,EACvC;AAEA,QAAM,cAAc,OAAO,QAAQ,IAAI,CAAC,MAAMC,MAAK,aAAa,CAAC,CAAC;AAGlE,QAAM,YAAY,oBAAI,IAAgC;AACtD,aAAW,cAAc,aAAa;AACpC,cAAU,IAAI,YAAY,MAAM,aAAa,UAAU,CAAC;AAAA,EAC1D;AAGA,MAAI,OAAO,OAAO,WAAW,KAAK,CAAC,GAAG,UAAU,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,MAAM,MAAS,GAAG;AACvF,WAAO,EAAE,SAAS,SAAS,OAAO,aAAa,GAAG,SAAS,CAAC,EAAE;AAAA,EAChE;AAEA,QAAM,SAAS,MAAM,qBAAqB,WAAW;AAGrD,MAAI,UAAU;AACd,aAAW,cAAc,aAAa;AACpC,UAAM,QAAQ,OAAO,eAAe,IAAI,UAAU;AAClD,QAAI,UAAU,IAAI,UAAU,MAAM,OAAO;AACvC,gBAAU;AACV;AAAA,IACF;AAAA,EACF;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA,aAAa,OAAO;AAAA,IACpB,SAAS,OAAO;AAAA,EAClB;AACF;","names":["join","join","writeFile","join","join","writeFile","join","entry","join","join","join"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "skilldex",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "CLI tool that indexes AI agent skills into passive context (AGENTS.md)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"module": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsup",
|
|
11
|
+
"dev": "tsup --watch",
|
|
12
|
+
"test": "vitest run --passWithNoTests",
|
|
13
|
+
"test:watch": "vitest",
|
|
14
|
+
"lint": "biome check .",
|
|
15
|
+
"format": "biome check --write .",
|
|
16
|
+
"prepublishOnly": "pnpm build && pnpm test"
|
|
17
|
+
},
|
|
18
|
+
"bin": {
|
|
19
|
+
"skilldex": "./dist/cli/index.js"
|
|
20
|
+
},
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/index.d.ts",
|
|
24
|
+
"import": "./dist/index.js",
|
|
25
|
+
"require": "./dist/index.cjs"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"keywords": [
|
|
29
|
+
"ai",
|
|
30
|
+
"agents",
|
|
31
|
+
"skills",
|
|
32
|
+
"cli",
|
|
33
|
+
"agents-md",
|
|
34
|
+
"claude",
|
|
35
|
+
"cursor",
|
|
36
|
+
"copilot",
|
|
37
|
+
"passive-context",
|
|
38
|
+
"llm"
|
|
39
|
+
],
|
|
40
|
+
"author": "Nafis Azizi Riza",
|
|
41
|
+
"license": "MIT",
|
|
42
|
+
"repository": {
|
|
43
|
+
"type": "git",
|
|
44
|
+
"url": "git+https://github.com/nafisazizir/skilldex.git"
|
|
45
|
+
},
|
|
46
|
+
"homepage": "https://github.com/nafisazizir/skilldex#readme",
|
|
47
|
+
"bugs": {
|
|
48
|
+
"url": "https://github.com/nafisazizir/skilldex/issues"
|
|
49
|
+
},
|
|
50
|
+
"files": [
|
|
51
|
+
"dist"
|
|
52
|
+
],
|
|
53
|
+
"engines": {
|
|
54
|
+
"node": ">=18"
|
|
55
|
+
},
|
|
56
|
+
"dependencies": {
|
|
57
|
+
"@clack/prompts": "^1.0.1",
|
|
58
|
+
"commander": "^14.0.3",
|
|
59
|
+
"picocolors": "^1.1.1"
|
|
60
|
+
},
|
|
61
|
+
"pnpm": {
|
|
62
|
+
"onlyBuiltDependencies": [
|
|
63
|
+
"esbuild",
|
|
64
|
+
"@biomejs/biome"
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
"devDependencies": {
|
|
68
|
+
"@biomejs/biome": "^2.3.15",
|
|
69
|
+
"@types/node": "^25.2.3",
|
|
70
|
+
"tsup": "^8.5.1",
|
|
71
|
+
"typescript": "^5.9.3",
|
|
72
|
+
"vitest": "^4.0.18"
|
|
73
|
+
}
|
|
74
|
+
}
|