agentsmesh 0.24.0 → 0.26.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/CHANGELOG.md +45 -0
- package/README.md +104 -456
- package/dist/canonical.js +199 -147
- package/dist/canonical.js.map +1 -1
- package/dist/cli.js +249 -201
- package/dist/engine.js +213 -147
- package/dist/engine.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +286 -207
- package/dist/index.js.map +1 -1
- package/dist/{init-YKxF2zpQ.d.ts → init-B0aI-g8W.d.ts} +1 -1
- package/dist/lessons.d.ts +2 -2
- package/dist/lessons.js +73 -60
- package/dist/lessons.js.map +1 -1
- package/dist/targets.js +111 -59
- package/dist/targets.js.map +1 -1
- package/package.json +3 -2
package/dist/engine.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import { stringify, parse } from 'yaml';
|
|
3
3
|
import { basename, join, dirname, relative, win32, posix, sep, resolve, extname } from 'path';
|
|
4
|
-
import { readFile, rm, mkdir, readdir, stat, lstat, unlink, writeFile, rename, chmod,
|
|
4
|
+
import { readFile, rm, mkdir, readdir, stat, lstat, unlink, writeFile, rename, chmod, access, realpath, mkdtemp, cp } from 'fs/promises';
|
|
5
5
|
import { setTimeout } from 'timers/promises';
|
|
6
6
|
import { existsSync, readFileSync, constants, readdirSync, realpathSync, statSync } from 'fs';
|
|
7
7
|
import { parse as parse$1 } from 'smol-toml';
|
|
@@ -1379,7 +1379,7 @@ async function importEmbeddedSkills(projectRoot, skillsDir, fromTool, results, n
|
|
|
1379
1379
|
toPath: `${AB_SKILLS}/${entry.name}/SKILL.md`,
|
|
1380
1380
|
feature: "skills"
|
|
1381
1381
|
});
|
|
1382
|
-
const sourceFiles = await
|
|
1382
|
+
const sourceFiles = await readDirRecursiveNoSymlinks(sourceSkillDir);
|
|
1383
1383
|
for (const sourcePath of sourceFiles) {
|
|
1384
1384
|
if (sourcePath === sourceSkillFile) continue;
|
|
1385
1385
|
const relativePath = relative(sourceSkillDir, sourcePath).replace(/\\/g, "/");
|
|
@@ -2327,7 +2327,7 @@ function matchesExtension(path, extensions) {
|
|
|
2327
2327
|
return extensions.some((extension) => path.endsWith(extension));
|
|
2328
2328
|
}
|
|
2329
2329
|
async function importFileDirectory(opts) {
|
|
2330
|
-
const files = await
|
|
2330
|
+
const files = await readDirRecursiveNoSymlinks(opts.srcDir);
|
|
2331
2331
|
const matchedFiles = files.filter((path) => matchesExtension(path, opts.extensions));
|
|
2332
2332
|
const results = [];
|
|
2333
2333
|
for (const srcPath of matchedFiles) {
|
|
@@ -6746,7 +6746,7 @@ var init_settings_helpers2 = __esm({
|
|
|
6746
6746
|
async function importClaudeSkills(projectRoot, results, normalize) {
|
|
6747
6747
|
const skillsBaseDir = join(projectRoot, CLAUDE_SKILLS_DIR);
|
|
6748
6748
|
const destBase = join(projectRoot, CLAUDE_CANONICAL_SKILLS_DIR);
|
|
6749
|
-
const allFiles = await
|
|
6749
|
+
const allFiles = await readDirRecursiveNoSymlinks(skillsBaseDir);
|
|
6750
6750
|
const skillMdFiles = allFiles.filter((f) => f.endsWith("SKILL.md"));
|
|
6751
6751
|
for (const skillMdPath of skillMdFiles) {
|
|
6752
6752
|
const skillDir = dirname(skillMdPath);
|
|
@@ -6765,7 +6765,7 @@ async function importClaudeSkills(projectRoot, results, normalize) {
|
|
|
6765
6765
|
`);
|
|
6766
6766
|
continue;
|
|
6767
6767
|
}
|
|
6768
|
-
const skillFiles = await
|
|
6768
|
+
const skillFiles = await readDirRecursiveNoSymlinks(skillDir);
|
|
6769
6769
|
for (const filePath of skillFiles) {
|
|
6770
6770
|
const fileContent = await readFileSafe(filePath);
|
|
6771
6771
|
if (fileContent === null) continue;
|
|
@@ -7390,7 +7390,7 @@ async function importClineRules(projectRoot, results, normalize) {
|
|
|
7390
7390
|
feature: "rules"
|
|
7391
7391
|
});
|
|
7392
7392
|
} else {
|
|
7393
|
-
const ruleFiles = await
|
|
7393
|
+
const ruleFiles = await readDirRecursiveNoSymlinks(clineRulesPath);
|
|
7394
7394
|
const mdFiles = ruleFiles.filter((f) => f.endsWith(".md") && !f.includes("/workflows/")).sort();
|
|
7395
7395
|
const first = mdFiles[0];
|
|
7396
7396
|
if (first) {
|
|
@@ -7454,27 +7454,40 @@ var init_importer_rules = __esm({
|
|
|
7454
7454
|
init_constants8();
|
|
7455
7455
|
}
|
|
7456
7456
|
});
|
|
7457
|
+
function toStringRecord2(raw) {
|
|
7458
|
+
if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return {};
|
|
7459
|
+
return Object.fromEntries(
|
|
7460
|
+
Object.entries(raw).filter((entry) => typeof entry[1] === "string")
|
|
7461
|
+
);
|
|
7462
|
+
}
|
|
7457
7463
|
function mapClineServerToCanonical(raw) {
|
|
7458
7464
|
if (!raw || typeof raw !== "object") return null;
|
|
7459
7465
|
const obj = raw;
|
|
7460
|
-
const
|
|
7461
|
-
|
|
7462
|
-
const
|
|
7463
|
-
const args = Array.isArray(obj.args) ? obj.args.filter((x) => typeof x === "string") : [];
|
|
7464
|
-
const envRaw = obj.env;
|
|
7465
|
-
const env = envRaw !== null && typeof envRaw === "object" && !Array.isArray(envRaw) ? Object.fromEntries(
|
|
7466
|
-
Object.entries(envRaw).filter(
|
|
7467
|
-
(entry) => typeof entry[1] === "string"
|
|
7468
|
-
)
|
|
7469
|
-
) : {};
|
|
7466
|
+
const transport = typeof obj.type === "string" ? obj.type : void 0;
|
|
7467
|
+
const transportType = typeof obj.transportType === "string" ? obj.transportType : void 0;
|
|
7468
|
+
const env = toStringRecord2(obj.env);
|
|
7470
7469
|
const description = typeof obj.description === "string" ? obj.description : void 0;
|
|
7471
|
-
|
|
7472
|
-
|
|
7473
|
-
|
|
7474
|
-
|
|
7475
|
-
|
|
7476
|
-
|
|
7477
|
-
|
|
7470
|
+
const command = typeof obj.command === "string" ? obj.command : "";
|
|
7471
|
+
if (command) {
|
|
7472
|
+
const args = Array.isArray(obj.args) ? obj.args.filter((x) => typeof x === "string") : [];
|
|
7473
|
+
return {
|
|
7474
|
+
...description !== void 0 && { description },
|
|
7475
|
+
type: transport ?? transportType ?? "stdio",
|
|
7476
|
+
command,
|
|
7477
|
+
args,
|
|
7478
|
+
env
|
|
7479
|
+
};
|
|
7480
|
+
}
|
|
7481
|
+
if (typeof obj.url === "string") {
|
|
7482
|
+
return {
|
|
7483
|
+
...description !== void 0 && { description },
|
|
7484
|
+
type: transport ?? transportType ?? "http",
|
|
7485
|
+
url: obj.url,
|
|
7486
|
+
headers: toStringRecord2(obj.headers),
|
|
7487
|
+
env
|
|
7488
|
+
};
|
|
7489
|
+
}
|
|
7490
|
+
return null;
|
|
7478
7491
|
}
|
|
7479
7492
|
async function importClineMcp(projectRoot, results) {
|
|
7480
7493
|
const candidatePaths = [CLINE_MCP_SETTINGS, CLINE_MCP_SETTINGS_LEGACY].map(
|
|
@@ -7548,7 +7561,7 @@ var init_reserved = __esm({
|
|
|
7548
7561
|
}
|
|
7549
7562
|
});
|
|
7550
7563
|
async function readNativeSkill(skillDir) {
|
|
7551
|
-
const allFiles = await
|
|
7564
|
+
const allFiles = await readDirRecursiveNoSymlinks(skillDir).catch(() => []);
|
|
7552
7565
|
const entries = [];
|
|
7553
7566
|
for (const absPath of allFiles) {
|
|
7554
7567
|
const relPath = relative(skillDir, absPath).replace(/\\/g, "/");
|
|
@@ -7614,7 +7627,7 @@ async function importFlatSkill(skillName, srcPath, content, options) {
|
|
|
7614
7627
|
async function findDirectorySkills(skillsDir) {
|
|
7615
7628
|
const skills = /* @__PURE__ */ new Map();
|
|
7616
7629
|
try {
|
|
7617
|
-
const allFiles = await
|
|
7630
|
+
const allFiles = await readDirRecursiveNoSymlinks(skillsDir);
|
|
7618
7631
|
const skillMdFiles = allFiles.filter((f) => basename(f) === "SKILL.md");
|
|
7619
7632
|
for (const skillMdPath of skillMdFiles) {
|
|
7620
7633
|
const skillDir = dirname(skillMdPath);
|
|
@@ -7745,7 +7758,7 @@ function extractMeta(content, key) {
|
|
|
7745
7758
|
return match?.[1]?.trim() ?? null;
|
|
7746
7759
|
}
|
|
7747
7760
|
async function loadHooksFromDir(dir, hooks) {
|
|
7748
|
-
const files = await
|
|
7761
|
+
const files = await readDirRecursiveNoSymlinks(dir).catch(() => []);
|
|
7749
7762
|
const shFiles = files.filter((f) => basename(f).endsWith(".sh") && dirname(f) === dir);
|
|
7750
7763
|
for (const srcPath of shFiles) {
|
|
7751
7764
|
const content = await readFileSafe(srcPath);
|
|
@@ -8480,7 +8493,7 @@ async function importCodexAgentsFromToml(projectRoot, results, normalize) {
|
|
|
8480
8493
|
const agentsPath = join(projectRoot, CODEX_AGENTS_DIR);
|
|
8481
8494
|
const agentsDestDir = join(projectRoot, CODEX_CANONICAL_AGENTS_DIR);
|
|
8482
8495
|
try {
|
|
8483
|
-
const agentFiles = await
|
|
8496
|
+
const agentFiles = await readDirRecursiveNoSymlinks(agentsPath);
|
|
8484
8497
|
const tomlFiles = agentFiles.filter((f) => f.endsWith(".toml"));
|
|
8485
8498
|
for (const srcPath of tomlFiles) {
|
|
8486
8499
|
const content = await readFileSafe(srcPath);
|
|
@@ -8576,7 +8589,7 @@ async function importCodexNonRootRuleFiles(projectRoot, destDir, normalize) {
|
|
|
8576
8589
|
const results = [];
|
|
8577
8590
|
const codexRulesPath = join(projectRoot, CODEX_RULES_DIR);
|
|
8578
8591
|
try {
|
|
8579
|
-
const ruleFiles = await
|
|
8592
|
+
const ruleFiles = await readDirRecursiveNoSymlinks(codexRulesPath);
|
|
8580
8593
|
const mdFiles = ruleFiles.filter((f) => f.endsWith(".md"));
|
|
8581
8594
|
for (const srcPath of mdFiles) {
|
|
8582
8595
|
const content = await readFileSafe(srcPath);
|
|
@@ -8733,7 +8746,7 @@ async function importCodexRules(projectRoot, results, normalize, normalizeWindsu
|
|
|
8733
8746
|
}
|
|
8734
8747
|
async function importInstructionMirrors(projectRoot, destDir, results, normalize) {
|
|
8735
8748
|
try {
|
|
8736
|
-
const files = await
|
|
8749
|
+
const files = await readDirRecursiveNoSymlinks(join(projectRoot, CODEX_INSTRUCTIONS_DIR));
|
|
8737
8750
|
const instructionFiles = files.filter((file) => file.endsWith(".md"));
|
|
8738
8751
|
const instructionsRoot = join(projectRoot, CODEX_INSTRUCTIONS_DIR);
|
|
8739
8752
|
for (const srcPath of instructionFiles) {
|
|
@@ -9179,19 +9192,31 @@ function readMcpServers(content, extension) {
|
|
|
9179
9192
|
for (const [name, value] of Object.entries(rawServers)) {
|
|
9180
9193
|
if (!value || typeof value !== "object" || Array.isArray(value)) continue;
|
|
9181
9194
|
const server = value;
|
|
9182
|
-
|
|
9183
|
-
|
|
9184
|
-
|
|
9185
|
-
|
|
9186
|
-
|
|
9187
|
-
|
|
9188
|
-
|
|
9189
|
-
|
|
9195
|
+
const description = typeof server.description === "string" ? server.description : void 0;
|
|
9196
|
+
if (typeof server.command === "string") {
|
|
9197
|
+
servers[name] = {
|
|
9198
|
+
type: typeof server.type === "string" ? server.type : "stdio",
|
|
9199
|
+
command: server.command,
|
|
9200
|
+
args: toStringArray5(server.args),
|
|
9201
|
+
env: toStringRecord(server.env),
|
|
9202
|
+
description
|
|
9203
|
+
};
|
|
9204
|
+
continue;
|
|
9205
|
+
}
|
|
9206
|
+
if (typeof server.url === "string") {
|
|
9207
|
+
servers[name] = {
|
|
9208
|
+
type: typeof server.type === "string" ? server.type : "http",
|
|
9209
|
+
url: server.url,
|
|
9210
|
+
headers: toStringRecord(server.headers),
|
|
9211
|
+
env: toStringRecord(server.env),
|
|
9212
|
+
description
|
|
9213
|
+
};
|
|
9214
|
+
}
|
|
9190
9215
|
}
|
|
9191
9216
|
return servers;
|
|
9192
9217
|
}
|
|
9193
9218
|
async function importMcp2(projectRoot, results) {
|
|
9194
|
-
const files = (await
|
|
9219
|
+
const files = (await readDirRecursiveNoSymlinks(join(projectRoot, CONTINUE_MCP_DIR))).filter(
|
|
9195
9220
|
(file) => [".json", ".yaml", ".yml"].includes(extname(file))
|
|
9196
9221
|
);
|
|
9197
9222
|
const merged = {};
|
|
@@ -9851,7 +9876,7 @@ function extractWrapperCommand(content) {
|
|
|
9851
9876
|
}
|
|
9852
9877
|
async function importHooks(projectRoot, results) {
|
|
9853
9878
|
const hooksDir = join(projectRoot, COPILOT_HOOKS_DIR);
|
|
9854
|
-
const allFiles = await
|
|
9879
|
+
const allFiles = await readDirRecursiveNoSymlinks(hooksDir).catch(() => []);
|
|
9855
9880
|
const jsonFiles = allFiles.filter((file) => file.endsWith(".json"));
|
|
9856
9881
|
const hooks = {};
|
|
9857
9882
|
for (const srcPath of jsonFiles) {
|
|
@@ -9887,7 +9912,7 @@ async function importHooks(projectRoot, results) {
|
|
|
9887
9912
|
}
|
|
9888
9913
|
}
|
|
9889
9914
|
const legacyDir = join(projectRoot, COPILOT_LEGACY_HOOKS_DIR);
|
|
9890
|
-
const legacyFiles = await
|
|
9915
|
+
const legacyFiles = await readDirRecursiveNoSymlinks(legacyDir).catch(() => []);
|
|
9891
9916
|
const shFiles = legacyFiles.filter(
|
|
9892
9917
|
(file) => dirname(file) === legacyDir && /^[^-]+-\d+\.sh$/i.test(basename(file))
|
|
9893
9918
|
);
|
|
@@ -9964,7 +9989,7 @@ var init_importer10 = __esm({
|
|
|
9964
9989
|
}
|
|
9965
9990
|
});
|
|
9966
9991
|
async function skillNamesFromNativeSkillDir(scanRoot) {
|
|
9967
|
-
const files = await
|
|
9992
|
+
const files = await readDirRecursiveNoSymlinks(scanRoot);
|
|
9968
9993
|
const names = /* @__PURE__ */ new Set();
|
|
9969
9994
|
for (const f of files) {
|
|
9970
9995
|
if (basename(f) === "SKILL.md") {
|
|
@@ -9986,7 +10011,7 @@ var init_native_skill_scan = __esm({
|
|
|
9986
10011
|
async function inferCopilotPickFromPath(repoRoot, posixPath) {
|
|
9987
10012
|
const scan = join(repoRoot, ...posixPath.split("/"));
|
|
9988
10013
|
if (posixPath.startsWith(COPILOT_PROMPTS_DIR)) {
|
|
9989
|
-
const files = await
|
|
10014
|
+
const files = await readDirRecursiveNoSymlinks(scan);
|
|
9990
10015
|
const commands = [
|
|
9991
10016
|
...new Set(
|
|
9992
10017
|
files.filter((f) => f.toLowerCase().endsWith(".prompt.md")).map((f) => basename(f, ".prompt.md"))
|
|
@@ -9995,7 +10020,7 @@ async function inferCopilotPickFromPath(repoRoot, posixPath) {
|
|
|
9995
10020
|
return commands.length ? { commands } : {};
|
|
9996
10021
|
}
|
|
9997
10022
|
if (posixPath.startsWith(".github/copilot") && !posixPath.includes("copilot-instructions.md")) {
|
|
9998
|
-
const files = await
|
|
10023
|
+
const files = await readDirRecursiveNoSymlinks(scan);
|
|
9999
10024
|
const rules = [
|
|
10000
10025
|
...new Set(
|
|
10001
10026
|
files.filter((f) => f.includes(".instructions.md")).map((f) => basename(f).replace(/\.instructions\.md$/i, ""))
|
|
@@ -10004,7 +10029,7 @@ async function inferCopilotPickFromPath(repoRoot, posixPath) {
|
|
|
10004
10029
|
return rules.length ? { rules } : {};
|
|
10005
10030
|
}
|
|
10006
10031
|
if (posixPath.startsWith(".github/instructions")) {
|
|
10007
|
-
const files = await
|
|
10032
|
+
const files = await readDirRecursiveNoSymlinks(scan);
|
|
10008
10033
|
const names = /* @__PURE__ */ new Set();
|
|
10009
10034
|
for (const f of files) {
|
|
10010
10035
|
const b = basename(f);
|
|
@@ -10020,7 +10045,7 @@ async function inferCopilotPickFromPath(repoRoot, posixPath) {
|
|
|
10020
10045
|
return skills.length ? { skills } : {};
|
|
10021
10046
|
}
|
|
10022
10047
|
if (posixPath.startsWith(".github/agents")) {
|
|
10023
|
-
const files = await
|
|
10048
|
+
const files = await readDirRecursiveNoSymlinks(scan);
|
|
10024
10049
|
const agents = [
|
|
10025
10050
|
...new Set(
|
|
10026
10051
|
files.filter((f) => f.toLowerCase().endsWith(".agent.md")).map((f) => basename(f, ".agent.md"))
|
|
@@ -11569,7 +11594,7 @@ async function importSkills3(projectRoot, results, normalize, skillsRelDir = CUR
|
|
|
11569
11594
|
for (const [skillName, skillDir] of directorySkills) {
|
|
11570
11595
|
await importDirectorySkill(skillName, skillDir, options);
|
|
11571
11596
|
}
|
|
11572
|
-
const allFiles = await
|
|
11597
|
+
const allFiles = await readDirRecursiveNoSymlinks(skillsDir).catch(() => []);
|
|
11573
11598
|
const mdFiles = allFiles.filter((f) => f.endsWith(".md"));
|
|
11574
11599
|
const handledPaths = new Set(
|
|
11575
11600
|
Array.from(directorySkills.values()).flatMap(
|
|
@@ -11607,11 +11632,11 @@ async function hasGlobalCursorArtifacts(projectRoot) {
|
|
|
11607
11632
|
const stat8 = await readFileSafe(p);
|
|
11608
11633
|
if (stat8 !== null && stat8.trim() !== "") return true;
|
|
11609
11634
|
}
|
|
11610
|
-
const skillFiles = await
|
|
11635
|
+
const skillFiles = await readDirRecursiveNoSymlinks(join(projectRoot, CURSOR_SKILLS_DIR));
|
|
11611
11636
|
if (skillFiles.some((f) => f.endsWith(".md"))) return true;
|
|
11612
|
-
const agentFiles = await
|
|
11637
|
+
const agentFiles = await readDirRecursiveNoSymlinks(join(projectRoot, CURSOR_AGENTS_DIR));
|
|
11613
11638
|
if (agentFiles.some((f) => f.endsWith(".md"))) return true;
|
|
11614
|
-
const commandFiles = await
|
|
11639
|
+
const commandFiles = await readDirRecursiveNoSymlinks(join(projectRoot, CURSOR_COMMANDS_DIR));
|
|
11615
11640
|
if (commandFiles.some((f) => f.endsWith(".md"))) return true;
|
|
11616
11641
|
return false;
|
|
11617
11642
|
}
|
|
@@ -13199,7 +13224,7 @@ async function importGeminiPolicies(projectRoot) {
|
|
|
13199
13224
|
const policiesDir = join(projectRoot, GEMINI_POLICIES_DIR);
|
|
13200
13225
|
let policyFiles;
|
|
13201
13226
|
try {
|
|
13202
|
-
policyFiles = await
|
|
13227
|
+
policyFiles = await readDirRecursiveNoSymlinks(policiesDir);
|
|
13203
13228
|
} catch {
|
|
13204
13229
|
return results;
|
|
13205
13230
|
}
|
|
@@ -13298,7 +13323,7 @@ var init_importer_strip = __esm({
|
|
|
13298
13323
|
});
|
|
13299
13324
|
async function importGeminiSkillsAndAgents(projectRoot, results, normalize) {
|
|
13300
13325
|
const geminiSkillsPath = join(projectRoot, GEMINI_SKILLS_DIR);
|
|
13301
|
-
const skillDirs = await
|
|
13326
|
+
const skillDirs = await readDirRecursiveNoSymlinks(geminiSkillsPath);
|
|
13302
13327
|
const skillMdFiles = skillDirs.filter((f) => basename(f) === "SKILL.md");
|
|
13303
13328
|
for (const srcPath of skillMdFiles) {
|
|
13304
13329
|
const content = await readFileSafe(srcPath);
|
|
@@ -13337,7 +13362,7 @@ async function importGeminiSkillsAndAgents(projectRoot, results, normalize) {
|
|
|
13337
13362
|
toPath: `${GEMINI_CANONICAL_SKILLS_DIR}/${skillName}/SKILL.md`,
|
|
13338
13363
|
feature: "skills"
|
|
13339
13364
|
});
|
|
13340
|
-
const allSkillFiles = await
|
|
13365
|
+
const allSkillFiles = await readDirRecursiveNoSymlinks(dirname(srcPath));
|
|
13341
13366
|
for (const absPath of allSkillFiles) {
|
|
13342
13367
|
if (absPath === srcPath) continue;
|
|
13343
13368
|
const supportContent = await readFileSafe(absPath);
|
|
@@ -13356,7 +13381,7 @@ async function importGeminiSkillsAndAgents(projectRoot, results, normalize) {
|
|
|
13356
13381
|
}
|
|
13357
13382
|
const geminiAgentsPath = join(projectRoot, GEMINI_AGENTS_DIR);
|
|
13358
13383
|
try {
|
|
13359
|
-
const agentFiles = await
|
|
13384
|
+
const agentFiles = await readDirRecursiveNoSymlinks(geminiAgentsPath);
|
|
13360
13385
|
const agentMdFiles = agentFiles.filter((f) => f.endsWith(".md"));
|
|
13361
13386
|
for (const srcPath of agentMdFiles) {
|
|
13362
13387
|
const content = await readFileSafe(srcPath);
|
|
@@ -13489,7 +13514,7 @@ function isUnderGeminiCommands(pathInRepoPosix) {
|
|
|
13489
13514
|
async function inferGeminiCommandNamesFromFiles(repoRoot, pathInRepoPosix) {
|
|
13490
13515
|
const commandsRoot = join(repoRoot, ...GEMINI_COMMANDS_DIR.split("/"));
|
|
13491
13516
|
const scanDir = join(repoRoot, ...pathInRepoPosix.split("/"));
|
|
13492
|
-
const files = await
|
|
13517
|
+
const files = await readDirRecursiveNoSymlinks(scanDir);
|
|
13493
13518
|
const names = [];
|
|
13494
13519
|
for (const f of files) {
|
|
13495
13520
|
if (!/\.(toml|md)$/i.test(f)) continue;
|
|
@@ -15701,7 +15726,7 @@ async function importNonRootRules(projectRoot, results, normalize) {
|
|
|
15701
15726
|
}
|
|
15702
15727
|
async function importHooks2(projectRoot, results) {
|
|
15703
15728
|
const hooks = {};
|
|
15704
|
-
for (const absPath of await
|
|
15729
|
+
for (const absPath of await readDirRecursiveNoSymlinks(join(projectRoot, KIRO_HOOKS_DIR))) {
|
|
15705
15730
|
if (!absPath.endsWith(".kiro.hook")) continue;
|
|
15706
15731
|
const parsed = parseKiroHookFile(await readFileSafe(absPath) ?? "");
|
|
15707
15732
|
if (!parsed) continue;
|
|
@@ -16053,7 +16078,7 @@ var init_generator24 = __esm({
|
|
|
16053
16078
|
init_constants19();
|
|
16054
16079
|
}
|
|
16055
16080
|
});
|
|
16056
|
-
function
|
|
16081
|
+
function toStringRecord3(value) {
|
|
16057
16082
|
if (!value || typeof value !== "object" || Array.isArray(value)) return {};
|
|
16058
16083
|
const out2 = {};
|
|
16059
16084
|
for (const [k, v] of Object.entries(value)) {
|
|
@@ -16079,8 +16104,8 @@ function parseOpenCodeMcp(content) {
|
|
|
16079
16104
|
out2[name] = {
|
|
16080
16105
|
type: "url",
|
|
16081
16106
|
url: entry.url,
|
|
16082
|
-
headers:
|
|
16083
|
-
env:
|
|
16107
|
+
headers: toStringRecord3(entry.headers),
|
|
16108
|
+
env: toStringRecord3(entry.environment),
|
|
16084
16109
|
...typeof entry.description === "string" ? { description: entry.description } : {}
|
|
16085
16110
|
};
|
|
16086
16111
|
continue;
|
|
@@ -16094,7 +16119,7 @@ function parseOpenCodeMcp(content) {
|
|
|
16094
16119
|
type: "stdio",
|
|
16095
16120
|
command,
|
|
16096
16121
|
args,
|
|
16097
|
-
env:
|
|
16122
|
+
env: toStringRecord3(entry.environment),
|
|
16098
16123
|
...typeof entry.description === "string" ? { description: entry.description } : {}
|
|
16099
16124
|
};
|
|
16100
16125
|
}
|
|
@@ -18916,7 +18941,7 @@ function toStringArray8(value) {
|
|
|
18916
18941
|
}
|
|
18917
18942
|
async function importWorkflows(projectRoot, results, normalize) {
|
|
18918
18943
|
const workflowsDir = join(projectRoot, WINDSURF_WORKFLOWS_DIR);
|
|
18919
|
-
const workflowFiles = await
|
|
18944
|
+
const workflowFiles = await readDirRecursiveNoSymlinks(workflowsDir);
|
|
18920
18945
|
const workflowMdFiles = workflowFiles.filter((f) => f.endsWith(".md"));
|
|
18921
18946
|
const destCommandsDir = join(projectRoot, WINDSURF_CANONICAL_COMMANDS_DIR);
|
|
18922
18947
|
for (const srcPath of workflowMdFiles) {
|
|
@@ -20758,8 +20783,22 @@ function resolveOutputCollisions(results) {
|
|
|
20758
20783
|
}
|
|
20759
20784
|
deduped[existingIdx] = mergeDuplicateMetadata(existing, result);
|
|
20760
20785
|
}
|
|
20786
|
+
assertNoCaseOnlyPathCollisions(deduped);
|
|
20761
20787
|
return deduped;
|
|
20762
20788
|
}
|
|
20789
|
+
function assertNoCaseOnlyPathCollisions(results) {
|
|
20790
|
+
const byLower = /* @__PURE__ */ new Map();
|
|
20791
|
+
for (const result of results) {
|
|
20792
|
+
const key = result.path.toLowerCase();
|
|
20793
|
+
const prior = byLower.get(key);
|
|
20794
|
+
if (prior !== void 0 && prior.path !== result.path) {
|
|
20795
|
+
throw new Error(
|
|
20796
|
+
`Case-only path collision: "${prior.path}" (${prior.target}) and "${result.path}" (${result.target}) resolve to the same file on case-insensitive filesystems (Windows/macOS). Rename one canonical source.`
|
|
20797
|
+
);
|
|
20798
|
+
}
|
|
20799
|
+
byLower.set(key, result);
|
|
20800
|
+
}
|
|
20801
|
+
}
|
|
20763
20802
|
function refreshResultStatus(result) {
|
|
20764
20803
|
const status = result.currentContent === void 0 ? "created" : result.currentContent !== result.content ? "updated" : "unchanged";
|
|
20765
20804
|
return result.status === status ? result : { ...result, status };
|
|
@@ -21314,6 +21353,100 @@ function redactUrlSecrets(message) {
|
|
|
21314
21353
|
}
|
|
21315
21354
|
);
|
|
21316
21355
|
}
|
|
21356
|
+
function gitProtocolOptIns() {
|
|
21357
|
+
const on = (v) => v === "1" || v === "true";
|
|
21358
|
+
return {
|
|
21359
|
+
http: on(process.env.AGENTSMESH_ALLOW_INSECURE_GIT),
|
|
21360
|
+
file: on(process.env.AGENTSMESH_ALLOW_LOCAL_GIT)
|
|
21361
|
+
};
|
|
21362
|
+
}
|
|
21363
|
+
function isAllowedGitProtocol(url) {
|
|
21364
|
+
let parsed;
|
|
21365
|
+
try {
|
|
21366
|
+
parsed = new URL(url);
|
|
21367
|
+
} catch {
|
|
21368
|
+
return false;
|
|
21369
|
+
}
|
|
21370
|
+
const { http, file } = gitProtocolOptIns();
|
|
21371
|
+
const allowed = ["https:", "ssh:"];
|
|
21372
|
+
if (http) allowed.push("http:");
|
|
21373
|
+
if (file) allowed.push("file:");
|
|
21374
|
+
return allowed.includes(parsed.protocol);
|
|
21375
|
+
}
|
|
21376
|
+
function gitAllowProtocolEnv() {
|
|
21377
|
+
const { http, file } = gitProtocolOptIns();
|
|
21378
|
+
const protos = ["https", "ssh"];
|
|
21379
|
+
if (http) protos.push("http");
|
|
21380
|
+
if (file) protos.push("file");
|
|
21381
|
+
return protos.join(":");
|
|
21382
|
+
}
|
|
21383
|
+
function assertAllowedGitUrl(url) {
|
|
21384
|
+
if (isAllowedGitProtocol(url)) return;
|
|
21385
|
+
throw new Error(
|
|
21386
|
+
`agentsmesh refuses a git remote with a disallowed transport: "${redactUrlSecrets(url)}". Allowed: https, ssh. Set AGENTSMESH_ALLOW_INSECURE_GIT=1 to permit http, AGENTSMESH_ALLOW_LOCAL_GIT=1 to permit file.`
|
|
21387
|
+
);
|
|
21388
|
+
}
|
|
21389
|
+
function splitSourceRef(source, prefix, defaultRef) {
|
|
21390
|
+
if (!source.startsWith(prefix)) return null;
|
|
21391
|
+
const rest = source.slice(prefix.length).trim();
|
|
21392
|
+
if (!rest) return null;
|
|
21393
|
+
const refIdx = rest.lastIndexOf("@");
|
|
21394
|
+
if (refIdx < 0) return [rest, defaultRef];
|
|
21395
|
+
const slug = rest.slice(0, refIdx).trim();
|
|
21396
|
+
const ref = rest.slice(refIdx + 1).trim();
|
|
21397
|
+
if (!slug || !ref) return null;
|
|
21398
|
+
return [slug, ref];
|
|
21399
|
+
}
|
|
21400
|
+
function parseGithubSource(source) {
|
|
21401
|
+
const parts = splitSourceRef(source, "github:", "latest");
|
|
21402
|
+
if (!parts) return null;
|
|
21403
|
+
const [slug, tag] = parts;
|
|
21404
|
+
const slash = slug.indexOf("/");
|
|
21405
|
+
if (slash < 0) return null;
|
|
21406
|
+
const org = slug.slice(0, slash).trim();
|
|
21407
|
+
const repo = slug.slice(slash + 1).trim();
|
|
21408
|
+
if (!org || !repo || !tag) return null;
|
|
21409
|
+
return { org, repo, tag };
|
|
21410
|
+
}
|
|
21411
|
+
function parseGitlabSource(source) {
|
|
21412
|
+
const parts = splitSourceRef(source, "gitlab:");
|
|
21413
|
+
if (!parts) return null;
|
|
21414
|
+
const [slug, ref] = parts;
|
|
21415
|
+
const slash = slug.lastIndexOf("/");
|
|
21416
|
+
if (slash < 0) return null;
|
|
21417
|
+
const namespace = slug.slice(0, slash).trim();
|
|
21418
|
+
const project31 = slug.slice(slash + 1).trim();
|
|
21419
|
+
if (!namespace || !project31) return null;
|
|
21420
|
+
return {
|
|
21421
|
+
namespace,
|
|
21422
|
+
project: project31,
|
|
21423
|
+
ref,
|
|
21424
|
+
cloneUrl: `https://gitlab.com/${namespace}/${project31}.git`
|
|
21425
|
+
};
|
|
21426
|
+
}
|
|
21427
|
+
function parseGitSource(source) {
|
|
21428
|
+
if (!source.startsWith("git+")) return null;
|
|
21429
|
+
const rest = source.slice(4).trim();
|
|
21430
|
+
if (!rest) return null;
|
|
21431
|
+
const hashIdx = rest.lastIndexOf("#");
|
|
21432
|
+
const url = (hashIdx < 0 ? rest : rest.slice(0, hashIdx)).trim();
|
|
21433
|
+
const ref = hashIdx < 0 ? void 0 : rest.slice(hashIdx + 1).trim();
|
|
21434
|
+
if (!url || hashIdx >= 0 && !ref) return null;
|
|
21435
|
+
if (!isAllowedGitProtocol(url)) return null;
|
|
21436
|
+
return { url, ref };
|
|
21437
|
+
}
|
|
21438
|
+
function parseRemoteSource(source) {
|
|
21439
|
+
const github = parseGithubSource(source);
|
|
21440
|
+
if (github) return { kind: "github", ...github };
|
|
21441
|
+
const gitlab = parseGitlabSource(source);
|
|
21442
|
+
if (gitlab) return { kind: "gitlab", ...gitlab };
|
|
21443
|
+
const git = parseGitSource(source);
|
|
21444
|
+
if (git) return { kind: "git", ...git };
|
|
21445
|
+
return null;
|
|
21446
|
+
}
|
|
21447
|
+
function isSupportedRemoteSource(source) {
|
|
21448
|
+
return parseRemoteSource(source) !== null;
|
|
21449
|
+
}
|
|
21317
21450
|
|
|
21318
21451
|
// src/config/remote/git-remote.ts
|
|
21319
21452
|
var execFileAsync = promisify(execFile);
|
|
@@ -21384,7 +21517,8 @@ function resolveCloneUrl(parsed) {
|
|
|
21384
21517
|
}
|
|
21385
21518
|
async function cloneRepo(cloneUrl, repoDir) {
|
|
21386
21519
|
ensureNotFlag(cloneUrl, "clone-url");
|
|
21387
|
-
|
|
21520
|
+
assertAllowedGitUrl(cloneUrl);
|
|
21521
|
+
await runGit(["clone", "-c", "core.symlinks=false", cloneUrl, repoDir]);
|
|
21388
21522
|
}
|
|
21389
21523
|
async function checkoutRef(repoDir, ref) {
|
|
21390
21524
|
ensureNotFlag(ref, "ref");
|
|
@@ -21398,7 +21532,10 @@ async function runGit(args, cwd) {
|
|
|
21398
21532
|
cwd,
|
|
21399
21533
|
env: {
|
|
21400
21534
|
...process.env,
|
|
21401
|
-
GIT_TERMINAL_PROMPT: "0"
|
|
21535
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
21536
|
+
// Constrain transports git may use (incl. mid-clone redirects/insteadOf)
|
|
21537
|
+
// to the same allowlist the clone URL itself passed.
|
|
21538
|
+
GIT_ALLOW_PROTOCOL: gitAllowProtocolEnv()
|
|
21402
21539
|
}
|
|
21403
21540
|
});
|
|
21404
21541
|
return stdout.trim();
|
|
@@ -21586,80 +21723,6 @@ async function fetchGithubDefaultBranch(parsed, extendName, options, cacheDir, b
|
|
|
21586
21723
|
}
|
|
21587
21724
|
throw lastError instanceof Error ? lastError : new Error("Failed to clone GitHub default branch");
|
|
21588
21725
|
}
|
|
21589
|
-
function splitSourceRef(source, prefix, defaultRef) {
|
|
21590
|
-
if (!source.startsWith(prefix)) return null;
|
|
21591
|
-
const rest = source.slice(prefix.length).trim();
|
|
21592
|
-
if (!rest) return null;
|
|
21593
|
-
const refIdx = rest.lastIndexOf("@");
|
|
21594
|
-
if (refIdx < 0) return [rest, defaultRef];
|
|
21595
|
-
const slug = rest.slice(0, refIdx).trim();
|
|
21596
|
-
const ref = rest.slice(refIdx + 1).trim();
|
|
21597
|
-
if (!slug || !ref) return null;
|
|
21598
|
-
return [slug, ref];
|
|
21599
|
-
}
|
|
21600
|
-
function parseGithubSource(source) {
|
|
21601
|
-
const parts = splitSourceRef(source, "github:", "latest");
|
|
21602
|
-
if (!parts) return null;
|
|
21603
|
-
const [slug, tag] = parts;
|
|
21604
|
-
const slash = slug.indexOf("/");
|
|
21605
|
-
if (slash < 0) return null;
|
|
21606
|
-
const org = slug.slice(0, slash).trim();
|
|
21607
|
-
const repo = slug.slice(slash + 1).trim();
|
|
21608
|
-
if (!org || !repo || !tag) return null;
|
|
21609
|
-
return { org, repo, tag };
|
|
21610
|
-
}
|
|
21611
|
-
function parseGitlabSource(source) {
|
|
21612
|
-
const parts = splitSourceRef(source, "gitlab:");
|
|
21613
|
-
if (!parts) return null;
|
|
21614
|
-
const [slug, ref] = parts;
|
|
21615
|
-
const slash = slug.lastIndexOf("/");
|
|
21616
|
-
if (slash < 0) return null;
|
|
21617
|
-
const namespace = slug.slice(0, slash).trim();
|
|
21618
|
-
const project31 = slug.slice(slash + 1).trim();
|
|
21619
|
-
if (!namespace || !project31) return null;
|
|
21620
|
-
return {
|
|
21621
|
-
namespace,
|
|
21622
|
-
project: project31,
|
|
21623
|
-
ref,
|
|
21624
|
-
cloneUrl: `https://gitlab.com/${namespace}/${project31}.git`
|
|
21625
|
-
};
|
|
21626
|
-
}
|
|
21627
|
-
function parseGitSource(source) {
|
|
21628
|
-
if (!source.startsWith("git+")) return null;
|
|
21629
|
-
const rest = source.slice(4).trim();
|
|
21630
|
-
if (!rest) return null;
|
|
21631
|
-
const hashIdx = rest.lastIndexOf("#");
|
|
21632
|
-
const url = (hashIdx < 0 ? rest : rest.slice(0, hashIdx)).trim();
|
|
21633
|
-
const ref = hashIdx < 0 ? void 0 : rest.slice(hashIdx + 1).trim();
|
|
21634
|
-
if (!url || hashIdx >= 0 && !ref) return null;
|
|
21635
|
-
let parsedUrl;
|
|
21636
|
-
try {
|
|
21637
|
-
parsedUrl = new URL(url);
|
|
21638
|
-
} catch {
|
|
21639
|
-
return null;
|
|
21640
|
-
}
|
|
21641
|
-
const allowInsecure = process.env.AGENTSMESH_ALLOW_INSECURE_GIT === "1" || process.env.AGENTSMESH_ALLOW_INSECURE_GIT === "true";
|
|
21642
|
-
const allowLocalGit = process.env.AGENTSMESH_ALLOW_LOCAL_GIT === "1" || process.env.AGENTSMESH_ALLOW_LOCAL_GIT === "true";
|
|
21643
|
-
const allowed = ["https:", "ssh:"];
|
|
21644
|
-
if (allowInsecure) allowed.push("http:");
|
|
21645
|
-
if (allowLocalGit) allowed.push("file:");
|
|
21646
|
-
if (!allowed.includes(parsedUrl.protocol)) {
|
|
21647
|
-
return null;
|
|
21648
|
-
}
|
|
21649
|
-
return { url, ref };
|
|
21650
|
-
}
|
|
21651
|
-
function parseRemoteSource(source) {
|
|
21652
|
-
const github = parseGithubSource(source);
|
|
21653
|
-
if (github) return { kind: "github", ...github };
|
|
21654
|
-
const gitlab = parseGitlabSource(source);
|
|
21655
|
-
if (gitlab) return { kind: "gitlab", ...gitlab };
|
|
21656
|
-
const git = parseGitSource(source);
|
|
21657
|
-
if (git) return { kind: "git", ...git };
|
|
21658
|
-
return null;
|
|
21659
|
-
}
|
|
21660
|
-
function isSupportedRemoteSource(source) {
|
|
21661
|
-
return parseRemoteSource(source) !== null;
|
|
21662
|
-
}
|
|
21663
21726
|
async function sweepStaleCache(cacheDir, maxAgeMs) {
|
|
21664
21727
|
const dir = cacheDir ?? getCacheDir();
|
|
21665
21728
|
const threshold = Number(process.env.AGENTSMESH_CACHE_MAX_AGE_DAYS ?? 30) * 864e5;
|
|
@@ -21893,15 +21956,17 @@ function assertNoBasenameCollisions(feature, paths, stripExt) {
|
|
|
21893
21956
|
const idx = Math.max(fwdIdx, bckIdx);
|
|
21894
21957
|
const base = idx === -1 ? p : p.slice(idx + 1);
|
|
21895
21958
|
const slug = base.endsWith(stripExt) ? base.slice(0, -stripExt.length) : base;
|
|
21896
|
-
const
|
|
21897
|
-
|
|
21959
|
+
const key = slug.toLowerCase();
|
|
21960
|
+
const prior = seen.get(key);
|
|
21961
|
+
if (prior !== void 0 && prior.path !== p) {
|
|
21962
|
+
const detail = prior.slug === slug ? `"${slug}"` : `"${prior.slug}" vs "${slug}" (case-insensitive)`;
|
|
21898
21963
|
throw new CanonicalNameError(
|
|
21899
21964
|
feature,
|
|
21900
21965
|
slug,
|
|
21901
|
-
`canonical ${feature} files collide on slug
|
|
21966
|
+
`canonical ${feature} files collide on slug ${detail}: ${prior.path} vs ${p}. Rename one.`
|
|
21902
21967
|
);
|
|
21903
21968
|
}
|
|
21904
|
-
seen.set(
|
|
21969
|
+
seen.set(key, { path: p, slug });
|
|
21905
21970
|
}
|
|
21906
21971
|
}
|
|
21907
21972
|
var ALTERNATE_RESOURCE_FORMATS = /* @__PURE__ */ new Set([".toml", ".yaml", ".yml", ".json"]);
|
|
@@ -21935,7 +22000,7 @@ function toStrArray(v) {
|
|
|
21935
22000
|
return [];
|
|
21936
22001
|
}
|
|
21937
22002
|
async function parseRules(rulesDir, opts = {}) {
|
|
21938
|
-
const files = await
|
|
22003
|
+
const files = await readDirRecursiveNoSymlinks(rulesDir);
|
|
21939
22004
|
const mdFiles = files.filter((f) => {
|
|
21940
22005
|
if (!f.endsWith(".md")) return false;
|
|
21941
22006
|
const name = basename(f, ".md");
|
|
@@ -21944,6 +22009,7 @@ async function parseRules(rulesDir, opts = {}) {
|
|
|
21944
22009
|
warnIfUnrecognizedResourceFormats("rules", rulesDir, files, mdFiles, {
|
|
21945
22010
|
handledByOtherReader: opts.handledByOtherReader
|
|
21946
22011
|
});
|
|
22012
|
+
assertNoBasenameCollisions("rule", mdFiles, ".md");
|
|
21947
22013
|
const rules = [];
|
|
21948
22014
|
for (const path of mdFiles) {
|
|
21949
22015
|
const content = await readFileSafe(path);
|
|
@@ -21992,7 +22058,7 @@ function toToolsArray2(v) {
|
|
|
21992
22058
|
return [];
|
|
21993
22059
|
}
|
|
21994
22060
|
async function parseCommands(commandsDir, opts = {}) {
|
|
21995
|
-
const files = await
|
|
22061
|
+
const files = await readDirRecursiveNoSymlinks(commandsDir);
|
|
21996
22062
|
const mdFiles = files.filter((f) => f.endsWith(".md") && !basename(f).startsWith("_"));
|
|
21997
22063
|
warnIfUnrecognizedResourceFormats("commands", commandsDir, files, mdFiles, {
|
|
21998
22064
|
handledByOtherReader: opts.handledByOtherReader
|
|
@@ -22052,7 +22118,7 @@ function toHooks2(v) {
|
|
|
22052
22118
|
return {};
|
|
22053
22119
|
}
|
|
22054
22120
|
async function parseAgents(agentsDir, opts = {}) {
|
|
22055
|
-
const files = await
|
|
22121
|
+
const files = await readDirRecursiveNoSymlinks(agentsDir);
|
|
22056
22122
|
const mdFiles = files.filter((f) => f.endsWith(".md") && !basename(f).startsWith("_"));
|
|
22057
22123
|
warnIfUnrecognizedResourceFormats("agents", agentsDir, files, mdFiles, {
|
|
22058
22124
|
handledByOtherReader: opts.handledByOtherReader
|
|
@@ -22636,7 +22702,7 @@ async function importEntities(kind, dir, opts) {
|
|
|
22636
22702
|
}
|
|
22637
22703
|
async function readToolNativeEntities(srcDir, targetId, kind, parseOpts = {}) {
|
|
22638
22704
|
const specs = directorySpecsFor(getDescriptor(targetId)?.importer, kind);
|
|
22639
|
-
const allFiles = await
|
|
22705
|
+
const allFiles = await readDirRecursiveNoSymlinks(srcDir);
|
|
22640
22706
|
const nonMdExtensions = /* @__PURE__ */ new Set();
|
|
22641
22707
|
for (const spec of specs) {
|
|
22642
22708
|
for (const ext of spec.extensions) {
|
|
@@ -22993,7 +23059,7 @@ async function stageSingleFile(sourcePath, destinationDir, acceptMdc) {
|
|
|
22993
23059
|
async function stageMarkdownCollection(sourceRoot, destinationDir, acceptMdc) {
|
|
22994
23060
|
const info = await stat(sourceRoot);
|
|
22995
23061
|
if (info.isFile()) return stageSingleFile(sourceRoot, destinationDir, acceptMdc);
|
|
22996
|
-
const files = (await
|
|
23062
|
+
const files = (await readDirRecursiveNoSymlinks(sourceRoot)).filter(
|
|
22997
23063
|
(file) => isAcceptedFile(file, acceptMdc) && !isBoilerplate(basename(file))
|
|
22998
23064
|
);
|
|
22999
23065
|
if (files.length === 0) {
|
|
@@ -23040,7 +23106,7 @@ async function stagePreferredSkills(sourceRoot, destinationDir, preferredSkillNa
|
|
|
23040
23106
|
}
|
|
23041
23107
|
const wanted = new Set(preferredSkillNames);
|
|
23042
23108
|
const matches = /* @__PURE__ */ new Map();
|
|
23043
|
-
for (const file of await
|
|
23109
|
+
for (const file of await readDirRecursiveNoSymlinks(sourceRoot)) {
|
|
23044
23110
|
if (!file.endsWith("/SKILL.md") && !file.endsWith("\\SKILL.md")) continue;
|
|
23045
23111
|
const skillDir = dirname(file);
|
|
23046
23112
|
const skillName = basename(skillDir);
|
|
@@ -23088,7 +23154,7 @@ async function stageSkills(sourceRoot, destinationDir, options = {}) {
|
|
|
23088
23154
|
return;
|
|
23089
23155
|
}
|
|
23090
23156
|
await mkdirp(destinationDir);
|
|
23091
|
-
const entries = await
|
|
23157
|
+
const entries = await readDirRecursiveNoSymlinks(sourceRoot);
|
|
23092
23158
|
const roots = /* @__PURE__ */ new Set();
|
|
23093
23159
|
for (const file of entries.filter(
|
|
23094
23160
|
(entry) => entry.endsWith("/SKILL.md") || entry.endsWith("\\SKILL.md")
|