agentsmesh 0.19.1 → 0.21.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/dist/engine.js CHANGED
@@ -768,6 +768,33 @@ async function readDirRecursive(dir, visited, branchSegments) {
768
768
  );
769
769
  }
770
770
  }
771
+ async function readDirRecursiveNoSymlinks(dir, branchSegments) {
772
+ const currentBranchSegments = branchSegments ?? [basename(dir)];
773
+ try {
774
+ const entries = await readdir(dir, { withFileTypes: true });
775
+ const files = [];
776
+ for (const ent of entries) {
777
+ if (ent.isSymbolicLink()) continue;
778
+ const full = join(dir, ent.name);
779
+ if (ent.isDirectory()) {
780
+ const nextSegments = [...currentBranchSegments, ent.name];
781
+ if (shouldSkipRecursiveBranch(nextSegments)) continue;
782
+ files.push(...await readDirRecursiveNoSymlinks(full, nextSegments));
783
+ } else if (ent.isFile()) {
784
+ files.push(full);
785
+ }
786
+ }
787
+ return files;
788
+ } catch (err) {
789
+ const e = err;
790
+ if (e.code === "ENOENT" || e.code === "ENOTDIR" || e.code === "EACCES") return [];
791
+ throw new FileSystemError(
792
+ dir,
793
+ `Failed to read directory ${dir}: ${e.message}. Check permissions.`,
794
+ { cause: err, errnoCode: e.code }
795
+ );
796
+ }
797
+ }
771
798
  var MAX_RECURSIVE_DEPTH, MAX_SEGMENT_REPETITIONS;
772
799
  var init_fs_traverse = __esm({
773
800
  "src/utils/filesystem/fs-traverse.ts"() {
@@ -1022,7 +1049,7 @@ ${legacy}`, "");
1022
1049
  }
1023
1050
  return result.trim();
1024
1051
  }
1025
- var ROOT_INSTRUCTION_BODY_V1, ROOT_INSTRUCTION_BODY_V2, ROOT_INSTRUCTION_BODY_V3, ROOT_INSTRUCTION_BODY_V4, ROOT_INSTRUCTION_BODY_V5, ROOT_INSTRUCTION_BODY_V6, ROOT_INSTRUCTION_BODY_V7, ROOT_INSTRUCTION_BODY, LEGACY_AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH, LEGACY_AGENTSMESH_ROOT_INSTRUCTION_SECTION, AGENTSMESH_CONTRACT_WITH_V1_BODY, AGENTSMESH_CONTRACT_WITH_V2_BODY, AGENTSMESH_CONTRACT_WITH_V3_BODY, AGENTSMESH_CONTRACT_WITH_V4_BODY, AGENTSMESH_CONTRACT_WITH_V5_BODY, AGENTSMESH_CONTRACT_WITH_V6_BODY, AGENTSMESH_CONTRACT_WITH_V7_BODY, AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH, LEGACY_FORMS;
1052
+ var ROOT_INSTRUCTION_BODY_V1, ROOT_INSTRUCTION_BODY_V2, ROOT_INSTRUCTION_BODY_V3, ROOT_INSTRUCTION_BODY_V4, ROOT_INSTRUCTION_BODY_V5, ROOT_INSTRUCTION_BODY_V6, ROOT_INSTRUCTION_BODY_V7, ROOT_INSTRUCTION_BODY_V8, ROOT_INSTRUCTION_BODY, LEGACY_AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH, LEGACY_AGENTSMESH_ROOT_INSTRUCTION_SECTION, AGENTSMESH_CONTRACT_WITH_V1_BODY, AGENTSMESH_CONTRACT_WITH_V2_BODY, AGENTSMESH_CONTRACT_WITH_V3_BODY, AGENTSMESH_CONTRACT_WITH_V4_BODY, AGENTSMESH_CONTRACT_WITH_V5_BODY, AGENTSMESH_CONTRACT_WITH_V6_BODY, AGENTSMESH_CONTRACT_WITH_V7_BODY, AGENTSMESH_CONTRACT_WITH_V8_BODY, AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH, LEGACY_FORMS;
1026
1053
  var init_root_instruction_paragraph = __esm({
1027
1054
  "src/targets/projection/root-instruction-paragraph.ts"() {
1028
1055
  init_managed_blocks();
@@ -1033,7 +1060,8 @@ var init_root_instruction_paragraph = __esm({
1033
1060
  ROOT_INSTRUCTION_BODY_V5 = "Use Claude-style Markdown in `.agentsmesh`: `agents/*.md`, `commands/*.md`, and `skills/*/SKILL.md`; keep rules in `rules/*.md`, hooks in `hooks.yaml`, MCP in `mcp.json`, permissions in `permissions.yaml`, and ignore patterns in `ignore`, then run `agentsmesh generate`.";
1034
1061
  ROOT_INSTRUCTION_BODY_V6 = "Create canonical files in `.agentsmesh`: `rules/_root.md` and `rules/*.md` are Markdown rules; `commands/*.md`, `agents/*.md`, and `skills/*/SKILL.md` plus supporting files use Claude-style frontmatter Markdown; `mcp.json` is MCP JSON; `hooks.yaml` and `permissions.yaml` are YAML; `ignore` is gitignore-style text. Then run `agentsmesh generate`.";
1035
1062
  ROOT_INSTRUCTION_BODY_V7 = "`.agentsmesh` is the only folder you edit or add these files in: `rules/_root.md` and `rules/*.md` are Markdown rules; `commands/*.md`, `agents/*.md`, and `skills/*/SKILL.md` plus supporting files use Claude-style frontmatter Markdown; `mcp.json` is MCP JSON; `hooks.yaml` and `permissions.yaml` are YAML; `ignore` is gitignore-style text. Do not edit generated tool files; run `agentsmesh generate`.";
1036
- ROOT_INSTRUCTION_BODY = "`agentsmesh.yaml` selects targets/features (`agentsmesh.local.yaml` overrides locally), and `.agentsmesh` is the only place to add or edit canonical items: `rules/_root.md`, `rules/*.md`, `commands/*.md`, `agents/*.md`, `skills/*/SKILL.md` plus supporting files, `mcp.json`, `hooks.yaml`, `permissions.yaml`, and `ignore`; if missing run `agentsmesh init`, use `agentsmesh import --from <tool>` for native configs, `agentsmesh install <source>` or `install --sync` for reusable packs, then run `agentsmesh generate`. Use `diff`, `lint`, `check`, `watch`, `matrix`, and `merge` as needed; never edit generated tool files.";
1063
+ ROOT_INSTRUCTION_BODY_V8 = "`agentsmesh.yaml` selects targets/features (`agentsmesh.local.yaml` overrides locally), and `.agentsmesh` is the only place to add or edit canonical items: `rules/_root.md`, `rules/*.md`, `commands/*.md`, `agents/*.md`, `skills/*/SKILL.md` plus supporting files, `mcp.json`, `hooks.yaml`, `permissions.yaml`, and `ignore`; if missing run `agentsmesh init`, use `agentsmesh import --from <tool>` for native configs, `agentsmesh install <source>` or `install --sync` for reusable packs, then run `agentsmesh generate`. Use `diff`, `lint`, `check`, `watch`, `matrix`, and `merge` as needed; never edit generated tool files.";
1064
+ ROOT_INSTRUCTION_BODY = "`agentsmesh.yaml` selects targets/features (`agentsmesh.local.yaml` overrides locally), and `.agentsmesh` is the only place to add or edit canonical items: `rules/_root.md`, `rules/*.md`, `commands/*.md`, `agents/*.md`, `skills/*/SKILL.md` plus supporting files, `mcp.json`, `hooks.yaml`, `permissions.yaml`, and `ignore`; if missing run `agentsmesh init`, use `agentsmesh import --from <tool>` for native configs, `agentsmesh install <source>` or `install --sync` for reusable packs, then run `agentsmesh generate`. Use `diff`, `lint`, `check`, `watch`, `matrix`, `merge`, and `refresh` as needed; never edit generated tool files.";
1037
1065
  LEGACY_AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH = ROOT_INSTRUCTION_BODY_V1;
1038
1066
  LEGACY_AGENTSMESH_ROOT_INSTRUCTION_SECTION = `## Project-Specific Rules
1039
1067
 
@@ -1059,12 +1087,16 @@ ${ROOT_INSTRUCTION_BODY_V6}`;
1059
1087
  AGENTSMESH_CONTRACT_WITH_V7_BODY = `## AgentsMesh Generation Contract
1060
1088
 
1061
1089
  ${ROOT_INSTRUCTION_BODY_V7}`;
1090
+ AGENTSMESH_CONTRACT_WITH_V8_BODY = `## AgentsMesh Generation Contract
1091
+
1092
+ ${ROOT_INSTRUCTION_BODY_V8}`;
1062
1093
  AGENTSMESH_ROOT_INSTRUCTION_PARAGRAPH = `${ROOT_CONTRACT_START}
1063
1094
  ## AgentsMesh Generation Contract
1064
1095
 
1065
1096
  ${ROOT_INSTRUCTION_BODY}
1066
1097
  ${ROOT_CONTRACT_END}`;
1067
1098
  LEGACY_FORMS = [
1099
+ AGENTSMESH_CONTRACT_WITH_V8_BODY,
1068
1100
  AGENTSMESH_CONTRACT_WITH_V7_BODY,
1069
1101
  AGENTSMESH_CONTRACT_WITH_V6_BODY,
1070
1102
  AGENTSMESH_CONTRACT_WITH_V5_BODY,
@@ -20142,6 +20174,19 @@ init_fs();
20142
20174
 
20143
20175
  // src/config/remote/git-remote.ts
20144
20176
  init_fs();
20177
+
20178
+ // src/utils/output/redact-url-secrets.ts
20179
+ var URL_WITH_CREDENTIALS = /([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)([^/@\s"'<>]+)@([^\s"'<>]+)/g;
20180
+ function redactUrlSecrets(message) {
20181
+ return message.replace(
20182
+ URL_WITH_CREDENTIALS,
20183
+ (_full, scheme, _userinfo, rest) => {
20184
+ return `${scheme}***@${rest}`;
20185
+ }
20186
+ );
20187
+ }
20188
+
20189
+ // src/config/remote/git-remote.ts
20145
20190
  var execFileAsync = promisify(execFile);
20146
20191
  var REPO_DIRNAME = "repo";
20147
20192
  function ensureNotFlag(value, kind) {
@@ -20175,12 +20220,13 @@ async function fetchGitRemoteExtend(parsed, extendName, options, cacheDir, build
20175
20220
  await rm(stagedRoot, { recursive: true, force: true });
20176
20221
  const allowFallback = options.allowOfflineFallback !== false;
20177
20222
  if (allowFallback && await hasCachedRepo(cacheRepoDir)) {
20223
+ const rawMsg = err instanceof Error ? err.message : String(err);
20178
20224
  console.warn(
20179
- `[agentsmesh] Remote fetch failed for ${extendName}; using cached version. Error: ${err instanceof Error ? err.message : String(err)}`
20225
+ `[agentsmesh] Remote fetch failed for ${extendName}; using cached version. Error: ${redactUrlSecrets(rawMsg)}`
20180
20226
  );
20181
20227
  return readCachedRepo(cacheRepoDir);
20182
20228
  }
20183
- throw err;
20229
+ throw err instanceof Error ? Object.assign(new Error(redactUrlSecrets(err.message)), { cause: err.cause }) : err;
20184
20230
  }
20185
20231
  }
20186
20232
  async function readCachedRepo(repoDir) {
@@ -20341,13 +20387,14 @@ async function fetchGithubRemoteExtend(parsed, extendName, options, cacheDir, bu
20341
20387
  if (allowFallback && await exists(extractDir)) {
20342
20388
  const topDir2 = await findExtractTopDir(extractDir);
20343
20389
  if (topDir2) {
20390
+ const rawMsg = err instanceof Error ? err.message : String(err);
20344
20391
  console.warn(
20345
- `[agentsmesh] Network failed for ${extendName}; using cached version. Error: ${err instanceof Error ? err.message : String(err)}`
20392
+ `[agentsmesh] Network failed for ${extendName}; using cached version. Error: ${redactUrlSecrets(rawMsg)}`
20346
20393
  );
20347
20394
  return { resolvedPath: join(extractDir, topDir2), version: tag };
20348
20395
  }
20349
20396
  }
20350
- throw err;
20397
+ throw err instanceof Error ? Object.assign(new Error(redactUrlSecrets(err.message)), { cause: err.cause }) : err;
20351
20398
  }
20352
20399
  await rm(extractDir, { recursive: true, force: true });
20353
20400
  await mkdir(extractDir, { recursive: true });
@@ -20358,12 +20405,14 @@ async function fetchGithubRemoteExtend(parsed, extendName, options, cacheDir, bu
20358
20405
  file: tarPath,
20359
20406
  cwd: extractDir,
20360
20407
  strict: true,
20408
+ // Allowlist entry types instead of denylist: only `File` and `Directory`
20409
+ // can be extracted. Hardlinks (`Link`), symlinks (`SymbolicLink`), FIFOs,
20410
+ // character/block devices, and any future/exotic tar entry type are
20411
+ // rejected. A denylist would silently let an unknown variant through.
20361
20412
  filter: (entryPath, entry) => {
20362
20413
  if (isZipSlipPath(entryPath)) return false;
20363
- if (entry && "type" in entry && (entry.type === "Link" || entry.type === "SymbolicLink")) {
20364
- return false;
20365
- }
20366
- return true;
20414
+ const type = entry && "type" in entry ? entry.type : void 0;
20415
+ return type === "File" || type === "Directory";
20367
20416
  }
20368
20417
  });
20369
20418
  } finally {
@@ -20461,8 +20510,11 @@ function parseGitSource(source) {
20461
20510
  return null;
20462
20511
  }
20463
20512
  const allowInsecure = process.env.AGENTSMESH_ALLOW_INSECURE_GIT === "1" || process.env.AGENTSMESH_ALLOW_INSECURE_GIT === "true";
20464
- const allowedProtocols = allowInsecure ? ["https:", "http:", "ssh:", "file:"] : ["https:", "ssh:", "file:"];
20465
- if (!allowedProtocols.includes(parsedUrl.protocol)) {
20513
+ const allowLocalGit = process.env.AGENTSMESH_ALLOW_LOCAL_GIT === "1" || process.env.AGENTSMESH_ALLOW_LOCAL_GIT === "true";
20514
+ const allowed = ["https:", "ssh:"];
20515
+ if (allowInsecure) allowed.push("http:");
20516
+ if (allowLocalGit) allowed.push("file:");
20517
+ if (!allowed.includes(parsedUrl.protocol)) {
20466
20518
  return null;
20467
20519
  }
20468
20520
  return { url, ref };
@@ -20921,6 +20973,7 @@ async function parseAgents(agentsDir, opts = {}) {
20921
20973
 
20922
20974
  // src/canonical/features/skills.ts
20923
20975
  init_fs();
20976
+ init_fs_traverse();
20924
20977
  init_markdown();
20925
20978
  init_boilerplate_filter();
20926
20979
  async function readContent(path) {
@@ -20939,7 +20992,7 @@ function sanitizeSkillName(raw) {
20939
20992
  return raw.toLowerCase().replace(/[^a-z0-9-]+/g, "-").replace(/-+/g, "-").replace(/^-|-$/g, "");
20940
20993
  }
20941
20994
  async function listSupportingFiles(skillDir) {
20942
- const files = await readDirRecursive(skillDir);
20995
+ const files = await readDirRecursiveNoSymlinks(skillDir);
20943
20996
  const result = [];
20944
20997
  for (const absPath of files) {
20945
20998
  const raw = absPath.slice(skillDir.length + 1);