agentsmesh 0.41.0 → 0.42.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/index.js CHANGED
@@ -1,8 +1,8 @@
1
- import { readFileSync, existsSync, mkdirSync, writeFileSync, constants, rmSync, readdirSync, statSync, chmodSync, renameSync, accessSync, realpathSync, rmdirSync, unlinkSync } from 'fs';
2
- import { join, resolve, relative, sep, dirname, basename, extname, win32, posix } from 'path';
1
+ import { readFileSync, existsSync, mkdirSync, writeFileSync, constants, rmSync, realpathSync, readdirSync, statSync, chmodSync, renameSync, accessSync, lstatSync, rmdirSync, unlinkSync } from 'fs';
2
+ import { join, relative, sep, resolve, dirname, basename, extname, win32, posix } from 'path';
3
3
  import { stringify, parse, parseDocument, YAMLSeq, isMap, Document, isSeq, YAMLMap, isScalar, Scalar, Pair } from 'yaml';
4
4
  import { z } from 'zod';
5
- import { access, readdir, mkdir, readFile, rm, writeFile, lstat, open, stat, realpath, rmdir, unlink, rename, mkdtemp, cp } from 'fs/promises';
5
+ import { access, readdir, mkdir, readFile, lstat, rm, writeFile, open, stat, realpath, rmdir, unlink, rename, mkdtemp, cp } from 'fs/promises';
6
6
  import { setTimeout } from 'timers/promises';
7
7
  import { createHash, randomUUID } from 'crypto';
8
8
  import picomatch from 'picomatch';
@@ -1579,25 +1579,6 @@ var init_no_outputs = __esm({
1579
1579
  function escapeRegExp(value) {
1580
1580
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1581
1581
  }
1582
- function managedBlockPattern(start, end) {
1583
- return new RegExp(`${escapeRegExp(start)}[\\s\\S]*?${escapeRegExp(end)}`, "g");
1584
- }
1585
- function stripManagedBlock(content, start, end) {
1586
- return content.replace(managedBlockPattern(start, end), "").trim();
1587
- }
1588
- function splitFrontmatterPrefix(content) {
1589
- const split = splitFrontmatter(content);
1590
- return split === null ? { prefix: "", body: content.trim() } : { prefix: split.prefix, body: split.body };
1591
- }
1592
- function insertAtBodyTop(content, block) {
1593
- const { prefix, body } = splitFrontmatterPrefix(content);
1594
- const placed = body ? `${block}
1595
-
1596
- ${body}` : block;
1597
- return prefix ? `${prefix}
1598
-
1599
- ${placed}` : placed;
1600
- }
1601
1582
  function ruleSource(source) {
1602
1583
  const normalized = source.replace(/\\/g, "/");
1603
1584
  const meshIndex = normalized.lastIndexOf(".agentsmesh/");
@@ -1605,36 +1586,22 @@ function ruleSource(source) {
1605
1586
  if (normalized.startsWith("rules/")) return normalized;
1606
1587
  return join("rules", basename(normalized)).replace(/\\/g, "/");
1607
1588
  }
1608
- function markerForRule(rule) {
1609
- return {
1589
+ function renderEmbeddedRule(rule) {
1590
+ const marker = {
1610
1591
  source: ruleSource(rule.source),
1611
1592
  description: rule.description,
1612
1593
  globs: rule.globs,
1613
1594
  targets: rule.targets
1614
1595
  };
1615
- }
1616
- function embeddedRuleStart(rule) {
1617
- return `${EMBEDDED_RULE_START_PREFIX}${JSON.stringify(markerForRule(rule))}${EMBEDDED_RULE_START_SUFFIX}`;
1618
- }
1619
- function renderRule(rule) {
1620
- const parts = [embeddedRuleStart(rule)];
1596
+ const parts = [`${START_PREFIX}${JSON.stringify(marker)}${START_SUFFIX}`];
1621
1597
  if (rule.description.trim()) {
1622
1598
  parts.push(`## ${rule.description.trim()}`, "");
1623
1599
  }
1624
1600
  parts.push(rule.body.trim(), EMBEDDED_RULE_END);
1625
1601
  return parts.filter((part) => part.length > 0).join("\n");
1626
1602
  }
1627
- function renderEmbeddedRulesBlock(rules) {
1628
- if (rules.length === 0) return "";
1629
- return [EMBEDDED_RULES_START, ...rules.map(renderRule), EMBEDDED_RULES_END].join("\n");
1630
- }
1631
- function appendEmbeddedRulesBlock(content, rules) {
1632
- const block = renderEmbeddedRulesBlock(rules);
1633
- const withoutExisting = stripManagedBlock(content, EMBEDDED_RULES_START, EMBEDDED_RULES_END);
1634
- if (!block) return withoutExisting;
1635
- return withoutExisting ? `${withoutExisting}
1636
-
1637
- ${block}` : block;
1603
+ function renderEmbeddedRuleEntries(rules) {
1604
+ return rules.map(renderEmbeddedRule).join("\n\n");
1638
1605
  }
1639
1606
  function toStringArray2(value) {
1640
1607
  return Array.isArray(value) ? value.filter((entry) => typeof entry === "string") : [];
@@ -1661,26 +1628,67 @@ function stripGeneratedHeading(body, description) {
1661
1628
  const heading = `## ${description.trim()}`;
1662
1629
  return trimmed.startsWith(heading) ? trimmed.slice(heading.length).trim() : trimmed;
1663
1630
  }
1631
+ function takeEmbeddedRuleEntries(text) {
1632
+ const rules = [];
1633
+ const entry = new RegExp(
1634
+ `${escapeRegExp(START_PREFIX)}([\\s\\S]*?)${escapeRegExp(START_SUFFIX)}([\\s\\S]*?)${escapeRegExp(EMBEDDED_RULE_END)}`,
1635
+ "g"
1636
+ );
1637
+ const rest = text.replace(entry, (whole, markerText, body) => {
1638
+ const marker = parseMarker(markerText);
1639
+ if (!marker) return whole;
1640
+ rules.push({ ...marker, body: stripGeneratedHeading(body, marker.description) });
1641
+ return "";
1642
+ });
1643
+ return { rest: rest.trim(), rules };
1644
+ }
1645
+ var EMBEDDED_RULE_END, START_PREFIX, START_SUFFIX;
1646
+ var init_embedded_rule_entries = __esm({
1647
+ "src/targets/projection/embedded-rule-entries.ts"() {
1648
+ EMBEDDED_RULE_END = "<!-- agentsmesh:embedded-rule:end -->";
1649
+ START_PREFIX = "<!-- agentsmesh:embedded-rule:start ";
1650
+ START_SUFFIX = " -->";
1651
+ }
1652
+ });
1653
+
1654
+ // src/targets/projection/managed-blocks.ts
1655
+ function managedBlockPattern(start, end) {
1656
+ return new RegExp(`${escapeRegExp(start)}[\\s\\S]*?${escapeRegExp(end)}`, "g");
1657
+ }
1658
+ function stripManagedBlock(content, start, end) {
1659
+ return content.replace(managedBlockPattern(start, end), "").trim();
1660
+ }
1661
+ function splitFrontmatterPrefix(content) {
1662
+ const split = splitFrontmatter(content);
1663
+ return split === null ? { prefix: "", body: content.trim() } : { prefix: split.prefix, body: split.body };
1664
+ }
1665
+ function insertAtBodyTop(content, block) {
1666
+ const { prefix, body } = splitFrontmatterPrefix(content);
1667
+ const placed = body ? `${block}
1668
+
1669
+ ${body}` : block;
1670
+ return prefix ? `${prefix}
1671
+
1672
+ ${placed}` : placed;
1673
+ }
1674
+ function renderEmbeddedRulesBlock(rules) {
1675
+ if (rules.length === 0) return "";
1676
+ return [EMBEDDED_RULES_START, ...rules.map(renderEmbeddedRule), EMBEDDED_RULES_END].join("\n");
1677
+ }
1678
+ function appendEmbeddedRulesBlock(content, rules) {
1679
+ const block = renderEmbeddedRulesBlock(rules);
1680
+ const withoutExisting = stripManagedBlock(content, EMBEDDED_RULES_START, EMBEDDED_RULES_END);
1681
+ if (!block) return withoutExisting;
1682
+ return withoutExisting ? `${withoutExisting}
1683
+
1684
+ ${block}` : block;
1685
+ }
1664
1686
  function extractEmbeddedRules(content) {
1665
1687
  const rules = [];
1666
1688
  const outerPattern = managedBlockPattern(EMBEDDED_RULES_START, EMBEDDED_RULES_END);
1667
1689
  const rootContent = content.replace(outerPattern, (block) => {
1668
- const inner = block.replace(EMBEDDED_RULES_START, "").replace(EMBEDDED_RULES_END, "").trim();
1669
- const entryPattern = new RegExp(
1670
- `${escapeRegExp(EMBEDDED_RULE_START_PREFIX)}([\\s\\S]*?)${escapeRegExp(EMBEDDED_RULE_START_SUFFIX)}([\\s\\S]*?)${escapeRegExp(EMBEDDED_RULE_END)}`,
1671
- "g"
1672
- );
1673
- for (const match of inner.matchAll(entryPattern)) {
1674
- const markerText = match[1];
1675
- const body = match[2];
1676
- if (markerText === void 0 || body === void 0) continue;
1677
- const marker = parseMarker(markerText);
1678
- if (!marker) continue;
1679
- rules.push({
1680
- ...marker,
1681
- body: stripGeneratedHeading(body, marker.description)
1682
- });
1683
- }
1690
+ const inner = block.replace(EMBEDDED_RULES_START, "").replace(EMBEDDED_RULES_END, "");
1691
+ rules.push(...takeEmbeddedRuleEntries(inner).rules);
1684
1692
  return "";
1685
1693
  });
1686
1694
  return { rootContent: rootContent.trim(), rules };
@@ -1693,19 +1701,18 @@ function embeddedRootRule(canonical, target34, rootFile) {
1693
1701
  const content = appendEmbeddedRulesBlock(rootBody, nonRootRules);
1694
1702
  return content ? [{ path: rootFile, content }] : [];
1695
1703
  }
1696
- var ROOT_CONTRACT_START, ROOT_CONTRACT_END, LESSONS_CONTRACT_START, LESSONS_CONTRACT_END, EMBEDDED_RULES_START, EMBEDDED_RULES_END, EMBEDDED_RULE_END, EMBEDDED_RULE_START_PREFIX, EMBEDDED_RULE_START_SUFFIX;
1704
+ var ROOT_CONTRACT_START, ROOT_CONTRACT_END, LESSONS_CONTRACT_START, LESSONS_CONTRACT_END, EMBEDDED_RULES_START, EMBEDDED_RULES_END;
1697
1705
  var init_managed_blocks = __esm({
1698
1706
  "src/targets/projection/managed-blocks.ts"() {
1699
1707
  init_markdown();
1708
+ init_embedded_rule_entries();
1709
+ init_embedded_rule_entries();
1700
1710
  ROOT_CONTRACT_START = "<!-- agentsmesh:root-generation-contract:start -->";
1701
1711
  ROOT_CONTRACT_END = "<!-- agentsmesh:root-generation-contract:end -->";
1702
1712
  LESSONS_CONTRACT_START = "<!-- agentsmesh:lessons-contract:start -->";
1703
1713
  LESSONS_CONTRACT_END = "<!-- agentsmesh:lessons-contract:end -->";
1704
1714
  EMBEDDED_RULES_START = "<!-- agentsmesh:embedded-rules:start -->";
1705
1715
  EMBEDDED_RULES_END = "<!-- agentsmesh:embedded-rules:end -->";
1706
- EMBEDDED_RULE_END = "<!-- agentsmesh:embedded-rule:end -->";
1707
- EMBEDDED_RULE_START_PREFIX = "<!-- agentsmesh:embedded-rule:start ";
1708
- EMBEDDED_RULE_START_SUFFIX = " -->";
1709
1716
  }
1710
1717
  });
1711
1718
 
@@ -2997,6 +3004,8 @@ function rewriteFileLinks(input) {
2997
3004
  const lineNumSuffix = lineNumMatch ? lineNumMatch[0] : "";
2998
3005
  if (!rawCandidate) return match;
2999
3006
  const tokenContext = getTokenContext(fullContent, offset, offset + rawCandidate.length);
3007
+ if (input.markdownLinksOnly === true && tokenContext.role !== "markdown-link-dest")
3008
+ return match;
3000
3009
  const candidate = decodeLinkPath(rawCandidate, tokenContext.role);
3001
3010
  if (tokenContext.role !== "markdown-link-dest" && WINDOWS_ABSOLUTE_PATH.test(candidate)) {
3002
3011
  return match;
@@ -3171,13 +3180,12 @@ var init_import_rewriter = __esm({
3171
3180
  });
3172
3181
  async function writeMcpWithMerge(projectRoot, canonicalPath, imported) {
3173
3182
  const destPath = join(projectRoot, canonicalPath);
3174
- const existing = await readExistingServers(destPath);
3183
+ const existing = parseMcpServers(await readFileSafe(destPath));
3175
3184
  const merged = { ...existing, ...imported };
3176
3185
  await mkdirp(dirname(destPath));
3177
3186
  await writeFileAtomic(destPath, JSON.stringify({ mcpServers: merged }, null, 2));
3178
3187
  }
3179
- async function readExistingServers(path) {
3180
- const content = await readFileSafe(path);
3188
+ function parseMcpServers(content) {
3181
3189
  if (content === null) return {};
3182
3190
  let parsed;
3183
3191
  try {
@@ -7215,12 +7223,14 @@ function canonicalRulePath(source) {
7215
7223
  }
7216
7224
  async function splitEmbeddedRulesToCanonical(input) {
7217
7225
  const extracted = extractEmbeddedRules(input.content);
7226
+ const results = await writeEmbeddedRules(extracted.rules, input);
7227
+ return { rootContent: extracted.rootContent, results };
7228
+ }
7229
+ async function writeEmbeddedRules(rules, input) {
7218
7230
  const results = [];
7219
- if (extracted.rules.length === 0) {
7220
- return { rootContent: extracted.rootContent, results };
7221
- }
7231
+ if (rules.length === 0) return results;
7222
7232
  await mkdirp(join(input.projectRoot, input.rulesDir));
7223
- for (const rule of extracted.rules) {
7233
+ for (const rule of rules) {
7224
7234
  const canonicalSource = canonicalRulePath(rule.source);
7225
7235
  if (canonicalSource === null || canonicalSource === "rules/_root.md") continue;
7226
7236
  const destPath = join(input.projectRoot, ".agentsmesh", canonicalSource);
@@ -7230,6 +7240,7 @@ async function splitEmbeddedRulesToCanonical(input) {
7230
7240
  destPath,
7231
7241
  {
7232
7242
  ...frontmatter,
7243
+ ...input.frontmatter,
7233
7244
  root: false,
7234
7245
  description: rule.description || void 0,
7235
7246
  globs: rule.globs.length > 0 ? rule.globs : void 0,
@@ -7245,13 +7256,19 @@ async function splitEmbeddedRulesToCanonical(input) {
7245
7256
  feature: "rules"
7246
7257
  });
7247
7258
  }
7248
- return { rootContent: extracted.rootContent, results };
7259
+ return results;
7260
+ }
7261
+ async function splitNestedAgentsFile(input, into) {
7262
+ const { rest, rules } = takeEmbeddedRuleEntries(input.content);
7263
+ into.push(...await writeEmbeddedRules(rules, input));
7264
+ return rest.length > 0 ? rest : null;
7249
7265
  }
7250
7266
  var init_embedded_rules = __esm({
7251
7267
  "src/targets/import/embedded-rules.ts"() {
7252
7268
  init_fs();
7253
7269
  init_markdown();
7254
7270
  init_managed_blocks();
7271
+ init_embedded_rule_entries();
7255
7272
  init_import_metadata();
7256
7273
  }
7257
7274
  });
@@ -8679,7 +8696,7 @@ function generateRules6(canonical) {
8679
8696
  const slug = basename(rule.source, ".md");
8680
8697
  const frontmatter = {};
8681
8698
  if (rule.description) frontmatter.description = rule.description;
8682
- if (rule.globs.length > 0) frontmatter.globs = rule.globs;
8699
+ if (rule.globs.length > 0) frontmatter.paths = rule.globs;
8683
8700
  const content = serializeFrontmatter(frontmatter, rule.body.trim() || "");
8684
8701
  outputs.push({ path: `${CLAUDE_RULES_DIR}/${slug}.md`, content });
8685
8702
  }
@@ -9061,12 +9078,14 @@ var init_import_mappers2 = __esm({
9061
9078
  }) => {
9062
9079
  const destPath = join(destDir, relativePath);
9063
9080
  const { frontmatter, body } = parseFrontmatter(normalizeTo(destPath));
9081
+ const { paths: paths2, ...rest } = frontmatter;
9082
+ const scoped = paths2 === void 0 ? {} : { globs: toToolsArray(paths2) };
9064
9083
  return {
9065
9084
  destPath,
9066
9085
  toPath: `${AB_RULES}/${relativePath}`,
9067
9086
  content: await serializeImportedRuleWithFallback(
9068
9087
  destPath,
9069
- { ...frontmatter, root: false },
9088
+ { ...rest, ...scoped, root: false },
9070
9089
  body
9071
9090
  )
9072
9091
  };
@@ -10571,7 +10590,7 @@ function generateRules8(canonical) {
10571
10590
  const rootBody = canonical.rules.find((rule) => rule.root)?.body.trim() ?? "";
10572
10591
  if (rootBody) outputs.push({ path: CODEBUFF_ROOT_FILE, content: rootBody });
10573
10592
  for (const [path, rules] of groupByNestedPath(eligibleRules(canonical))) {
10574
- const content = rules.map((rule) => rule.body.trim()).filter((body) => body.length > 0).join("\n\n");
10593
+ const content = renderEmbeddedRuleEntries(rules.filter((rule) => rule.body.trim().length > 0));
10575
10594
  if (content) outputs.push({ path, content });
10576
10595
  }
10577
10596
  return outputs;
@@ -10599,6 +10618,7 @@ var init_generator8 = __esm({
10599
10618
  init_no_outputs();
10600
10619
  init_embedded_skill();
10601
10620
  init_managed_blocks();
10621
+ init_embedded_rule_entries();
10602
10622
  init_command_skill();
10603
10623
  init_nested_rules();
10604
10624
  init_mcp_format2();
@@ -10648,33 +10668,47 @@ function isVendored(relDir) {
10648
10668
  }
10649
10669
  async function importNestedRules(projectRoot, normalize) {
10650
10670
  const destDir = join(projectRoot, AB_RULES);
10651
- return importFileDirectory({
10671
+ const embedded = [];
10672
+ const results = await importFileDirectory({
10652
10673
  srcDir: projectRoot,
10653
10674
  destDir,
10654
10675
  extensions: [CODEBUFF_ROOT_FILE],
10655
10676
  fromTool: CODEBUFF_TARGET,
10656
10677
  normalize,
10657
- mapEntry: ({ srcPath, normalizeTo }) => {
10678
+ mapEntry: async ({ srcPath, content, normalizeTo }) => {
10658
10679
  if (basename(srcPath) !== CODEBUFF_ROOT_FILE) return null;
10659
10680
  const relDir = relative(projectRoot, dirname(srcPath)).replace(/\\/g, "/");
10660
10681
  if (!relDir || relDir === ".") return null;
10661
10682
  if (!shouldImportScopedAgentsRule(relDir)) return null;
10662
10683
  if (isVendored(relDir)) return null;
10684
+ const ownText = await splitNestedAgentsFile(
10685
+ {
10686
+ content,
10687
+ projectRoot,
10688
+ rulesDir: AB_RULES,
10689
+ sourcePath: srcPath,
10690
+ fromTool: CODEBUFF_TARGET,
10691
+ normalize
10692
+ },
10693
+ embedded
10694
+ );
10695
+ if (ownText === null) return null;
10663
10696
  const ruleName2 = relDir.replace(/\//g, "-");
10664
10697
  const destPath = join(destDir, `${ruleName2}.md`);
10665
- const { frontmatter, body } = parseFrontmatter(normalizeTo(destPath));
10666
- return serializeImportedRuleWithFallback(
10667
- destPath,
10668
- { ...frontmatter, root: false, globs: [`${relDir}/**`] },
10669
- body
10670
- ).then((content) => ({
10698
+ const { frontmatter, body } = parseFrontmatter(normalizeTo(destPath, ownText));
10699
+ return {
10671
10700
  destPath,
10672
10701
  toPath: `${AB_RULES}/${ruleName2}.md`,
10673
10702
  feature: "rules",
10674
- content
10675
- }));
10703
+ content: await serializeImportedRuleWithFallback(
10704
+ destPath,
10705
+ { ...frontmatter, root: false, globs: [`${relDir}/**`] },
10706
+ body
10707
+ )
10708
+ };
10676
10709
  }
10677
10710
  });
10711
+ return [...results, ...embedded];
10678
10712
  }
10679
10713
  async function importCodebuffRules(projectRoot, scope, normalize) {
10680
10714
  const results = await importRootRule2(projectRoot, scope, normalize);
@@ -11107,7 +11141,7 @@ function generateRules9(canonical) {
11107
11141
  }
11108
11142
  const nested = advisory.filter((rule) => !isRootEmbedded(rule));
11109
11143
  for (const [path, rules] of groupByNestedPath2(nested)) {
11110
- const content = rules.map((rule) => rule.body.trim()).filter((body) => body.length > 0).join("\n\n");
11144
+ const content = renderEmbeddedRuleEntries(rules.filter((rule) => rule.body.trim().length > 0));
11111
11145
  outputs.push({ path, content });
11112
11146
  }
11113
11147
  return outputs;
@@ -11120,6 +11154,7 @@ function renderCodexGlobalInstructions(canonical) {
11120
11154
  var init_rules = __esm({
11121
11155
  "src/targets/codex-cli/generator/rules.ts"() {
11122
11156
  init_managed_blocks();
11157
+ init_embedded_rule_entries();
11123
11158
  init_constants10();
11124
11159
  init_codex_rule_paths();
11125
11160
  }
@@ -11749,6 +11784,7 @@ async function importCodexRules(projectRoot, results, normalize, normalizeWindsu
11749
11784
  await importInstructionMirrors(projectRoot, destDir, results, normalize);
11750
11785
  results.push(...await importCodexNonRootRuleFiles(projectRoot, destDir, normalize));
11751
11786
  if (layoutScope !== "global") {
11787
+ const embedded = [];
11752
11788
  results.push(
11753
11789
  ...await importFileDirectory({
11754
11790
  srcDir: projectRoot,
@@ -11756,7 +11792,7 @@ async function importCodexRules(projectRoot, results, normalize, normalizeWindsu
11756
11792
  extensions: ["AGENTS.md", "AGENTS.override.md"],
11757
11793
  fromTool: "codex-cli",
11758
11794
  normalize,
11759
- mapEntry: async ({ srcPath, normalizeTo }) => {
11795
+ mapEntry: async ({ srcPath, content: content2, normalizeTo }) => {
11760
11796
  const relDir = relative(projectRoot, dirname(srcPath)).replace(/\\/g, "/");
11761
11797
  const fileName = basename(srcPath);
11762
11798
  const isOverride = fileName === "AGENTS.override.md";
@@ -11767,26 +11803,36 @@ async function importCodexRules(projectRoot, results, normalize, normalizeWindsu
11767
11803
  await removePathIfExists(join(destDir, `${ruleName2}.md`));
11768
11804
  return null;
11769
11805
  }
11806
+ const variant = isOverride ? { codex_instruction: "override" } : {};
11807
+ const ownText = await splitNestedAgentsFile(
11808
+ {
11809
+ content: content2,
11810
+ projectRoot,
11811
+ rulesDir: AB_RULES,
11812
+ sourcePath: srcPath,
11813
+ fromTool: "codex-cli",
11814
+ normalize,
11815
+ frontmatter: variant
11816
+ },
11817
+ embedded
11818
+ );
11819
+ if (ownText === null) return null;
11770
11820
  const destPath = join(destDir, `${ruleName2}.md`);
11771
- const { frontmatter, body } = parseFrontmatter(normalizeTo(destPath));
11821
+ const { frontmatter, body } = parseFrontmatter(normalizeTo(destPath, ownText));
11772
11822
  return {
11773
11823
  destPath,
11774
11824
  toPath: `${AB_RULES}/${ruleName2}.md`,
11775
11825
  feature: "rules",
11776
11826
  content: await serializeImportedRuleWithFallback(
11777
11827
  destPath,
11778
- {
11779
- ...frontmatter,
11780
- root: false,
11781
- globs: [`${relDir}/**`],
11782
- ...isOverride ? { codex_instruction: "override" } : {}
11783
- },
11828
+ { ...frontmatter, root: false, globs: [`${relDir}/**`], ...variant },
11784
11829
  body
11785
11830
  )
11786
11831
  };
11787
11832
  }
11788
11833
  })
11789
11834
  );
11835
+ results.push(...embedded);
11790
11836
  }
11791
11837
  }
11792
11838
  async function importInstructionMirrors(projectRoot, destDir, results, normalize) {
@@ -18410,7 +18456,7 @@ function parseExtensions(content) {
18410
18456
  }
18411
18457
  return out2;
18412
18458
  }
18413
- async function readExistingServers2(destPath) {
18459
+ async function readExistingServers(destPath) {
18414
18460
  const content = await readFileSafe(destPath);
18415
18461
  if (content === null) return {};
18416
18462
  let parsed;
@@ -18433,7 +18479,7 @@ async function gooseMcpMap(ctx) {
18433
18479
  const imported = ctx.relativePath.endsWith(".mcp.json") ? parsePluginMcpJson(ctx.content) : parseExtensions(ctx.content);
18434
18480
  if (Object.keys(imported).length === 0) return null;
18435
18481
  const destPath = join(ctx.destDir, "mcp.json");
18436
- const existing = await readExistingServers2(destPath);
18482
+ const existing = await readExistingServers(destPath);
18437
18483
  const merged = { ...existing, ...imported };
18438
18484
  return {
18439
18485
  destPath,
@@ -26988,12 +27034,6 @@ function ruleSlug3(source) {
26988
27034
  const name = basename(source, ".md");
26989
27035
  return name === "_root" ? "root" : name;
26990
27036
  }
26991
- function directoryScopedRuleDir(globs) {
26992
- if (globs.length === 0) return null;
26993
- const dirs = globs.map((glob) => glob.split("/")[0] ?? "").filter((segment) => /^[A-Za-z0-9._-]+$/.test(segment));
26994
- if (dirs.length !== globs.length) return null;
26995
- return dirs.every((dir) => dir === dirs[0]) ? dirs[0] : null;
26996
- }
26997
27037
  function generateRules32(canonical) {
26998
27038
  const outputs = [];
26999
27039
  const root = canonical.rules.find((r) => r.root);
@@ -27010,21 +27050,14 @@ function generateRules32(canonical) {
27010
27050
  const frontmatter = {
27011
27051
  description: rule.description || void 0,
27012
27052
  trigger: normalizedTrigger,
27013
- glob: rule.globs.length === 1 ? rule.globs[0] : void 0,
27014
- globs: rule.globs.length > 1 ? rule.globs : void 0
27053
+ // Windsurf reads `globs` as one comma-joined string; it ignores `glob`.
27054
+ globs: rule.globs.length > 0 ? rule.globs.join(",") : void 0
27015
27055
  };
27016
27056
  Object.keys(frontmatter).forEach((k) => {
27017
27057
  if (frontmatter[k] === void 0) delete frontmatter[k];
27018
27058
  });
27019
27059
  const content = Object.keys(frontmatter).length > 0 ? serializeFrontmatter(frontmatter, rule.body.trim() || "") : rule.body.trim() || "";
27020
27060
  outputs.push({ path: `${WINDSURF_RULES_DIR}/${slug}.md`, content });
27021
- const dir = directoryScopedRuleDir(rule.globs);
27022
- if (dir) {
27023
- if (dir !== slug) {
27024
- outputs.push({ path: `${WINDSURF_RULES_DIR}/${dir}.md`, content });
27025
- }
27026
- outputs.push({ path: `${dir}/AGENTS.md`, content: rule.body.trim() || "" });
27027
- }
27028
27061
  }
27029
27062
  return outputs;
27030
27063
  }
@@ -27224,6 +27257,185 @@ var init_generator36 = __esm({
27224
27257
  init_generator35();
27225
27258
  }
27226
27259
  });
27260
+
27261
+ // src/utils/output/color.ts
27262
+ function noColorRequested() {
27263
+ const value = process.env.NO_COLOR;
27264
+ return value !== void 0 && value !== "";
27265
+ }
27266
+ function forceColorRequested() {
27267
+ const value = process.env.FORCE_COLOR;
27268
+ if (value === void 0) return void 0;
27269
+ return value !== "0" && value !== "false";
27270
+ }
27271
+ function colorEnabled(stream = process.stdout) {
27272
+ const forced = forceColorRequested();
27273
+ if (forced !== void 0) return forced;
27274
+ if (noColorRequested()) return false;
27275
+ return stream.isTTY === true;
27276
+ }
27277
+ var init_color = __esm({
27278
+ "src/utils/output/color.ts"() {
27279
+ }
27280
+ });
27281
+
27282
+ // src/utils/output/logger.ts
27283
+ function outStream() {
27284
+ return stdoutRedirectedToStderr ? process.stderr : process.stdout;
27285
+ }
27286
+ function out(text) {
27287
+ outStream().write(text);
27288
+ }
27289
+ function c(code, text, stream) {
27290
+ return colorEnabled(stream) ? `${code}${text}${C.reset}` : text;
27291
+ }
27292
+ var C, muted, stdoutRedirectedToStderr, logger;
27293
+ var init_logger = __esm({
27294
+ "src/utils/output/logger.ts"() {
27295
+ init_color();
27296
+ C = {
27297
+ green: "\x1B[32m",
27298
+ red: "\x1B[31m",
27299
+ yellow: "\x1B[33m",
27300
+ cyan: "\x1B[36m",
27301
+ reset: "\x1B[0m"
27302
+ };
27303
+ muted = false;
27304
+ stdoutRedirectedToStderr = false;
27305
+ logger = {
27306
+ info(msg) {
27307
+ if (muted) return;
27308
+ out(c(C.cyan, msg, outStream()) + "\n");
27309
+ },
27310
+ warn(msg) {
27311
+ if (muted) return;
27312
+ process.stderr.write(c(C.yellow, "\u26A0 ", process.stderr) + msg + "\n");
27313
+ },
27314
+ error(msg) {
27315
+ if (muted) return;
27316
+ process.stderr.write(c(C.red, "\u2717 ", process.stderr) + msg + "\n");
27317
+ },
27318
+ success(msg) {
27319
+ if (muted) return;
27320
+ out(c(C.green, "\u2713 ", outStream()) + msg + "\n");
27321
+ },
27322
+ debug(msg) {
27323
+ if (muted) return;
27324
+ if (process.env.AGENTSMESH_DEBUG === "1") {
27325
+ out(c(C.cyan, "[debug] ", outStream()) + msg + "\n");
27326
+ }
27327
+ }
27328
+ };
27329
+ }
27330
+ });
27331
+
27332
+ // src/targets/windsurf/rule-globs.ts
27333
+ function splitTopLevelCommas(value) {
27334
+ const parts = [];
27335
+ let depth = 0;
27336
+ let current = "";
27337
+ for (const char of value) {
27338
+ if (char === "{") depth++;
27339
+ else if (char === "}") depth = Math.max(0, depth - 1);
27340
+ if (char === "," && depth === 0) {
27341
+ parts.push(current);
27342
+ current = "";
27343
+ } else {
27344
+ current += char;
27345
+ }
27346
+ }
27347
+ parts.push(current);
27348
+ return parts.map((part) => part.trim()).filter(Boolean);
27349
+ }
27350
+ function parseWindsurfGlobs(value) {
27351
+ if (typeof value === "string") return splitTopLevelCommas(value);
27352
+ return Array.isArray(value) ? toToolsArray(value) : [];
27353
+ }
27354
+ function quoteWindsurfGlobValues(content) {
27355
+ const lines = content.split("\n");
27356
+ if (lines[0]?.trim() !== "---") return content;
27357
+ for (let i = 1; i < lines.length; i++) {
27358
+ if (lines[i].trim() === "---") break;
27359
+ const match = UNQUOTED_GLOBS_LINE.exec(lines[i]);
27360
+ if (match !== null) lines[i] = `${match[1]}${JSON.stringify(match[2])}${match[3]}`;
27361
+ }
27362
+ return lines.join("\n");
27363
+ }
27364
+ var UNQUOTED_GLOBS_LINE;
27365
+ var init_rule_globs = __esm({
27366
+ "src/targets/windsurf/rule-globs.ts"() {
27367
+ init_shared_import_helpers();
27368
+ UNQUOTED_GLOBS_LINE = /^(\s*globs?\s*:[ \t]*)([^\s"'[{|>#][^\r\n]*?)[ \t]*(\r?)$/;
27369
+ }
27370
+ });
27371
+ async function windsurfRuleBodies(projectRoot) {
27372
+ const files = await readDirRecursiveNoSymlinks(join(projectRoot, WINDSURF_RULES_DIR));
27373
+ const bodies = /* @__PURE__ */ new Set();
27374
+ for (const file of files.filter((path) => path.endsWith(".md"))) {
27375
+ const content = await readFileSafe(file);
27376
+ if (content !== null) bodies.add(bodyKey(splitFrontmatter(content)?.body ?? content));
27377
+ }
27378
+ return bodies;
27379
+ }
27380
+ async function importWindsurfNestedAgents(projectRoot, normalize) {
27381
+ const destRulesDir = join(projectRoot, AB_RULES);
27382
+ const embedded = [];
27383
+ const ruleBodies = await windsurfRuleBodies(projectRoot);
27384
+ const results = await importFileDirectory({
27385
+ srcDir: projectRoot,
27386
+ destDir: destRulesDir,
27387
+ extensions: ["AGENTS.md"],
27388
+ fromTool: "windsurf",
27389
+ normalize,
27390
+ mapEntry: async ({ srcPath, content, normalizeTo }) => {
27391
+ const relDir = relative(projectRoot, dirname(srcPath)).replace(/\\/g, "/");
27392
+ if (!relDir || relDir === "." || basename(srcPath) !== "AGENTS.md") return null;
27393
+ const ruleName2 = relDir.replace(/\//g, "-");
27394
+ if (!shouldImportScopedAgentsRule(relDir)) {
27395
+ await removePathIfExists(join(destRulesDir, `${ruleName2}.md`));
27396
+ return null;
27397
+ }
27398
+ const ownText = await splitNestedAgentsFile(
27399
+ {
27400
+ content,
27401
+ projectRoot,
27402
+ rulesDir: AB_RULES,
27403
+ sourcePath: srcPath,
27404
+ fromTool: "windsurf",
27405
+ normalize
27406
+ },
27407
+ embedded
27408
+ );
27409
+ if (ownText === null || ruleBodies.has(bodyKey(ownText))) return null;
27410
+ const destPath = join(destRulesDir, `${ruleName2}.md`);
27411
+ return {
27412
+ destPath,
27413
+ toPath: `${AB_RULES}/${ruleName2}.md`,
27414
+ feature: "rules",
27415
+ content: await serializeImportedRuleWithFallback(
27416
+ destPath,
27417
+ { root: false, globs: [`${relDir}/**`] },
27418
+ normalizeTo(destPath, ownText)
27419
+ )
27420
+ };
27421
+ }
27422
+ });
27423
+ return [...results, ...embedded];
27424
+ }
27425
+ var bodyKey;
27426
+ var init_import_nested_agents = __esm({
27427
+ "src/targets/windsurf/import-nested-agents.ts"() {
27428
+ init_canonical_paths();
27429
+ init_embedded_rules();
27430
+ init_import_metadata();
27431
+ init_import_orchestrator();
27432
+ init_scoped_agents_import();
27433
+ init_fs();
27434
+ init_markdown();
27435
+ init_constants34();
27436
+ bodyKey = (text) => text.replace(/\r\n?/g, "\n").trim();
27437
+ }
27438
+ });
27227
27439
  function toStringArray3(value) {
27228
27440
  if (Array.isArray(value)) {
27229
27441
  return value.filter((entry) => typeof entry === "string").map((entry) => entry.trim()).filter(Boolean);
@@ -27492,35 +27704,7 @@ async function importFromWindsurf(projectRoot, options) {
27492
27704
  }
27493
27705
  }
27494
27706
  if (layoutScope !== "global") {
27495
- results.push(
27496
- ...await importFileDirectory({
27497
- srcDir: projectRoot,
27498
- destDir: destRulesDir,
27499
- extensions: ["AGENTS.md"],
27500
- fromTool: "windsurf",
27501
- normalize,
27502
- mapEntry: async ({ srcPath, normalizeTo }) => {
27503
- const relDir = relative(projectRoot, dirname(srcPath)).replace(/\\/g, "/");
27504
- if (!relDir || relDir === "." || basename(srcPath) !== "AGENTS.md") return null;
27505
- const ruleName2 = relDir.replace(/\//g, "-");
27506
- if (!shouldImportScopedAgentsRule(relDir)) {
27507
- await removePathIfExists(join(destRulesDir, `${ruleName2}.md`));
27508
- return null;
27509
- }
27510
- const destPath = join(destRulesDir, `${ruleName2}.md`);
27511
- return {
27512
- destPath,
27513
- toPath: `${AB_RULES}/${ruleName2}.md`,
27514
- feature: "rules",
27515
- content: await serializeImportedRuleWithFallback(
27516
- destPath,
27517
- { root: false, globs: [`${relDir}/**`] },
27518
- normalizeTo(destPath)
27519
- )
27520
- };
27521
- }
27522
- })
27523
- );
27707
+ results.push(...await importWindsurfNestedAgents(projectRoot, normalize));
27524
27708
  }
27525
27709
  const rulesDir = join(projectRoot, WINDSURF_RULES_DIR);
27526
27710
  results.push(
@@ -27530,15 +27714,22 @@ async function importFromWindsurf(projectRoot, options) {
27530
27714
  extensions: [".md"],
27531
27715
  fromTool: "windsurf",
27532
27716
  normalize,
27533
- mapEntry: async ({ relativePath, normalizeTo }) => {
27717
+ mapEntry: async ({ relativePath, content, normalizeTo }) => {
27534
27718
  if (relativePath === "_root.md" && rootContent !== null) return null;
27535
27719
  const destPath = join(destRulesDir, relativePath);
27536
- const { frontmatter, body } = parseFrontmatter(normalizeTo(destPath));
27537
- const normalizedFrontmatter = { ...frontmatter };
27538
- if (typeof normalizedFrontmatter.glob === "string" && normalizedFrontmatter.glob.trim()) {
27539
- normalizedFrontmatter.globs = [normalizedFrontmatter.glob];
27540
- delete normalizedFrontmatter.glob;
27720
+ const sourceLabel = `${WINDSURF_RULES_DIR}/${relativePath}`;
27721
+ const parsed = tryParseFrontmatter(
27722
+ normalizeTo(destPath, quoteWindsurfGlobValues(content)),
27723
+ sourceLabel
27724
+ );
27725
+ if (!parsed.ok) {
27726
+ logger.warn(`Skipping ${sourceLabel}: ${parsed.error.message}`);
27727
+ return null;
27541
27728
  }
27729
+ const { frontmatter, body } = parsed.value;
27730
+ const { glob, ...normalizedFrontmatter } = frontmatter;
27731
+ const globs = parseWindsurfGlobs(frontmatter.globs ?? glob);
27732
+ if (globs.length > 0) normalizedFrontmatter.globs = globs;
27542
27733
  return {
27543
27734
  destPath,
27544
27735
  toPath: `${AB_RULES}/${relativePath}`,
@@ -27589,9 +27780,11 @@ var init_importer32 = __esm({
27589
27780
  init_import_rewriter();
27590
27781
  init_fs();
27591
27782
  init_markdown();
27783
+ init_logger();
27784
+ init_rule_globs();
27592
27785
  init_import_metadata();
27593
27786
  init_import_orchestrator();
27594
- init_scoped_agents_import();
27787
+ init_import_nested_agents();
27595
27788
  init_constants34();
27596
27789
  init_importer_workflows();
27597
27790
  init_skills_adapter5();
@@ -27696,12 +27889,6 @@ var init_lint31 = __esm({
27696
27889
  });
27697
27890
 
27698
27891
  // src/targets/windsurf/index.ts
27699
- function directoryScopedRuleDir2(globs) {
27700
- if (globs.length === 0) return null;
27701
- const dirs = globs.map((glob) => glob.split("/")[0] ?? "").filter((segment) => /^[A-Za-z0-9._-]+$/.test(segment));
27702
- if (dirs.length !== globs.length) return null;
27703
- return dirs.every((dir) => dir === dirs[0]) ? dirs[0] : null;
27704
- }
27705
27892
  var target32, project24, globalLayout30, globalCapabilities25, descriptor32;
27706
27893
  var init_windsurf2 = __esm({
27707
27894
  "src/targets/windsurf/index.ts"() {
@@ -27733,9 +27920,7 @@ var init_windsurf2 = __esm({
27733
27920
  project24 = {
27734
27921
  rootInstructionPath: WINDSURF_AGENTS_MD,
27735
27922
  extraRuleOutputPaths(rule) {
27736
- if (rule.root) return [WINDSURF_AGENTS_MD];
27737
- const dir = directoryScopedRuleDir2(rule.globs);
27738
- return dir !== null ? [`${dir}/AGENTS.md`] : [];
27923
+ return rule.root ? [WINDSURF_AGENTS_MD] : [];
27739
27924
  },
27740
27925
  skillDir: WINDSURF_SKILLS_DIR,
27741
27926
  managedOutputs: {
@@ -29344,7 +29529,6 @@ function rewriteGeneratedReferences(results, canonical, config, projectRoot, sco
29344
29529
  const artifactCache = /* @__PURE__ */ new Map();
29345
29530
  const sourceCache = /* @__PURE__ */ new Map();
29346
29531
  return results.map((result2) => {
29347
- if (skipPaths?.has(result2.path)) return result2;
29348
29532
  const smKey = sourceMapCacheKey(result2.target, activeTargets);
29349
29533
  const sourceMap = sourceCache.get(smKey) ?? (() => {
29350
29534
  const built = buildOutputSourceMap(result2.target, canonical, config, scope, activeTargets);
@@ -29353,9 +29537,10 @@ function rewriteGeneratedReferences(results, canonical, config, projectRoot, sco
29353
29537
  })();
29354
29538
  const sourceFile = sourceMap.get(result2.path);
29355
29539
  if (!sourceFile) return result2;
29540
+ const shared = skipPaths?.has(result2.path) === true;
29356
29541
  const artifactMapTarget = artifactMapTargetForResult(result2, scope, activeTargets);
29357
29542
  const cacheKey = artifactCacheKey(result2, scope, activeTargets);
29358
- const artifactMap = artifactCache.get(cacheKey) ?? (() => {
29543
+ const artifactMap = shared ? /* @__PURE__ */ new Map() : artifactCache.get(cacheKey) ?? (() => {
29359
29544
  const built = buildArtifactPathMap(
29360
29545
  artifactMapTarget,
29361
29546
  canonical,
@@ -29376,6 +29561,7 @@ function rewriteGeneratedReferences(results, canonical, config, projectRoot, sco
29376
29561
  pathExists: (absolutePath) => plannedPaths.has(absolutePath) || existsSync(absolutePath),
29377
29562
  explicitCurrentDirLinks: true,
29378
29563
  rewriteBarePathTokens: true,
29564
+ markdownLinksOnly: shared,
29379
29565
  scope,
29380
29566
  pathIsDirectory: (absolutePath) => {
29381
29567
  try {
@@ -29393,62 +29579,7 @@ function rewriteGeneratedReferences(results, canonical, config, projectRoot, sco
29393
29579
  init_path_helpers();
29394
29580
  init_link_rebaser_helpers();
29395
29581
  init_protected_ranges();
29396
-
29397
- // src/utils/output/color.ts
29398
- function noColorRequested() {
29399
- const value = process.env.NO_COLOR;
29400
- return value !== void 0 && value !== "";
29401
- }
29402
- function forceColorRequested() {
29403
- const value = process.env.FORCE_COLOR;
29404
- if (value === void 0) return void 0;
29405
- return value !== "0" && value !== "false";
29406
- }
29407
- function colorEnabled(stream = process.stdout) {
29408
- const forced = forceColorRequested();
29409
- if (forced !== void 0) return forced;
29410
- if (noColorRequested()) return false;
29411
- return stream.isTTY === true;
29412
- }
29413
-
29414
- // src/utils/output/logger.ts
29415
- var C = {
29416
- green: "\x1B[32m",
29417
- red: "\x1B[31m",
29418
- yellow: "\x1B[33m",
29419
- cyan: "\x1B[36m",
29420
- reset: "\x1B[0m"
29421
- };
29422
- function outStream() {
29423
- return process.stdout;
29424
- }
29425
- function out(text) {
29426
- outStream().write(text);
29427
- }
29428
- function c(code, text, stream) {
29429
- return colorEnabled(stream) ? `${code}${text}${C.reset}` : text;
29430
- }
29431
- var logger = {
29432
- info(msg) {
29433
- out(c(C.cyan, msg, outStream()) + "\n");
29434
- },
29435
- warn(msg) {
29436
- process.stderr.write(c(C.yellow, "\u26A0 ", process.stderr) + msg + "\n");
29437
- },
29438
- error(msg) {
29439
- process.stderr.write(c(C.red, "\u2717 ", process.stderr) + msg + "\n");
29440
- },
29441
- success(msg) {
29442
- out(c(C.green, "\u2713 ", outStream()) + msg + "\n");
29443
- },
29444
- debug(msg) {
29445
- if (process.env.AGENTSMESH_DEBUG === "1") {
29446
- out(c(C.cyan, "[debug] ", outStream()) + msg + "\n");
29447
- }
29448
- }
29449
- };
29450
-
29451
- // src/core/reference/validate-generated-markdown-links.ts
29582
+ init_logger();
29452
29583
  var INLINE_MD_LINK = /!?\[[^\]]*\]\(([^)]+)\)/g;
29453
29584
  var REF_LINK_DEF = /^\s*\[(?!\^)[^\]\n]+\]:\s*(?:<([^>\n]*)>|(\S+))/gm;
29454
29585
  function isMarkdownLikeOutput(relativePath) {
@@ -29680,6 +29811,7 @@ init_fs_text_encoding();
29680
29811
 
29681
29812
  // src/core/generate/collision-agents.ts
29682
29813
  init_target_ids();
29814
+ init_logger();
29683
29815
  var AGENTS_SUFFIX = "AGENTS.md";
29684
29816
  var OPTIONAL_AGENTS_BLOCKS = [
29685
29817
  /<!-- agentsmesh:embedded-rules:start -->[\s\S]*?<!-- agentsmesh:embedded-rules:end -->\n*/g
@@ -30040,6 +30172,7 @@ init_errors();
30040
30172
 
30041
30173
  // src/config/core/loader.ts
30042
30174
  init_fs();
30175
+ init_logger();
30043
30176
  init_errors();
30044
30177
 
30045
30178
  // src/config/core/schema.ts
@@ -30867,6 +31000,7 @@ async function resolveExtendPaths(config, configDir, options = {}) {
30867
31000
  }
30868
31001
 
30869
31002
  // src/canonical/features/empty-file.ts
31003
+ init_logger();
30870
31004
  function isEmptyCanonicalFile(content, path) {
30871
31005
  if (content.trim() !== "") return false;
30872
31006
  logger.warn(`Skipping empty canonical file ${path.replaceAll("\\", "/")}`);
@@ -30968,6 +31102,9 @@ function assertNoBasenameCollisions(feature, paths2, stripExt) {
30968
31102
  seen.set(key, { path: p, slug });
30969
31103
  }
30970
31104
  }
31105
+
31106
+ // src/canonical/features/unrecognized-files-warning.ts
31107
+ init_logger();
30971
31108
  var ALTERNATE_RESOURCE_FORMATS = /* @__PURE__ */ new Set([".toml", ".yaml", ".yml", ".json"]);
30972
31109
  function warnIfUnrecognizedResourceFormats(featureLabel, dir, allFiles, parsedFiles, opts = {}) {
30973
31110
  if (allFiles.length === 0) return;
@@ -31176,6 +31313,14 @@ async function readContent(path) {
31176
31313
  return c2 ?? "";
31177
31314
  }
31178
31315
  var SKILL_FILE = "SKILL.md";
31316
+ async function readSkillFile(skillPath) {
31317
+ try {
31318
+ if ((await lstat(skillPath)).isSymbolicLink()) return null;
31319
+ } catch {
31320
+ return null;
31321
+ }
31322
+ return readFileSafe(skillPath);
31323
+ }
31179
31324
  var DOC_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".mdx", ".rst", ".txt"]);
31180
31325
  function isMarkdownLikeDoc(name) {
31181
31326
  const dot = name.lastIndexOf(".");
@@ -31205,7 +31350,7 @@ async function listSupportingFiles(skillDir) {
31205
31350
  }
31206
31351
  async function parseSkillDirectory(skillDir, opts = {}) {
31207
31352
  const skillPath = join(skillDir, SKILL_FILE);
31208
- const content = await readFileSafe(skillPath);
31353
+ const content = await readSkillFile(skillPath);
31209
31354
  if (!content) return null;
31210
31355
  const parsed = parseOrSkipFrontmatter(content, skillPath, opts.onParseError);
31211
31356
  if (!parsed) return null;
@@ -31236,7 +31381,7 @@ async function parseSkills(skillsDir, opts = {}) {
31236
31381
  assertCanonicalName("skill", ent.name);
31237
31382
  const skillDir = join(skillsDir, ent.name);
31238
31383
  const skillPath = join(skillDir, SKILL_FILE);
31239
- const content = await readFileSafe(skillPath);
31384
+ const content = await readSkillFile(skillPath);
31240
31385
  if (!content) continue;
31241
31386
  const parsed = parseOrSkipFrontmatter(content, skillPath, opts.onParseError);
31242
31387
  if (!parsed) continue;
@@ -31266,6 +31411,9 @@ function ensureStringArray(val) {
31266
31411
  async function parsePermissions(permissionsPath, onParseError) {
31267
31412
  const content = await readFileSafe(permissionsPath);
31268
31413
  if (content === null) return null;
31414
+ return parsePermissionsContent(content, permissionsPath, onParseError);
31415
+ }
31416
+ function parsePermissionsContent(content, permissionsPath, onParseError) {
31269
31417
  if (!content.trim()) return { allow: [], deny: [], ask: [] };
31270
31418
  let parsed;
31271
31419
  try {
@@ -31398,6 +31546,20 @@ function hookEvents(a, b) {
31398
31546
  function hookKey(entry) {
31399
31547
  return JSON.stringify([entry.type ?? "command", entry.matcher, entry.command]);
31400
31548
  }
31549
+ function settleRootRule(merged, local, packs) {
31550
+ const roots = merged.filter((rule) => rule.root);
31551
+ const root = roots.find((rule) => local.includes(rule)) ?? roots.find((rule) => packs.includes(rule)) ?? roots[0];
31552
+ const demoted = roots.filter((rule) => rule !== root);
31553
+ return {
31554
+ rules: merged.map((rule) => demoted.includes(rule) ? { ...rule, root: false } : rule),
31555
+ root,
31556
+ demoted
31557
+ };
31558
+ }
31559
+ function demotedRootMessage(rule, root, baseDir) {
31560
+ const shown = (r) => relative(baseDir, r.source).replaceAll("\\", "/");
31561
+ return `[agentsmesh] Rule "${shown(rule)}" also says root: true, but "${shown(root)}" is the root rule (the project's own root wins over installed packs, and packs over extends), so it is used as a normal rule.`;
31562
+ }
31401
31563
 
31402
31564
  // src/config/resolve/native-format-detector.ts
31403
31565
  init_fs();
@@ -31443,6 +31605,7 @@ var KNOWN_NATIVE_PATHS = BUILTIN_TARGETS.map(
31443
31605
 
31444
31606
  // src/canonical/extends/extend-load.ts
31445
31607
  init_fs();
31608
+ init_logger();
31446
31609
 
31447
31610
  // src/canonical/extends/native-extends-importer.ts
31448
31611
  init_registry();
@@ -32069,6 +32232,9 @@ Expected one of: .agentsmesh/, ${KNOWN_NATIVE_PATHS.join(", ")}.`
32069
32232
  throw wrapped;
32070
32233
  }
32071
32234
  }
32235
+
32236
+ // src/canonical/extends/extend-pick.ts
32237
+ init_logger();
32072
32238
  function applyExtendPick(canonical, features, pick, extendName) {
32073
32239
  if (!pick) return canonical;
32074
32240
  let next = { ...canonical };
@@ -32261,6 +32427,7 @@ async function loadPacksCanonical(abDir) {
32261
32427
  }
32262
32428
 
32263
32429
  // src/canonical/extends/extends.ts
32430
+ init_logger();
32264
32431
  var FEATURE_TO_KEYS = {
32265
32432
  rules: ["rules"],
32266
32433
  commands: ["commands"],
@@ -32309,6 +32476,15 @@ async function loadCanonicalWithExtends(config, configDir, options = {}, canonic
32309
32476
  merged = mergeCanonicalFiles(merged, packsCanonical, { hooks: "combine" });
32310
32477
  const localCanonical = await loadCanonicalFiles(canonicalDir);
32311
32478
  merged = mergeCanonicalFiles(merged, localCanonical);
32479
+ const { rules, root, demoted } = settleRootRule(
32480
+ merged.rules,
32481
+ localCanonical.rules,
32482
+ packsCanonical.rules
32483
+ );
32484
+ if (root !== void 0) {
32485
+ for (const rule of demoted) logger.warn(demotedRootMessage(rule, root, configDir));
32486
+ }
32487
+ merged = { ...merged, rules };
32312
32488
  merged = { ...merged, hooks: combineHooks(merged.hooks, packsCanonical.hooks) };
32313
32489
  return { canonical: merged, resolvedExtends };
32314
32490
  }
@@ -32316,6 +32492,7 @@ async function loadCanonicalWithExtends(config, configDir, options = {}, canonic
32316
32492
  // src/plugins/load-plugin.ts
32317
32493
  init_target_descriptor_schema();
32318
32494
  init_registry();
32495
+ init_logger();
32319
32496
  function resolveNpmSpecifier(source, projectRoot) {
32320
32497
  const pkgDir = join(projectRoot, "node_modules", source);
32321
32498
  const pkgJsonPath = join(pkgDir, "package.json");
@@ -32637,6 +32814,113 @@ function parseGraph(raw) {
32637
32814
  function emptyGraph() {
32638
32815
  return { version: CURRENT_GRAPH_VERSION, lessons: {}, topics: {}, triggers: {} };
32639
32816
  }
32817
+ async function canonicalizePath(path) {
32818
+ try {
32819
+ return await realpath(path);
32820
+ } catch (error) {
32821
+ if (error.code !== "ENOENT") throw error;
32822
+ const parent = dirname(path);
32823
+ if (parent === path) return resolve(path);
32824
+ return join(await canonicalizePath(parent), basename(path));
32825
+ }
32826
+ }
32827
+ function canonicalizePathSync(path) {
32828
+ try {
32829
+ return realpathSync(path);
32830
+ } catch (error) {
32831
+ if (error.code !== "ENOENT") return null;
32832
+ }
32833
+ try {
32834
+ lstatSync(path);
32835
+ return null;
32836
+ } catch {
32837
+ const parent = dirname(path);
32838
+ if (parent === path) return resolve(path);
32839
+ const realParent = canonicalizePathSync(parent);
32840
+ return realParent === null ? null : join(realParent, basename(path));
32841
+ }
32842
+ }
32843
+ function resolvesInsideRootSync(root, target34) {
32844
+ const realRoot = canonicalizePathSync(resolve(root));
32845
+ const realTarget = canonicalizePathSync(resolve(target34));
32846
+ return realRoot !== null && realTarget !== null && isPathInside(realTarget, realRoot);
32847
+ }
32848
+ function isPathInside(target34, root) {
32849
+ return target34 === root || target34.startsWith(root.endsWith(sep) ? root : `${root}${sep}`);
32850
+ }
32851
+ var display = (path) => path.replaceAll("\\", "/");
32852
+ async function assertPathInsideRoot(root, target34) {
32853
+ const rootAbs = resolve(root);
32854
+ const targetAbs = resolve(target34);
32855
+ if (!isPathInside(targetAbs, rootAbs)) {
32856
+ throw new Error(`Unsafe filesystem path: ${display(target34)} is outside ${display(rootAbs)}`);
32857
+ }
32858
+ let realTarget;
32859
+ let realRoot;
32860
+ try {
32861
+ [realTarget, realRoot] = await Promise.all([
32862
+ canonicalizePath(targetAbs),
32863
+ canonicalizePath(rootAbs)
32864
+ ]);
32865
+ } catch (cause) {
32866
+ const detail = cause instanceof Error ? cause.message : String(cause);
32867
+ throw new Error(
32868
+ `Unsafe filesystem path: ${display(target34)} could not be resolved (${detail})`,
32869
+ { cause }
32870
+ );
32871
+ }
32872
+ if (isPathInside(realTarget, realRoot)) return;
32873
+ throw new Error(
32874
+ `Unsafe filesystem path: ${display(target34)} resolves to ${display(realTarget)} outside ${display(realRoot)}`
32875
+ );
32876
+ }
32877
+ var BASE_REL = ".agentsmesh/lessons";
32878
+ function lessonsPaths(projectRoot) {
32879
+ const base = join(projectRoot, BASE_REL);
32880
+ return {
32881
+ base,
32882
+ graph: join(base, "lessons.json"),
32883
+ config: join(base, "config.json"),
32884
+ journal: join(base, "journal.md"),
32885
+ index: join(base, "index.yaml"),
32886
+ topicsDir: join(base, "topics")
32887
+ };
32888
+ }
32889
+ function toRelPath(projectRoot, absolute) {
32890
+ return relative(projectRoot, absolute).split(sep).join("/");
32891
+ }
32892
+ var LESSONS_PROCEDURAL_RULE = `## Lessons (BLOCKING)
32893
+
32894
+ Graph \`.agentsmesh/lessons/lessons.json\` is canonical; never hand-edit it. Manual: \`lessons\` skill.
32895
+
32896
+ **Recall:** before every file edit or state-changing command, MUST run \`agentsmesh lessons query --file <path> --cmd <command> --session auto\` and obey matches; at task start, ALSO run \`agentsmesh lessons query --keyword "<task terms>" --always --session auto\` for conceptual + universal rules no path/command names. Pure-read commands and recall itself are exempt.
32897
+
32898
+ **Capture:** after any failure, user correction, regression, wrong assumption, useful surprise, repeated friction, or non-obvious fix, MUST self-critique and run \`agentsmesh lessons add "<imperative rule>" --topic <id> --trigger-file <glob> --evidence <sha|lesson-id>\`.
32899
+
32900
+ **Before final:** report \`Lesson: captured <id>\` or \`Lesson: none\`. No recall/capture gate = task incomplete. No shell: use \`lessons_query\` / \`lessons_add\`.`;
32901
+
32902
+ // src/lessons/lessons-dir-guard.ts
32903
+ function lessonsDirInsideProject(projectRoot) {
32904
+ return resolvesInsideRootSync(projectRoot, lessonsPaths(projectRoot).base);
32905
+ }
32906
+ var LessonsDirOutsideProjectError = class extends Error {
32907
+ code = "LESSONS_DIR_OUTSIDE_PROJECT";
32908
+ constructor(projectRoot) {
32909
+ const base = lessonsPaths(projectRoot).base;
32910
+ let where = "a path that does not exist";
32911
+ try {
32912
+ where = realpathSync(base).replaceAll("\\", "/");
32913
+ } catch {
32914
+ }
32915
+ super(
32916
+ `${base.replaceAll("\\", "/")} resolves to ${where}, outside the project ${projectRoot.replaceAll("\\", "/")}. agentsmesh only writes lessons inside the project; replace the link with a real folder.`
32917
+ );
32918
+ this.name = "LessonsDirOutsideProjectError";
32919
+ }
32920
+ };
32921
+ function assertLessonsDirInsideProject(projectRoot) {
32922
+ if (!lessonsDirInsideProject(projectRoot)) throw new LessonsDirOutsideProjectError(projectRoot);
32923
+ }
32640
32924
 
32641
32925
  // src/lessons/graph-store.ts
32642
32926
  var LESSONS_GRAPH_PATH = ".agentsmesh/lessons/lessons.json";
@@ -32675,6 +32959,7 @@ function isWritable(path) {
32675
32959
  }
32676
32960
  }
32677
32961
  function saveLessonsGraph(projectRoot, graph) {
32962
+ assertLessonsDirInsideProject(projectRoot);
32678
32963
  const path = graphFilePath(projectRoot);
32679
32964
  mkdirSync(dirname(path), { recursive: true });
32680
32965
  const mode = fileMode(path);
@@ -32701,30 +32986,6 @@ function canonicalize2(value) {
32701
32986
  }
32702
32987
  return value;
32703
32988
  }
32704
- var BASE_REL = ".agentsmesh/lessons";
32705
- function lessonsPaths(projectRoot) {
32706
- const base = join(projectRoot, BASE_REL);
32707
- return {
32708
- base,
32709
- graph: join(base, "lessons.json"),
32710
- config: join(base, "config.json"),
32711
- journal: join(base, "journal.md"),
32712
- index: join(base, "index.yaml"),
32713
- topicsDir: join(base, "topics")
32714
- };
32715
- }
32716
- function toRelPath(projectRoot, absolute) {
32717
- return relative(projectRoot, absolute).split(sep).join("/");
32718
- }
32719
- var LESSONS_PROCEDURAL_RULE = `## Lessons (BLOCKING)
32720
-
32721
- Graph \`.agentsmesh/lessons/lessons.json\` is canonical; never hand-edit it. Manual: \`lessons\` skill.
32722
-
32723
- **Recall:** before every file edit or state-changing command, MUST run \`agentsmesh lessons query --file <path> --cmd <command> --session auto\` and obey matches; at task start, ALSO run \`agentsmesh lessons query --keyword "<task terms>" --always --session auto\` for conceptual + universal rules no path/command names. Pure-read commands and recall itself are exempt.
32724
-
32725
- **Capture:** after any failure, user correction, regression, wrong assumption, useful surprise, repeated friction, or non-obvious fix, MUST self-critique and run \`agentsmesh lessons add "<imperative rule>" --topic <id> --trigger-file <glob> --evidence <sha|lesson-id>\`.
32726
-
32727
- **Before final:** report \`Lesson: captured <id>\` or \`Lesson: none\`. No recall/capture gate = task incomplete. No shell: use \`lessons_query\` / \`lessons_add\`.`;
32728
32989
  var MAX_OUTPUT_BYTES = 64 * 1024 * 1024;
32729
32990
  function runGit2(cwd, args, timeoutMs) {
32730
32991
  const r = spawnSync("git", [...args], {
@@ -34541,6 +34802,13 @@ async function hashFileForManifest(path) {
34541
34802
  }
34542
34803
  }
34543
34804
 
34805
+ // src/config/core/lock-stale-targets.ts
34806
+ function parseStaleTargets(raw) {
34807
+ if (!Array.isArray(raw)) return void 0;
34808
+ const targets = raw.filter((target34) => typeof target34 === "string");
34809
+ return targets.length > 0 ? targets : void 0;
34810
+ }
34811
+
34544
34812
  // src/config/core/lock.ts
34545
34813
  var LOCK_FILENAME = ".lock";
34546
34814
  var CANONICAL_PATTERNS = [
@@ -34582,7 +34850,8 @@ async function readLock(abDir) {
34582
34850
  extends: raw.extends && typeof raw.extends === "object" ? raw.extends : {},
34583
34851
  packs: raw.packs && typeof raw.packs === "object" ? raw.packs : {},
34584
34852
  // undefined (not {}) when absent → old-format lock; skips output check.
34585
- outputs: raw.outputs && typeof raw.outputs === "object" ? raw.outputs : void 0
34853
+ outputs: raw.outputs && typeof raw.outputs === "object" ? raw.outputs : void 0,
34854
+ staleTargets: parseStaleTargets(raw.stale_targets)
34586
34855
  };
34587
34856
  } catch {
34588
34857
  return null;
@@ -34651,47 +34920,6 @@ async function diffOutputChecksums(rootBase, lockOutputs) {
34651
34920
 
34652
34921
  // src/core/generate/stale-cleanup.ts
34653
34922
  init_fs();
34654
- async function canonicalizePath(path) {
34655
- try {
34656
- return await realpath(path);
34657
- } catch (error) {
34658
- if (error.code !== "ENOENT") throw error;
34659
- const parent = dirname(path);
34660
- if (parent === path) return resolve(path);
34661
- return join(await canonicalizePath(parent), basename(path));
34662
- }
34663
- }
34664
- function isPathInside(target34, root) {
34665
- return target34 === root || target34.startsWith(root.endsWith(sep) ? root : `${root}${sep}`);
34666
- }
34667
- var display = (path) => path.replaceAll("\\", "/");
34668
- async function assertPathInsideRoot(root, target34) {
34669
- const rootAbs = resolve(root);
34670
- const targetAbs = resolve(target34);
34671
- if (!isPathInside(targetAbs, rootAbs)) {
34672
- throw new Error(`Unsafe filesystem path: ${display(target34)} is outside ${display(rootAbs)}`);
34673
- }
34674
- let realTarget;
34675
- let realRoot;
34676
- try {
34677
- [realTarget, realRoot] = await Promise.all([
34678
- canonicalizePath(targetAbs),
34679
- canonicalizePath(rootAbs)
34680
- ]);
34681
- } catch (cause) {
34682
- const detail = cause instanceof Error ? cause.message : String(cause);
34683
- throw new Error(
34684
- `Unsafe filesystem path: ${display(target34)} could not be resolved (${detail})`,
34685
- { cause }
34686
- );
34687
- }
34688
- if (isPathInside(realTarget, realRoot)) return;
34689
- throw new Error(
34690
- `Unsafe filesystem path: ${display(target34)} resolves to ${display(realTarget)} outside ${display(realRoot)}`
34691
- );
34692
- }
34693
-
34694
- // src/core/generate/stale-cleanup.ts
34695
34923
  init_builtin_targets();
34696
34924
  init_registry();
34697
34925
  init_builtin_targets();
@@ -34806,6 +35034,7 @@ async function checkLockSync(opts) {
34806
35034
  outputsModified: [],
34807
35035
  outputsRemoved: [],
34808
35036
  outputsStale: [],
35037
+ staleTargets: [],
34809
35038
  outputsUntracked: [],
34810
35039
  outputsChecked: false
34811
35040
  };
@@ -34859,8 +35088,10 @@ async function checkLockSync(opts) {
34859
35088
  generatedOutputs: Object.keys(lock.outputs),
34860
35089
  scope
34861
35090
  }) : [];
35091
+ const configured = [...config.targets, ...config.pluginTargets ?? []];
35092
+ const staleTargets = (lock.staleTargets ?? []).filter((target34) => configured.includes(target34));
34862
35093
  const canonicalDrift = modified.length > 0 || added.length > 0 || removed.length > 0 || extendsModified.length > 0;
34863
- const outputDrift = outputsModified.length > 0 || outputsRemoved.length > 0 || outputsStale.length > 0;
35094
+ const outputDrift = outputsModified.length > 0 || outputsRemoved.length > 0 || outputsStale.length > 0 || staleTargets.length > 0;
34864
35095
  const inSync = !canonicalDrift && !outputDrift;
34865
35096
  return {
34866
35097
  inSync,
@@ -34876,6 +35107,7 @@ async function checkLockSync(opts) {
34876
35107
  outputsModified,
34877
35108
  outputsRemoved,
34878
35109
  outputsStale,
35110
+ staleTargets,
34879
35111
  outputsUntracked,
34880
35112
  outputsChecked
34881
35113
  };
@@ -34884,6 +35116,62 @@ async function checkLockSync(opts) {
34884
35116
  // src/public/engine.ts
34885
35117
  init_registry();
34886
35118
  init_target_ids();
35119
+ init_canonical_paths();
35120
+ init_fs();
35121
+ init_mcp_merge();
35122
+ var PERMISSION_LISTS = ["allow", "deny", "ask"];
35123
+ async function keepPermissions(path, before) {
35124
+ const after = await readFileSafe(path) ?? "";
35125
+ const skipBroken = () => void 0;
35126
+ const earlier = parsePermissionsContent(before, path, skipBroken);
35127
+ const now = parsePermissionsContent(after, path, skipBroken);
35128
+ if (earlier === null || now === null) return;
35129
+ const doc = parseDocument(after);
35130
+ let changed = false;
35131
+ for (const list of PERMISSION_LISTS) {
35132
+ const old = earlier[list];
35133
+ const cur = now[list];
35134
+ if (old.every((entry) => cur.includes(entry))) continue;
35135
+ doc.set(list, [...old, ...cur.filter((entry) => !old.includes(entry))]);
35136
+ changed = true;
35137
+ }
35138
+ if (changed) await writeFileAtomic(path, doc.toString());
35139
+ }
35140
+ async function keepIgnorePatterns(path, before) {
35141
+ const lines = (text) => text.split(/\r?\n/).map((line) => line.trim()).filter(Boolean);
35142
+ const earlier = lines(before);
35143
+ const now = lines(await readFileSafe(path) ?? "");
35144
+ if (earlier.every((line) => now.includes(line))) return;
35145
+ const added = now.filter((line) => !earlier.includes(line));
35146
+ await writeFileAtomic(path, `${[before.trimEnd(), ...added].join("\n")}
35147
+ `);
35148
+ }
35149
+ async function keepMcpServers(path, before) {
35150
+ const earlier = parseMcpServers(before);
35151
+ const now = parseMcpServers(await readFileSafe(path));
35152
+ if (Object.keys(earlier).every((name) => Object.hasOwn(now, name))) return;
35153
+ await writeFileAtomic(path, JSON.stringify({ mcpServers: { ...earlier, ...now } }, null, 2));
35154
+ }
35155
+ var KEEPERS = [
35156
+ [AB_PERMISSIONS, keepPermissions],
35157
+ [AB_IGNORE, keepIgnorePatterns],
35158
+ [AB_MCP, keepMcpServers]
35159
+ ];
35160
+ async function runTargetImport(descriptor34, rootBase, scope) {
35161
+ const snapshots = await Promise.all(
35162
+ KEEPERS.map(async ([rel2, keep]) => {
35163
+ const path = join(rootBase, rel2);
35164
+ return { path, keep, before: await readFileSafe(path) };
35165
+ })
35166
+ );
35167
+ const results = await descriptor34.generators.importFrom(rootBase, { scope });
35168
+ for (const { path, keep, before } of snapshots) {
35169
+ if (before !== null) await keep(path, before);
35170
+ }
35171
+ return results;
35172
+ }
35173
+
35174
+ // src/public/engine.ts
34887
35175
  async function importFrom(target34, opts) {
34888
35176
  const descriptor34 = getDescriptor(target34);
34889
35177
  if (!descriptor34) {
@@ -34891,7 +35179,7 @@ async function importFrom(target34, opts) {
34891
35179
  supported: [...TARGET_IDS, ...getAllDescriptors().map((d) => d.id)]
34892
35180
  });
34893
35181
  }
34894
- return descriptor34.generators.importFrom(opts.root, { scope: opts.scope ?? "project" });
35182
+ return runTargetImport(descriptor34, opts.root, opts.scope ?? "project");
34895
35183
  }
34896
35184
  async function loadConfig2(projectRoot) {
34897
35185
  return loadConfigFromDir(projectRoot);
@@ -35064,42 +35352,8 @@ function makeTriggerId2(spec) {
35064
35352
  const hash = createHash("sha1").update(`${spec.kind}|${spec.pattern}`).digest("hex").slice(0, 8);
35065
35353
  return `t-${TRIGGER_PREFIX2[spec.kind]}-${hash}`;
35066
35354
  }
35067
- var RULE_HEADING_RE = /^##\s+Rules\b.*$/i;
35068
- var NEXT_HEADING_RE = /^##\s+/;
35069
- var RULE_LINE_RE = /^(\d+)\.\s+(.+?)\s*$/;
35070
- var EVIDENCE_TAIL_RE = /\s*\(Evidence:?\s+([^)]+)\)\s*$/;
35071
- var EVIDENCE_REF_RE = /L\d+/g;
35072
- function parseRulesSection(markdown) {
35073
- const lines = markdown.split(/\r?\n/);
35074
- let inRules = false;
35075
- const rules = [];
35076
- for (const line of lines) {
35077
- if (!inRules) {
35078
- if (RULE_HEADING_RE.test(line)) inRules = true;
35079
- continue;
35080
- }
35081
- if (NEXT_HEADING_RE.test(line)) break;
35082
- const m = RULE_LINE_RE.exec(line);
35083
- if (m === null) continue;
35084
- const ruleIndex = Number(m[1]);
35085
- let body = m[2];
35086
- const evidence = [];
35087
- let tail = EVIDENCE_TAIL_RE.exec(body);
35088
- while (tail !== null) {
35089
- const refs = tail[1];
35090
- const matches = refs.match(EVIDENCE_REF_RE);
35091
- if (matches !== null) evidence.unshift(...matches);
35092
- body = body.slice(0, tail.index).trimEnd();
35093
- tail = EVIDENCE_TAIL_RE.exec(body);
35094
- }
35095
- rules.push({ index: ruleIndex, body, evidence });
35096
- }
35097
- return rules;
35098
- }
35099
35355
  var LEGACY_ARTIFACT_REL = [
35100
35356
  "index.yaml",
35101
- "journal.md",
35102
- "journal.legacy.md",
35103
35357
  "topics",
35104
35358
  "distill-ledger.yaml",
35105
35359
  "distill-proposal.md"
@@ -35794,6 +36048,7 @@ function holdLock(lockPath, token) {
35794
36048
  }
35795
36049
 
35796
36050
  // src/lessons/lessons-lock.ts
36051
+ init_logger();
35797
36052
  var LESSONS_LOCK_FILENAME = ".lessons.lock";
35798
36053
  var LESSONS_LOCK_OPTIONS = Object.freeze({
35799
36054
  retries: 500,
@@ -35806,6 +36061,7 @@ function lessonsLockPath(projectRoot) {
35806
36061
  return resolve(projectRoot, ".agentsmesh/lessons", LESSONS_LOCK_FILENAME);
35807
36062
  }
35808
36063
  async function acquireLessonsLock(projectRoot, opts = {}) {
36064
+ assertLessonsDirInsideProject(projectRoot);
35809
36065
  return acquireProcessLock(lessonsLockPath(projectRoot), {
35810
36066
  retries: opts.retries ?? LESSONS_LOCK_OPTIONS.retries,
35811
36067
  retryDelayMs: opts.retryDelayMs ?? LESSONS_LOCK_OPTIONS.retryDelayMs,
@@ -35972,6 +36228,50 @@ async function mergeLegacy(projectRoot, paths2, specs, summaryByTopic, options)
35972
36228
  triggerCount: addedTriggers.size
35973
36229
  };
35974
36230
  }
36231
+
36232
+ // src/lessons/import-legacy-rules.ts
36233
+ var HEADING = /^#{1,6}\s/;
36234
+ var SECTION_HEADING = /^#{1,2}\s/;
36235
+ var RULES_HEADING = /^##\s+(?:Rules|Lessons)\b/i;
36236
+ var ITEM = /^\s{0,3}(?:\d+[.)]|[-*+])\s+(.+?)\s*$/;
36237
+ var EVIDENCE_TAIL = /\s*\(Evidence:?\s+([^)]+)\)\s*$/;
36238
+ var EVIDENCE_REF = /L\d+/g;
36239
+ function withEvidence(index, text) {
36240
+ let body = text;
36241
+ const evidence = [];
36242
+ let tail = EVIDENCE_TAIL.exec(body);
36243
+ while (tail !== null) {
36244
+ evidence.unshift(...tail[1].match(EVIDENCE_REF) ?? []);
36245
+ body = body.slice(0, tail.index).trimEnd();
36246
+ tail = EVIDENCE_TAIL.exec(body);
36247
+ }
36248
+ return { index, body, evidence };
36249
+ }
36250
+ function parseRulesSection(markdown) {
36251
+ const items = [];
36252
+ let inRules = false;
36253
+ let open2 = false;
36254
+ let strayLine = null;
36255
+ markdown.split(/\r?\n/).forEach((line, i) => {
36256
+ if (HEADING.test(line)) {
36257
+ if (SECTION_HEADING.test(line)) inRules = RULES_HEADING.test(line);
36258
+ open2 = false;
36259
+ return;
36260
+ }
36261
+ const item = ITEM.exec(line);
36262
+ if (item !== null) {
36263
+ if (inRules) items.push(item[1]);
36264
+ else strayLine ??= i + 1;
36265
+ open2 = inRules;
36266
+ return;
36267
+ }
36268
+ if (line.trim() === "") open2 = false;
36269
+ else if (open2) items[items.length - 1] += ` ${line.trim()}`;
36270
+ });
36271
+ return { rules: items.map((text, i) => withEvidence(i + 1, text)), strayLine };
36272
+ }
36273
+
36274
+ // src/lessons/import-legacy-read.ts
35975
36275
  var LESSONS_DIR = ".agentsmesh/lessons";
35976
36276
  var LegacyTopicPathError = class extends Error {
35977
36277
  code = "LEGACY_TOPIC_PATH_OUTSIDE";
@@ -35985,8 +36285,8 @@ var LegacyTopicPathError = class extends Error {
35985
36285
  async function resolveLegacyTopicPath(projectRoot, file) {
35986
36286
  const forward = file.replaceAll("\\", "/");
35987
36287
  const normalized = posix.normalize(forward);
35988
- const relative27 = !/^[A-Za-z]:/.test(forward) && !forward.startsWith("/") && normalized.startsWith(`${LESSONS_DIR}/`);
35989
- if (!relative27) throw new LegacyTopicPathError(file);
36288
+ const relative28 = !/^[A-Za-z]:/.test(forward) && !forward.startsWith("/") && normalized.startsWith(`${LESSONS_DIR}/`);
36289
+ if (!relative28) throw new LegacyTopicPathError(file);
35990
36290
  const target34 = join(projectRoot, normalized);
35991
36291
  try {
35992
36292
  await assertPathInsideRoot(join(projectRoot, LESSONS_DIR), target34);
@@ -36015,9 +36315,13 @@ async function readLegacySource(projectRoot, migratedAt) {
36015
36315
  `Legacy topic file is missing: ${cluster.file}. Refusing to migrate (legacy artifacts left intact).`
36016
36316
  );
36017
36317
  }
36018
- for (const { index: ruleIndex, body, evidence } of parseRulesSection(
36019
- readFileSync(topicFile, "utf8")
36020
- )) {
36318
+ const parsed = parseRulesSection(readFileSync(topicFile, "utf8"));
36319
+ if (parsed.strayLine !== null) {
36320
+ throw new Error(
36321
+ `Legacy lessons were not migrated: ${cluster.file} line ${parsed.strayLine} is a list item outside a "## Rules" or "## Lessons" section. Move it under one of them or delete it, then run \`agentsmesh lessons import-md\`. Nothing was changed.`
36322
+ );
36323
+ }
36324
+ for (const { index: ruleIndex, body, evidence } of parsed.rules) {
36021
36325
  const lessonEvidence = [
36022
36326
  `legacy:${cluster.file}#rule-${ruleIndex}`,
36023
36327
  ...evidence.map((e) => `legacy:${e}`)
@@ -36718,6 +37022,7 @@ async function scaffoldLessons(projectRoot) {
36718
37022
  const created = [];
36719
37023
  const updated = [];
36720
37024
  const skipped = [];
37025
+ assertLessonsDirInsideProject(projectRoot);
36721
37026
  mkdirSync(paths2.base, { recursive: true });
36722
37027
  await maybeAutoMigrateLessons(projectRoot);
36723
37028
  if (existsSync(paths2.graph)) {