agentsmesh 0.33.0 → 0.34.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/canonical.js CHANGED
@@ -24,7 +24,7 @@ var __export = (target34, all) => {
24
24
  };
25
25
 
26
26
  // src/core/errors.ts
27
- var AgentsMeshError, ConfigNotFoundError, ConfigValidationError, FileSystemError;
27
+ var AgentsMeshError, ConfigNotFoundError, ConfigValidationError, CanonicalParseError, FileSystemError;
28
28
  var init_errors = __esm({
29
29
  "src/core/errors.ts"() {
30
30
  AgentsMeshError = class extends Error {
@@ -59,6 +59,21 @@ var init_errors = __esm({
59
59
  this.issues = issues;
60
60
  }
61
61
  };
62
+ CanonicalParseError = class extends AgentsMeshError {
63
+ path;
64
+ constructor(path, cause) {
65
+ const detail = cause instanceof Error ? cause.message : String(cause);
66
+ super(
67
+ "AM_CONFIG_INVALID",
68
+ `Invalid canonical file ${path}: ${detail}. Fix the syntax and try again.`,
69
+ {
70
+ cause
71
+ }
72
+ );
73
+ this.name = "CanonicalParseError";
74
+ this.path = path;
75
+ }
76
+ };
62
77
  FileSystemError = class extends AgentsMeshError {
63
78
  path;
64
79
  errnoCode;
@@ -332,24 +347,28 @@ var init_fs = __esm({
332
347
  }
333
348
  });
334
349
  function parseFrontmatter(content) {
335
- const open = content.indexOf("---");
336
- if (open !== 0) {
337
- return { frontmatter: {}, body: content.trim() };
338
- }
339
- const close = content.indexOf("---", 3);
340
- if (close === -1) {
341
- return { frontmatter: {}, body: content.trim() };
342
- }
343
- const yamlStr = content.slice(3, close).trim();
344
- const body = content.slice(close + 3).trim();
350
+ const split = splitFrontmatter(content);
351
+ if (split === null) return { frontmatter: {}, body: content.trim() };
352
+ const yamlStr = split.yaml.trim();
345
353
  const frontmatter = yamlStr === "" ? {} : parse(yamlStr) ?? {};
346
- return { frontmatter, body };
354
+ return { frontmatter, body: split.body };
355
+ }
356
+ function splitFrontmatter(content) {
357
+ const opener = OPENER.exec(content);
358
+ if (opener === null) return null;
359
+ const yamlStart = opener[0].length;
360
+ const closer = CLOSER.exec(content.slice(yamlStart));
361
+ if (closer === null) return null;
362
+ const closeStart = yamlStart + closer.index;
363
+ const closeEnd = closeStart + closer[0].length;
364
+ return {
365
+ yaml: content.slice(yamlStart, closeStart),
366
+ body: content.slice(closeEnd).trim(),
367
+ prefix: content.slice(0, closeEnd)
368
+ };
347
369
  }
348
370
  function extractBody(content) {
349
- if (content.indexOf("---") !== 0) return content.trim();
350
- const close = content.indexOf("---", 3);
351
- if (close === -1) return content.trim();
352
- return content.slice(close + 3).trim();
371
+ return splitFrontmatter(content)?.body ?? content.trim();
353
372
  }
354
373
  function tryParseFrontmatter(content, filePath2) {
355
374
  try {
@@ -381,8 +400,11 @@ ${yamlStr}
381
400
 
382
401
  ${body}`;
383
402
  }
403
+ var OPENER, CLOSER;
384
404
  var init_markdown = __esm({
385
405
  "src/utils/text/markdown.ts"() {
406
+ OPENER = /^---[ \t]*\r?\n/;
407
+ CLOSER = /^---[ \t]*\r?$/m;
386
408
  }
387
409
  });
388
410
 
@@ -465,6 +487,21 @@ var init_boilerplate_filter = __esm({
465
487
  }
466
488
  });
467
489
 
490
+ // src/canonical/features/syntax-error.ts
491
+ function failSyntax(filePath2, cause, onParseError) {
492
+ const error = new CanonicalParseError(filePath2, cause);
493
+ if (onParseError !== void 0) {
494
+ onParseError(error, filePath2);
495
+ return null;
496
+ }
497
+ throw error;
498
+ }
499
+ var init_syntax_error = __esm({
500
+ "src/canonical/features/syntax-error.ts"() {
501
+ init_errors();
502
+ }
503
+ });
504
+
468
505
  // src/canonical/features/mcp.ts
469
506
  function parseStringMap(raw) {
470
507
  if (raw === null || typeof raw !== "object" || Array.isArray(raw)) return {};
@@ -547,14 +584,14 @@ function stripJsonComments(text) {
547
584
  }
548
585
  return result2;
549
586
  }
550
- async function parseMcp(mcpPath) {
587
+ async function parseMcp(mcpPath, onParseError) {
551
588
  const content = await readFileSafe(mcpPath);
552
589
  if (!content) return null;
553
590
  let parsed;
554
591
  try {
555
592
  parsed = JSON.parse(stripJsonComments(content));
556
- } catch {
557
- return null;
593
+ } catch (err) {
594
+ return failSyntax(mcpPath, err, onParseError);
558
595
  }
559
596
  if (!parsed || typeof parsed !== "object") return null;
560
597
  const mcpServersRaw = parsed.mcpServers;
@@ -569,6 +606,7 @@ async function parseMcp(mcpPath) {
569
606
  }
570
607
  var init_mcp = __esm({
571
608
  "src/canonical/features/mcp.ts"() {
609
+ init_syntax_error();
572
610
  init_fs();
573
611
  }
574
612
  });
@@ -598,6 +636,57 @@ var init_hook_command = __esm({
598
636
  "src/core/hook-command.ts"() {
599
637
  }
600
638
  });
639
+ function toHookEntry(raw) {
640
+ if (!raw || typeof raw !== "object") return null;
641
+ const obj = raw;
642
+ const matcher = obj.matcher;
643
+ if (typeof matcher !== "string") return null;
644
+ const command = getHookText(obj);
645
+ if (!command) return null;
646
+ const type = typeof obj.type === "string" && VALID_TYPES.includes(obj.type) ? obj.type : void 0;
647
+ const timeout = typeof obj.timeout === "number" && Number.isFinite(obj.timeout) ? obj.timeout : void 0;
648
+ const prompt = getHookPrompt(obj) || void 0;
649
+ return {
650
+ matcher,
651
+ command,
652
+ ...timeout !== void 0 && { timeout },
653
+ ...type && { type },
654
+ ...prompt && { prompt }
655
+ };
656
+ }
657
+ async function parseHooks(hooksPath, onParseError) {
658
+ const content = await readFileSafe(hooksPath);
659
+ if (content === null) return null;
660
+ if (!content.trim()) return {};
661
+ let parsed;
662
+ try {
663
+ parsed = parse(content);
664
+ } catch (err) {
665
+ return failSyntax(hooksPath, err, onParseError);
666
+ }
667
+ if (!parsed || typeof parsed !== "object") return null;
668
+ const result2 = {};
669
+ const obj = parsed;
670
+ for (const [key, val] of Object.entries(obj)) {
671
+ if (!Array.isArray(val)) continue;
672
+ const entries = [];
673
+ for (const item of val) {
674
+ const entry = toHookEntry(item);
675
+ if (entry) entries.push(entry);
676
+ }
677
+ if (entries.length > 0) result2[key] = entries;
678
+ }
679
+ return result2;
680
+ }
681
+ var VALID_TYPES;
682
+ var init_hooks = __esm({
683
+ "src/canonical/features/hooks.ts"() {
684
+ init_syntax_error();
685
+ init_fs();
686
+ init_hook_command();
687
+ VALID_TYPES = ["command", "prompt"];
688
+ }
689
+ });
601
690
  function capabilityLevel(capability) {
602
691
  return typeof capability === "string" ? capability : capability.level;
603
692
  }
@@ -665,8 +754,17 @@ var init_target_descriptor_schema = __esm({
665
754
  managedOutputsSchema = z.object({
666
755
  dirs: z.array(z.string()),
667
756
  files: z.array(z.string()),
668
- coOwnedFiles: z.array(z.string()).optional()
757
+ coOwnedFiles: z.array(z.string()).optional(),
758
+ supersededFiles: z.array(z.string()).optional()
669
759
  }).passthrough().superRefine((value, ctx) => {
760
+ for (const file of value.supersededFiles ?? []) {
761
+ if (!value.files.includes(file) && !(value.coOwnedFiles ?? []).includes(file)) continue;
762
+ ctx.addIssue({
763
+ code: "custom",
764
+ path: ["supersededFiles"],
765
+ message: `"${file}" is in managedOutputs.supersededFiles and another managedOutputs list.`
766
+ });
767
+ }
670
768
  for (const file of value.coOwnedFiles ?? []) {
671
769
  if (!value.files.includes(file)) continue;
672
770
  ctx.addIssue({
@@ -1007,11 +1105,25 @@ var init_command_skill = __esm({
1007
1105
  LEGACY_CODEX_COMMAND_SKILL_PREFIX = "ab-command-";
1008
1106
  }
1009
1107
  });
1108
+ var RECALL_HOOK_COMMAND;
1109
+ var init_recall_hook_scaffold = __esm({
1110
+ "src/lessons/recall-hook-scaffold.ts"() {
1111
+ RECALL_HOOK_COMMAND = "agentsmesh lessons hook";
1112
+ }
1113
+ });
1010
1114
 
1011
1115
  // src/core/hook-types.ts
1116
+ function isBestEffortHookEvent(event, entries) {
1117
+ if (!BEST_EFFORT_HOOK_EVENTS.has(event)) return false;
1118
+ if (!Array.isArray(entries)) return true;
1119
+ return entries.every(
1120
+ (entry) => typeof entry === "object" && entry !== null && typeof entry.command === "string" && entry.command.includes(RECALL_HOOK_COMMAND)
1121
+ );
1122
+ }
1012
1123
  var BEST_EFFORT_HOOK_EVENTS;
1013
1124
  var init_hook_types = __esm({
1014
1125
  "src/core/hook-types.ts"() {
1126
+ init_recall_hook_scaffold();
1015
1127
  BEST_EFFORT_HOOK_EVENTS = /* @__PURE__ */ new Set([
1016
1128
  "UserPromptSubmit",
1017
1129
  "PostToolUseFailure",
@@ -1409,6 +1521,7 @@ function extractEmbeddedRules(content) {
1409
1521
  var ROOT_CONTRACT_START, ROOT_CONTRACT_END, EMBEDDED_RULES_START, EMBEDDED_RULES_END, EMBEDDED_RULE_END, EMBEDDED_RULE_START_PREFIX, EMBEDDED_RULE_START_SUFFIX;
1410
1522
  var init_managed_blocks = __esm({
1411
1523
  "src/targets/projection/managed-blocks.ts"() {
1524
+ init_markdown();
1412
1525
  ROOT_CONTRACT_START = "<!-- agentsmesh:root-generation-contract:start -->";
1413
1526
  ROOT_CONTRACT_END = "<!-- agentsmesh:root-generation-contract:end -->";
1414
1527
  EMBEDDED_RULES_START = "<!-- agentsmesh:embedded-rules:start -->";
@@ -1570,10 +1683,17 @@ async function serializeImportedCommandWithFallback(destinationPath, imported, b
1570
1683
  })();
1571
1684
  const description = imported.hasDescription ? imported.description ?? "" : typeof existingFrontmatter.description === "string" ? existingFrontmatter.description : "";
1572
1685
  const allowedTools = imported.hasAllowedTools ? imported.allowedTools ?? [] : existingAllowedTools;
1686
+ const {
1687
+ description: _d,
1688
+ "allowed-tools": _a,
1689
+ allowedTools: _c,
1690
+ ...preserved
1691
+ } = existingFrontmatter;
1573
1692
  return serializeFrontmatter(
1574
1693
  {
1575
1694
  description,
1576
- "allowed-tools": allowedTools
1695
+ "allowed-tools": allowedTools,
1696
+ ...preserved
1577
1697
  },
1578
1698
  body.trim() || ""
1579
1699
  );
@@ -2471,6 +2591,26 @@ var init_link_rebaser_resolution = __esm({
2471
2591
  }
2472
2592
  });
2473
2593
 
2594
+ // src/core/reference/link-uri-encoding.ts
2595
+ function decodeLinkPath(token, role) {
2596
+ if (role !== "markdown-link-dest" || !token.includes("%")) return token;
2597
+ try {
2598
+ return decodeURIComponent(token);
2599
+ } catch {
2600
+ return token;
2601
+ }
2602
+ }
2603
+ function encodeLinkPath(path, enabled) {
2604
+ if (!enabled) return path;
2605
+ return path.split("/").map(
2606
+ (segment) => segment === "" || segment === "." || segment === ".." ? segment : encodeURIComponent(segment)
2607
+ ).join("/");
2608
+ }
2609
+ var init_link_uri_encoding = __esm({
2610
+ "src/core/reference/link-uri-encoding.ts"() {
2611
+ }
2612
+ });
2613
+
2474
2614
  // src/core/reference/link-token-guards.ts
2475
2615
  function isTildeHomeRelativePathToken(fullContent, matchOffset, matchText) {
2476
2616
  if (matchOffset >= 2 && fullContent[matchOffset - 2] === "~" && fullContent[matchOffset - 1] === "/") {
@@ -2600,10 +2740,11 @@ function rewriteFileLinks(input) {
2600
2740
  const { candidate: punctStripped, suffix } = stripTrailingPunctuation(match);
2601
2741
  if (!punctStripped) return match;
2602
2742
  const lineNumMatch = LINE_NUMBER_SUFFIX.exec(punctStripped);
2603
- const candidate = lineNumMatch ? punctStripped.slice(0, lineNumMatch.index) : punctStripped;
2743
+ const rawCandidate = lineNumMatch ? punctStripped.slice(0, lineNumMatch.index) : punctStripped;
2604
2744
  const lineNumSuffix = lineNumMatch ? lineNumMatch[0] : "";
2605
- if (!candidate) return match;
2606
- const tokenContext = getTokenContext(fullContent, offset, offset + candidate.length);
2745
+ if (!rawCandidate) return match;
2746
+ const tokenContext = getTokenContext(fullContent, offset, offset + rawCandidate.length);
2747
+ const candidate = decodeLinkPath(rawCandidate, tokenContext.role);
2607
2748
  if (tokenContext.role !== "markdown-link-dest" && WINDOWS_ABSOLUTE_PATH.test(candidate)) {
2608
2749
  return match;
2609
2750
  }
@@ -2673,7 +2814,7 @@ function rewriteFileLinks(input) {
2673
2814
  const targetTop = targetFromRoot.split("/").filter(Boolean)[0] ?? "";
2674
2815
  const tokenIsCanonicalMesh = normalizeSeparators(candidate).startsWith(".agentsmesh/");
2675
2816
  const preferRelativeProseInSameSurface = !tokenIsCanonicalMesh && !targetIsDirectory && destTop.length > 0 && destTop === targetTop && destTop.startsWith(".") && destTop !== ".agentsmesh";
2676
- const forceRelative = preferRelativeProseInSameSurface || tokenContext.role === "markdown-link-dest" || isMarkdownLinkDestinationToken(fullContent, offset, candidate);
2817
+ const forceRelative = preferRelativeProseInSameSurface || tokenContext.role === "markdown-link-dest" || isMarkdownLinkDestinationToken(fullContent, offset, rawCandidate);
2677
2818
  const rewritten = formatLinkPathForDestination(
2678
2819
  input.projectRoot,
2679
2820
  input.destinationFile,
@@ -2690,7 +2831,7 @@ function rewriteFileLinks(input) {
2690
2831
  }
2691
2832
  );
2692
2833
  if (!rewritten) return match;
2693
- return `${rewritten}${lineNumSuffix}${suffix}`;
2834
+ return `${encodeLinkPath(rewritten, candidate !== rawCandidate)}${lineNumSuffix}${suffix}`;
2694
2835
  });
2695
2836
  return { content, missing: [...missing] };
2696
2837
  }
@@ -2700,6 +2841,7 @@ var init_link_rebaser = __esm({
2700
2841
  init_link_rebaser_helpers();
2701
2842
  init_link_rebaser_output();
2702
2843
  init_link_rebaser_resolution();
2844
+ init_link_uri_encoding();
2703
2845
  init_link_token_guards();
2704
2846
  init_link_token_context();
2705
2847
  }
@@ -3504,7 +3646,7 @@ function unsupportedHookEventNames(hooks, supportedEvents) {
3504
3646
  if (!hooks) return [];
3505
3647
  const supported = new Set(supportedEvents);
3506
3648
  return Object.keys(hooks).filter(
3507
- (event) => !supported.has(event) && !BEST_EFFORT_HOOK_EVENTS.has(event)
3649
+ (event) => !supported.has(event) && !isBestEffortHookEvent(event, hooks[event])
3508
3650
  );
3509
3651
  }
3510
3652
  function createUnsupportedHookWarning(event, target34, supportedEvents, options) {
@@ -8816,10 +8958,10 @@ var init_claude_code2 = __esm({
8816
8958
  skillDir: ".claude/skills",
8817
8959
  managedOutputs: {
8818
8960
  dirs: [".claude/agents", ".claude/commands", ".claude/rules", ".claude/skills"],
8819
- // CLAUDE_NESTED_ROOT is the pre-migration project location; listing it here lets
8820
- // `cleanupStaleGeneratedOutputs` evict a leftover `.claude/CLAUDE.md` once generation
8961
+ files: [CLAUDE_ROOT, ".claudeignore"],
8962
+ // CLAUDE_NESTED_ROOT is the pre-migration project location: evicted once a run
8821
8963
  // writes the root `CLAUDE.md`, so Claude Code never concatenates both into context.
8822
- files: [CLAUDE_ROOT, CLAUDE_NESTED_ROOT, ".claudeignore"],
8964
+ supersededFiles: [CLAUDE_NESTED_ROOT],
8823
8965
  // `.mcp.json` is the shared project MCP file teams hand-commit and
8824
8966
  // deepagents-cli writes too; agentsmesh owns only `mcpServers` in it.
8825
8967
  // `.claude/settings.json` is co-owned through the `SETTINGS_JSON_PATHS`
@@ -10050,9 +10192,8 @@ var init_cline2 = __esm({
10050
10192
  };
10051
10193
  }
10052
10194
  });
10053
- function ruleSlug3(source) {
10054
- return basename(source, ".md");
10055
- }
10195
+
10196
+ // src/targets/codex-cli/codex-rule-paths.ts
10056
10197
  function directoryFromGlob(glob) {
10057
10198
  let normalized = glob.trim();
10058
10199
  if (normalized.startsWith("./")) normalized = normalized.slice(2);
@@ -10073,12 +10214,12 @@ function codexRuleDirectory(rule) {
10073
10214
  const dir = directoryFromGlob(glob);
10074
10215
  if (dir) return dir;
10075
10216
  }
10076
- return ruleSlug3(rule.source);
10217
+ return null;
10077
10218
  }
10078
10219
  function codexNestedAgentsPath(rule) {
10079
10220
  const dir = codexRuleDirectory(rule);
10080
10221
  const filename = rule.codexInstructionVariant === "override" ? "AGENTS.override.md" : "AGENTS.md";
10081
- return `${dir}/${filename}`;
10222
+ return dir === null ? filename : `${dir}/${filename}`;
10082
10223
  }
10083
10224
  var GLOB_METACHAR;
10084
10225
  var init_codex_rule_paths = __esm({
@@ -10086,10 +10227,9 @@ var init_codex_rule_paths = __esm({
10086
10227
  GLOB_METACHAR = /[*?[\]]/;
10087
10228
  }
10088
10229
  });
10089
-
10090
- // src/targets/codebuff/rule-paths.ts
10091
10230
  function codebuffNestedKnowledgePath(rule) {
10092
- return `${codexRuleDirectory(rule)}/AGENTS.md`;
10231
+ const dir = codexRuleDirectory(rule) ?? basename(rule.source, ".md");
10232
+ return `${dir}/AGENTS.md`;
10093
10233
  }
10094
10234
  var init_rule_paths = __esm({
10095
10235
  "src/targets/codebuff/rule-paths.ts"() {
@@ -10700,6 +10840,9 @@ function eligibleAdvisoryRules(canonical) {
10700
10840
  return rule.targets.length === 0 || rule.targets.includes("codex-cli");
10701
10841
  });
10702
10842
  }
10843
+ function isRootEmbedded(rule) {
10844
+ return codexNestedAgentsPath(rule) === AGENTS_MD;
10845
+ }
10703
10846
  function groupByNestedPath2(rules) {
10704
10847
  const groups = /* @__PURE__ */ new Map();
10705
10848
  for (const rule of rules) {
@@ -10712,9 +10855,11 @@ function groupByNestedPath2(rules) {
10712
10855
  }
10713
10856
  function generateRules9(canonical) {
10714
10857
  const root = canonical.rules.find((r) => r.root);
10858
+ const advisory = eligibleAdvisoryRules(canonical);
10715
10859
  const outputs = [];
10716
10860
  if (root) {
10717
- outputs.push({ path: AGENTS_MD, content: root.body.trim() });
10861
+ const content = appendEmbeddedRulesBlock(root.body.trim(), advisory.filter(isRootEmbedded));
10862
+ outputs.push({ path: AGENTS_MD, content });
10718
10863
  }
10719
10864
  for (const rule of canonical.rules) {
10720
10865
  if (rule.root) continue;
@@ -10726,7 +10871,8 @@ function generateRules9(canonical) {
10726
10871
  content: toSafeCodexRulesContent(rule.body)
10727
10872
  });
10728
10873
  }
10729
- for (const [path, rules] of groupByNestedPath2(eligibleAdvisoryRules(canonical))) {
10874
+ const nested = advisory.filter((rule) => !isRootEmbedded(rule));
10875
+ for (const [path, rules] of groupByNestedPath2(nested)) {
10730
10876
  const content = rules.map((rule) => rule.body.trim()).filter((body) => body.length > 0).join("\n\n");
10731
10877
  outputs.push({ path, content });
10732
10878
  }
@@ -10953,7 +11099,7 @@ function generateHooks6(canonical) {
10953
11099
  supportedEvents: CODEX_SUPPORTED_HOOK_EVENTS
10954
11100
  });
10955
11101
  }
10956
- var init_hooks = __esm({
11102
+ var init_hooks2 = __esm({
10957
11103
  "src/targets/codex-cli/generator/hooks.ts"() {
10958
11104
  init_wrapped_command_hooks();
10959
11105
  init_constants10();
@@ -11034,7 +11180,7 @@ var init_generator9 = __esm({
11034
11180
  init_skills();
11035
11181
  init_agents();
11036
11182
  init_mcp2();
11037
- init_hooks();
11183
+ init_hooks2();
11038
11184
  init_permissions();
11039
11185
  }
11040
11186
  });
@@ -11551,6 +11697,23 @@ var init_linter9 = __esm({
11551
11697
  });
11552
11698
 
11553
11699
  // src/targets/codex-cli/lint.ts
11700
+ function lintAgents3(canonical) {
11701
+ const diagnostics = [];
11702
+ for (const agent of canonical.agents) {
11703
+ const dropped = CODEX_DROPPED_AGENT_FIELDS.filter(
11704
+ (field) => hasAgentValue(agent, field)
11705
+ ).sort();
11706
+ if (dropped.length === 0) continue;
11707
+ diagnostics.push(
11708
+ createWarning(
11709
+ agent.source,
11710
+ "codex-cli",
11711
+ `Codex agent TOML supports name, description, developer_instructions, model, sandbox_mode and mcp_servers; canonical ${dropped.join(", ")} are not projected to ${CODEX_AGENTS_DIR}/${agent.name}.toml.`
11712
+ )
11713
+ );
11714
+ }
11715
+ return diagnostics;
11716
+ }
11554
11717
  function lintMcp4(canonical) {
11555
11718
  if (!canonical.mcp || Object.keys(canonical.mcp.mcpServers).length === 0) return [];
11556
11719
  const diagnostics = [];
@@ -11582,11 +11745,21 @@ function lintHooks7(canonical) {
11582
11745
  (event) => createUnsupportedHookWarning(event, "codex-cli", CODEX_SUPPORTED_HOOK_EVENTS)
11583
11746
  );
11584
11747
  }
11748
+ var CODEX_DROPPED_AGENT_FIELDS;
11585
11749
  var init_lint8 = __esm({
11586
11750
  "src/targets/codex-cli/lint.ts"() {
11587
11751
  init_helpers();
11588
11752
  init_mcp_servers();
11753
+ init_agents_format();
11589
11754
  init_constants10();
11755
+ CODEX_DROPPED_AGENT_FIELDS = [
11756
+ "tools",
11757
+ "disallowedTools",
11758
+ "maxTurns",
11759
+ "hooks",
11760
+ "skills",
11761
+ "memory"
11762
+ ];
11590
11763
  }
11591
11764
  });
11592
11765
 
@@ -11661,6 +11834,7 @@ var init_codex_cli2 = __esm({
11661
11834
  generateMcp: generateMcp6,
11662
11835
  generateHooks: generateHooks6,
11663
11836
  generatePermissions: generatePermissions7,
11837
+ lint: lintAgents3,
11664
11838
  importFrom: importFromCodex
11665
11839
  };
11666
11840
  project6 = {
@@ -11708,7 +11882,7 @@ var init_codex_cli2 = __esm({
11708
11882
  },
11709
11883
  rewriteGeneratedPath(path) {
11710
11884
  if (path === AGENTS_MD) return CODEX_GLOBAL_AGENTS_MD;
11711
- if (/\/AGENTS(\.override)?\.md$/.test(path)) return null;
11885
+ if (/(^|\/)AGENTS(\.override)?\.md$/.test(path)) return null;
11712
11886
  if (path.startsWith(`${CODEX_INSTRUCTIONS_DIR}/`)) return null;
11713
11887
  return path;
11714
11888
  },
@@ -11975,7 +12149,7 @@ async function importContinueHooks(projectRoot, results) {
11975
12149
  });
11976
12150
  }
11977
12151
  var CONTINUE_HOOK_EVENTS, mergeContinueSettings;
11978
- var init_hooks2 = __esm({
12152
+ var init_hooks3 = __esm({
11979
12153
  "src/targets/continue/hooks.ts"() {
11980
12154
  init_hooks_format2();
11981
12155
  init_settings_helpers2();
@@ -12243,7 +12417,7 @@ var init_importer10 = __esm({
12243
12417
  init_shared_import_helpers();
12244
12418
  init_constants11();
12245
12419
  init_permissions2();
12246
- init_hooks2();
12420
+ init_hooks3();
12247
12421
  init_continue2();
12248
12422
  }
12249
12423
  });
@@ -12364,7 +12538,7 @@ function hasValue(agent, field) {
12364
12538
  if (value && typeof value === "object") return Object.keys(value).length > 0;
12365
12539
  return false;
12366
12540
  }
12367
- function lintAgents3(canonical) {
12541
+ function lintAgents4(canonical) {
12368
12542
  const diagnostics = [];
12369
12543
  for (const agent of canonical.agents) {
12370
12544
  const dropped = DROPPED_FIELDS.filter((field) => hasValue(agent, field)).sort();
@@ -12384,7 +12558,7 @@ var init_lint9 = __esm({
12384
12558
  "src/targets/continue/lint.ts"() {
12385
12559
  init_helpers();
12386
12560
  init_constants11();
12387
- init_hooks2();
12561
+ init_hooks3();
12388
12562
  DROPPED_FIELDS = [
12389
12563
  "disallowedTools",
12390
12564
  "permissionMode",
@@ -12510,7 +12684,7 @@ var target10, descriptor10;
12510
12684
  var init_continue2 = __esm({
12511
12685
  "src/targets/continue/index.ts"() {
12512
12686
  init_generator11();
12513
- init_hooks2();
12687
+ init_hooks3();
12514
12688
  init_config_merge2();
12515
12689
  init_capabilities5();
12516
12690
  init_layout4();
@@ -12532,7 +12706,7 @@ var init_continue2 = __esm({
12532
12706
  generateHooks: generateHooks7,
12533
12707
  generateIgnore: generateIgnore8,
12534
12708
  // Feature-independent lint hook: agent warnings must not hang off `rules`.
12535
- lint: lintAgents3,
12709
+ lint: lintAgents4,
12536
12710
  importFrom: importFromContinue
12537
12711
  };
12538
12712
  descriptor10 = {
@@ -12789,7 +12963,7 @@ var init_hook_format = __esm({
12789
12963
  init_hook_entry();
12790
12964
  }
12791
12965
  });
12792
- function ruleSlug4(source) {
12966
+ function ruleSlug3(source) {
12793
12967
  const name = basename(source, ".md");
12794
12968
  return name === "_root" ? "root" : name;
12795
12969
  }
@@ -12823,7 +12997,7 @@ function generateRules11(canonical) {
12823
12997
  if (rule.root) continue;
12824
12998
  if (rule.targets.length > 0 && !rule.targets.includes("copilot")) continue;
12825
12999
  if (rule.globs.length === 0) continue;
12826
- const slug = ruleSlug4(rule.source);
13000
+ const slug = ruleSlug3(rule.source);
12827
13001
  const frontmatter = {
12828
13002
  description: rule.description || void 0,
12829
13003
  applyTo: rule.globs.length === 1 ? rule.globs[0] : rule.globs
@@ -14312,13 +14486,18 @@ var init_rules2 = __esm({
14312
14486
 
14313
14487
  // src/targets/cursor/generator/commands.ts
14314
14488
  function generateCommands13(canonical) {
14315
- return canonical.commands.map((cmd) => ({
14316
- path: `${CURSOR_COMMANDS_DIR}/${cmd.name}.md`,
14317
- content: cmd.body.trim() || ""
14318
- }));
14489
+ return canonical.commands.map((cmd) => {
14490
+ const frontmatter = {};
14491
+ if (cmd.description) frontmatter.description = cmd.description;
14492
+ return {
14493
+ path: `${CURSOR_COMMANDS_DIR}/${cmd.name}.md`,
14494
+ content: serializeFrontmatter(frontmatter, cmd.body.trim() || "")
14495
+ };
14496
+ });
14319
14497
  }
14320
14498
  var init_commands = __esm({
14321
14499
  "src/targets/cursor/generator/commands.ts"() {
14500
+ init_markdown();
14322
14501
  init_constants13();
14323
14502
  }
14324
14503
  });
@@ -14413,7 +14592,7 @@ var init_permissions3 = __esm({
14413
14592
  // src/targets/cursor/hook-format.ts
14414
14593
  function unmappedCursorHookEvents(hooks) {
14415
14594
  return Object.keys(hooks).filter(
14416
- (event) => Array.isArray(hooks[event]) && hooks[event].length > 0 && !(event in CANONICAL_TO_CURSOR) && !BEST_EFFORT_HOOK_EVENTS.has(event)
14595
+ (event) => Array.isArray(hooks[event]) && hooks[event].length > 0 && !(event in CANONICAL_TO_CURSOR) && !isBestEffortHookEvent(event, hooks[event])
14417
14596
  );
14418
14597
  }
14419
14598
  function toCursorHooks(hooks) {
@@ -14488,7 +14667,7 @@ function generateHooks10(canonical) {
14488
14667
  const content = JSON.stringify({ version: 1, hooks: cursorHooks }, null, 2);
14489
14668
  return [{ path: CURSOR_HOOKS, content }];
14490
14669
  }
14491
- var init_hooks3 = __esm({
14670
+ var init_hooks4 = __esm({
14492
14671
  "src/targets/cursor/generator/hooks.ts"() {
14493
14672
  init_constants13();
14494
14673
  init_hook_format2();
@@ -14516,7 +14695,7 @@ var init_generator14 = __esm({
14516
14695
  init_skills2();
14517
14696
  init_agents2();
14518
14697
  init_permissions3();
14519
- init_hooks3();
14698
+ init_hooks4();
14520
14699
  init_ignore();
14521
14700
  }
14522
14701
  });
@@ -14851,14 +15030,11 @@ async function hasGlobalCursorArtifacts(projectRoot) {
14851
15030
  join(projectRoot, CURSOR_GLOBAL_USER_RULES),
14852
15031
  join(projectRoot, CURSOR_MCP),
14853
15032
  join(projectRoot, CURSOR_HOOKS),
14854
- join(projectRoot, CURSOR_IGNORE),
14855
- join(projectRoot, CURSOR_SKILLS_DIR),
14856
- join(projectRoot, CURSOR_AGENTS_DIR),
14857
- join(projectRoot, CURSOR_COMMANDS_DIR)
15033
+ join(projectRoot, CURSOR_IGNORE)
14858
15034
  ];
14859
15035
  for (const p of candidates) {
14860
- const stat7 = await readFileSafe(p);
14861
- if (stat7 !== null && stat7.trim() !== "") return true;
15036
+ const content = await readFileSafe(p);
15037
+ if (content !== null && content.trim() !== "") return true;
14862
15038
  }
14863
15039
  const skillFiles = await readDirRecursiveNoSymlinks(join(projectRoot, CURSOR_SKILLS_DIR));
14864
15040
  if (skillFiles.some((f) => f.endsWith(".md"))) return true;
@@ -15145,11 +15321,11 @@ function lintHooks10(canonical) {
15145
15321
  ];
15146
15322
  }
15147
15323
  function lintCommands6(canonical) {
15148
- return canonical.commands.filter((command) => command.description.length > 0 || command.allowedTools.length > 0).map(
15324
+ return canonical.commands.filter((command) => command.allowedTools.length > 0).map(
15149
15325
  (command) => createWarning(
15150
15326
  command.source,
15151
15327
  "cursor",
15152
- "Cursor command files are plain Markdown; command description and allowed-tools metadata are not projected."
15328
+ "Cursor command files project only description frontmatter; allowed-tools metadata is not projected."
15153
15329
  )
15154
15330
  );
15155
15331
  }
@@ -15482,7 +15658,7 @@ var init_mcp_merge4 = __esm({
15482
15658
  // src/targets/deepagents-cli/hooks-format.ts
15483
15659
  function unmappedDeepagentsHookEvents(hooks) {
15484
15660
  return Object.keys(hooks).filter(
15485
- (event) => Array.isArray(hooks[event]) && hooks[event].length > 0 && !(event in CANONICAL_TO_DEEPAGENTS) && !BEST_EFFORT_HOOK_EVENTS.has(event)
15661
+ (event) => Array.isArray(hooks[event]) && hooks[event].length > 0 && !(event in CANONICAL_TO_DEEPAGENTS) && !isBestEffortHookEvent(event, hooks[event])
15486
15662
  );
15487
15663
  }
15488
15664
  function toDeepagentsHooks(hooks) {
@@ -16050,7 +16226,8 @@ var init_deepagents_cli2 = __esm({
16050
16226
  }
16051
16227
  },
16052
16228
  buildImportPaths: buildDeepagentsCliImportPaths,
16053
- detectionPaths: [DEEPAGENTS_CLI_ROOT_FILE, DEEPAGENTS_CLI_MCP_FILE]
16229
+ // `.mcp.json` is co-owned with claude-code (agentsmesh writes it), so it must not enroll this target.
16230
+ detectionPaths: [DEEPAGENTS_CLI_ROOT_FILE]
16054
16231
  };
16055
16232
  }
16056
16233
  });
@@ -20028,7 +20205,8 @@ var init_layout8 = __esm({
20028
20205
  skillDir: KIMI_CODE_SKILLS_DIR,
20029
20206
  managedOutputs: {
20030
20207
  dirs: [KIMI_CODE_AGENTS_DIR, KIMI_CODE_SKILLS_DIR],
20031
- files: [KIMI_CODE_ROOT_FILE, KIMI_CODE_NESTED_ROOT_FILE],
20208
+ files: [KIMI_CODE_ROOT_FILE],
20209
+ supersededFiles: [KIMI_CODE_NESTED_ROOT_FILE],
20032
20210
  // Kimi Code's own MCP config, in the same directory as the credential-
20033
20211
  // bearing config.toml this layout already refuses to delete.
20034
20212
  coOwnedFiles: [KIMI_CODE_MCP_FILE]
@@ -20676,7 +20854,7 @@ function lintMcp9(canonical) {
20676
20854
  }
20677
20855
  return diagnostics;
20678
20856
  }
20679
- function lintAgents4(canonical) {
20857
+ function lintAgents5(canonical) {
20680
20858
  return canonical.agents.flatMap((agent) => {
20681
20859
  const dropped = DROPPED_AGENT_FIELDS.filter(([, has]) => has(agent)).map(([field]) => field);
20682
20860
  if (dropped.length === 0) return [];
@@ -20767,7 +20945,7 @@ var init_kimi_code2 = __esm({
20767
20945
  generateHooks: generateHooks14,
20768
20946
  generatePermissions: generatePermissions16,
20769
20947
  importFrom: importFromKimiCode,
20770
- lint: lintAgents4
20948
+ lint: lintAgents5
20771
20949
  };
20772
20950
  capabilities9 = {
20773
20951
  rules: "native",
@@ -23065,7 +23243,7 @@ function lintAgentFields(agent) {
23065
23243
  )
23066
23244
  ];
23067
23245
  }
23068
- function lintAgents5(canonical) {
23246
+ function lintAgents6(canonical) {
23069
23247
  const diagnostics = [];
23070
23248
  for (const agent of canonical.agents) {
23071
23249
  diagnostics.push(...lintAgentFields(agent));
@@ -23127,7 +23305,7 @@ var init_openhands2 = __esm({
23127
23305
  generatePermissions: generatePermissions19,
23128
23306
  importFrom: importFromOpenhands,
23129
23307
  // Ungated by feature, so agent-only feature sets still get the warning.
23130
- lint: lintAgents5
23308
+ lint: lintAgents6
23131
23309
  };
23132
23310
  descriptor24 = {
23133
23311
  id: OPENHANDS_TARGET,
@@ -26942,7 +27120,7 @@ var init_constants34 = __esm({
26942
27120
  WINDSURF_GLOBAL_AGENTS_SKILLS_DIR = ".agents/skills";
26943
27121
  }
26944
27122
  });
26945
- function ruleSlug5(source) {
27123
+ function ruleSlug4(source) {
26946
27124
  const name = basename(source, ".md");
26947
27125
  return name === "_root" ? "root" : name;
26948
27126
  }
@@ -26963,7 +27141,7 @@ function generateRules32(canonical) {
26963
27141
  for (const rule of canonical.rules) {
26964
27142
  if (rule.root) continue;
26965
27143
  if (rule.targets.length > 0 && !rule.targets.includes("windsurf")) continue;
26966
- const slug = ruleSlug5(rule.source);
27144
+ const slug = ruleSlug4(rule.source);
26967
27145
  const normalizedTrigger = rule.trigger || (rule.globs.length > 0 ? "glob" : void 0);
26968
27146
  const frontmatter = {
26969
27147
  description: rule.description || void 0,
@@ -27067,19 +27245,34 @@ var init_mcp4 = __esm({
27067
27245
  }
27068
27246
  });
27069
27247
 
27070
- // src/targets/windsurf/generator/hooks.ts
27248
+ // src/targets/windsurf/hook-events.ts
27071
27249
  function windsurfEventName(event) {
27072
- const explicit = {
27073
- PreToolUse: "pre_tool_use",
27074
- PostToolUse: "post_tool_use",
27075
- Notification: "notification",
27076
- UserPromptSubmit: "user_prompt_submit",
27077
- SubagentStart: "subagent_start",
27078
- SubagentStop: "subagent_stop"
27079
- };
27080
- if (explicit[event]) return explicit[event];
27081
27250
  return event.replace(/([a-z0-9])([A-Z])/g, "$1_$2").replace(/[-\s]+/g, "_").toLowerCase();
27082
27251
  }
27252
+ function canonicalHookEventName(event) {
27253
+ if (KNOWN_CANONICAL_HOOK_EVENTS.includes(event)) return event;
27254
+ return WINDSURF_TO_CANONICAL.get(event) ?? null;
27255
+ }
27256
+ var KNOWN_CANONICAL_HOOK_EVENTS, WINDSURF_TO_CANONICAL;
27257
+ var init_hook_events = __esm({
27258
+ "src/targets/windsurf/hook-events.ts"() {
27259
+ init_hook_types();
27260
+ KNOWN_CANONICAL_HOOK_EVENTS = [
27261
+ "PreToolUse",
27262
+ "PostToolUse",
27263
+ "Notification",
27264
+ "UserPromptSubmit",
27265
+ "SubagentStart",
27266
+ "SubagentStop",
27267
+ ...BEST_EFFORT_HOOK_EVENTS
27268
+ ];
27269
+ WINDSURF_TO_CANONICAL = new Map(
27270
+ KNOWN_CANONICAL_HOOK_EVENTS.map((event) => [windsurfEventName(event), event])
27271
+ );
27272
+ }
27273
+ });
27274
+
27275
+ // src/targets/windsurf/generator/hooks.ts
27083
27276
  function toWindsurfHooks(hooks) {
27084
27277
  const result2 = {};
27085
27278
  for (const [event, entries] of Object.entries(hooks)) {
@@ -27103,10 +27296,11 @@ function generateHooks22(canonical) {
27103
27296
  if (Object.keys(hooks).length === 0) return [];
27104
27297
  return [{ path: WINDSURF_HOOKS_FILE, content: JSON.stringify({ hooks }, null, 2) }];
27105
27298
  }
27106
- var init_hooks4 = __esm({
27299
+ var init_hooks5 = __esm({
27107
27300
  "src/targets/windsurf/generator/hooks.ts"() {
27108
27301
  init_hook_command();
27109
27302
  init_constants34();
27303
+ init_hook_events();
27110
27304
  }
27111
27305
  });
27112
27306
 
@@ -27154,7 +27348,7 @@ var init_generator35 = __esm({
27154
27348
  init_workflows();
27155
27349
  init_agents4();
27156
27350
  init_mcp4();
27157
- init_hooks4();
27351
+ init_hooks5();
27158
27352
  init_skills4();
27159
27353
  init_permissions6();
27160
27354
  }
@@ -27245,9 +27439,10 @@ async function importWindsurfHooks(projectRoot, results) {
27245
27439
  try {
27246
27440
  const parsed = JSON.parse(hooksContent);
27247
27441
  if (!parsed.hooks || typeof parsed.hooks !== "object" || Array.isArray(parsed.hooks)) return;
27248
- const canonical = windsurfHooksToCanonical(parsed.hooks);
27249
- if (Object.keys(canonical).length === 0) return;
27250
27442
  const destPath = join(projectRoot, WINDSURF_CANONICAL_HOOKS);
27443
+ const existing = await parseHooks(destPath) ?? {};
27444
+ const canonical = windsurfHooksToCanonical(parsed.hooks, existing);
27445
+ if (Object.keys(canonical).length === 0) return;
27251
27446
  await mkdirp(dirname(destPath));
27252
27447
  await writeFileAtomic(destPath, stringify(canonical));
27253
27448
  results.push({
@@ -27259,54 +27454,63 @@ async function importWindsurfHooks(projectRoot, results) {
27259
27454
  } catch {
27260
27455
  }
27261
27456
  }
27262
- function canonicalHookEventName(event) {
27263
- const explicit = {
27264
- pre_tool_use: "PreToolUse",
27265
- post_tool_use: "PostToolUse",
27266
- notification: "Notification",
27267
- user_prompt_submit: "UserPromptSubmit",
27268
- subagent_start: "SubagentStart",
27269
- subagent_stop: "SubagentStop"
27270
- };
27271
- return explicit[event] ?? event;
27457
+ function preservedMatcher(existing, event, command) {
27458
+ const match = existing[event]?.find((entry) => entry.command === command);
27459
+ return match?.matcher ?? WILDCARD_MATCHER;
27272
27460
  }
27273
- function windsurfHooksToCanonical(hooks) {
27461
+ function legacyEntries(entry) {
27462
+ const matcher = typeof entry.matcher === "string" && entry.matcher.trim() ? entry.matcher : WILDCARD_MATCHER;
27463
+ const hooksList = Array.isArray(entry.hooks) ? entry.hooks : [];
27464
+ const out2 = [];
27465
+ for (const item of hooksList) {
27466
+ if (!item || typeof item !== "object") continue;
27467
+ const hook = item;
27468
+ const command = typeof hook.command === "string" ? hook.command : typeof hook.prompt === "string" ? hook.prompt : "";
27469
+ if (!command.trim()) continue;
27470
+ const canonical = {
27471
+ matcher,
27472
+ type: hook.type === "prompt" ? "prompt" : "command",
27473
+ command
27474
+ };
27475
+ if (typeof hook.timeout === "number") canonical.timeout = hook.timeout;
27476
+ out2.push(canonical);
27477
+ }
27478
+ return out2;
27479
+ }
27480
+ function windsurfHooksToCanonical(hooks, existing) {
27274
27481
  const result2 = {};
27275
27482
  for (const [event, entries] of Object.entries(hooks)) {
27276
27483
  if (!Array.isArray(entries)) continue;
27277
27484
  const mappedEvent = canonicalHookEventName(event);
27485
+ if (mappedEvent === null) continue;
27278
27486
  const canonicalEntries = [];
27279
27487
  for (const entry of entries) {
27280
27488
  if (!entry || typeof entry !== "object") continue;
27281
27489
  const e = entry;
27282
27490
  if (typeof e.command === "string" && e.command.trim()) {
27283
27491
  canonicalEntries.push({
27284
- matcher: ".*",
27492
+ matcher: preservedMatcher(existing, mappedEvent, e.command),
27285
27493
  type: "command",
27286
27494
  command: e.command
27287
27495
  });
27288
27496
  continue;
27289
27497
  }
27290
- const matcher = typeof e.matcher === "string" && e.matcher.trim() ? e.matcher : ".*";
27291
- const hooksList = Array.isArray(e.hooks) ? e.hooks : [];
27292
- for (const item of hooksList) {
27293
- if (!item || typeof item !== "object") continue;
27294
- const hook = item;
27295
- const command = typeof hook.command === "string" ? hook.command : typeof hook.prompt === "string" ? hook.prompt : "";
27296
- if (!command.trim()) continue;
27297
- const canonical = {
27298
- matcher,
27299
- type: hook.type === "prompt" ? "prompt" : "command",
27300
- command
27301
- };
27302
- if (typeof hook.timeout === "number") canonical.timeout = hook.timeout;
27303
- canonicalEntries.push(canonical);
27304
- }
27498
+ canonicalEntries.push(...legacyEntries(e));
27305
27499
  }
27306
27500
  if (canonicalEntries.length > 0) result2[mappedEvent] = canonicalEntries;
27307
27501
  }
27308
27502
  return result2;
27309
27503
  }
27504
+ var WILDCARD_MATCHER;
27505
+ var init_importer_hooks2 = __esm({
27506
+ "src/targets/windsurf/importer-hooks.ts"() {
27507
+ init_hooks();
27508
+ init_fs();
27509
+ init_constants34();
27510
+ init_hook_events();
27511
+ WILDCARD_MATCHER = "*";
27512
+ }
27513
+ });
27310
27514
  async function importWindsurfMcp(projectRoot, results) {
27311
27515
  const sourceCandidates = [WINDSURF_MCP_EXAMPLE_FILE, WINDSURF_MCP_CONFIG_FILE];
27312
27516
  for (const relPath of sourceCandidates) {
@@ -27330,8 +27534,8 @@ async function importWindsurfMcp(projectRoot, results) {
27330
27534
  }
27331
27535
  }
27332
27536
  }
27333
- var init_importer_hooks_mcp = __esm({
27334
- "src/targets/windsurf/importer-hooks-mcp.ts"() {
27537
+ var init_importer_mcp = __esm({
27538
+ "src/targets/windsurf/importer-mcp.ts"() {
27335
27539
  init_fs();
27336
27540
  init_constants34();
27337
27541
  }
@@ -27481,7 +27685,8 @@ var init_importer32 = __esm({
27481
27685
  init_constants34();
27482
27686
  init_importer_workflows();
27483
27687
  init_skills_adapter5();
27484
- init_importer_hooks_mcp();
27688
+ init_importer_hooks2();
27689
+ init_importer_mcp();
27485
27690
  }
27486
27691
  });
27487
27692
 
@@ -27566,9 +27771,28 @@ function lintPermissions22(canonical) {
27566
27771
  )
27567
27772
  ];
27568
27773
  }
27774
+ function lintHooks23(canonical) {
27775
+ if (!canonical.hooks) return [];
27776
+ const diagnostics = [];
27777
+ for (const [event, entries] of Object.entries(canonical.hooks)) {
27778
+ for (const entry of entries ?? []) {
27779
+ if (WILDCARD_MATCHERS.has(entry.matcher.trim())) continue;
27780
+ diagnostics.push(
27781
+ createWarning(
27782
+ ".agentsmesh/hooks.yaml",
27783
+ "windsurf",
27784
+ `Windsurf hooks have no matcher field; ${event} hook "${entry.command}" runs on every ${event} event (matcher "${entry.matcher}" is not projected).`
27785
+ )
27786
+ );
27787
+ }
27788
+ }
27789
+ return diagnostics;
27790
+ }
27791
+ var WILDCARD_MATCHERS;
27569
27792
  var init_lint31 = __esm({
27570
27793
  "src/targets/windsurf/lint.ts"() {
27571
27794
  init_helpers();
27795
+ WILDCARD_MATCHERS = /* @__PURE__ */ new Set(["", "*", ".*"]);
27572
27796
  }
27573
27797
  });
27574
27798
 
@@ -27724,6 +27948,7 @@ var init_windsurf2 = __esm({
27724
27948
  lintRules: lintRules32,
27725
27949
  lint: {
27726
27950
  commands: lintCommands10,
27951
+ hooks: lintHooks23,
27727
27952
  mcp: lintMcp13,
27728
27953
  permissions: lintPermissions22
27729
27954
  },
@@ -29301,24 +29526,14 @@ async function sweepStaleCache(cacheDir, maxAgeMs) {
29301
29526
 
29302
29527
  // src/config/remote/remote-fetcher.ts
29303
29528
  var MAX_CACHE_KEY_LENGTH = 80;
29529
+ var CACHE_KEY_HASH_LENGTH = 12;
29304
29530
  function buildCacheKey(provider, identifier, ref) {
29305
29531
  const safe = (value) => value.replace(/[^a-zA-Z0-9_.-]/g, "_").replace(/^\.+/, "_");
29306
- let key;
29307
- if (provider === "github") {
29308
- const [org, repo] = identifier.split("/", 2);
29309
- if (org && repo) {
29310
- key = `${safe(org)}--${safe(repo)}--${safe(ref)}`;
29311
- } else {
29312
- key = `${safe(provider)}__${safe(identifier)}__${safe(ref)}`;
29313
- }
29314
- } else {
29315
- key = `${safe(provider)}__${safe(identifier)}__${safe(ref)}`;
29316
- }
29317
- if (key.length > MAX_CACHE_KEY_LENGTH) {
29318
- const hash = createHash("sha256").update(key).digest("hex").slice(0, 16);
29319
- key = `${key.slice(0, MAX_CACHE_KEY_LENGTH - 18)}--${hash}`;
29320
- }
29321
- return key;
29532
+ const [org, repo] = provider === "github" ? identifier.split("/", 2) : [];
29533
+ const readable = org && repo ? `${safe(org)}--${safe(repo)}--${safe(ref)}` : `${safe(provider)}__${safe(identifier)}__${safe(ref)}`;
29534
+ const hash = createHash("sha256").update(`${provider}|${identifier}|${ref}`).digest("hex").slice(0, CACHE_KEY_HASH_LENGTH);
29535
+ const maxReadable = MAX_CACHE_KEY_LENGTH - CACHE_KEY_HASH_LENGTH - 2;
29536
+ return `${readable.slice(0, maxReadable)}--${hash}`;
29322
29537
  }
29323
29538
  function getCacheDir() {
29324
29539
  const env = process.env.AGENTSMESH_CACHE;
@@ -29430,6 +29645,92 @@ async function resolveExtendPaths(config, configDir, options = {}) {
29430
29645
  return result2;
29431
29646
  }
29432
29647
 
29648
+ // src/utils/output/color.ts
29649
+ function noColorRequested() {
29650
+ const value = process.env.NO_COLOR;
29651
+ return value !== void 0 && value !== "";
29652
+ }
29653
+ function forceColorRequested() {
29654
+ const value = process.env.FORCE_COLOR;
29655
+ if (value === void 0) return void 0;
29656
+ return value !== "0" && value !== "false";
29657
+ }
29658
+ function colorEnabled(stream = process.stdout) {
29659
+ const forced = forceColorRequested();
29660
+ if (forced !== void 0) return forced;
29661
+ if (noColorRequested()) return false;
29662
+ return stream.isTTY === true;
29663
+ }
29664
+
29665
+ // src/utils/output/logger.ts
29666
+ var C = {
29667
+ green: "\x1B[32m",
29668
+ red: "\x1B[31m",
29669
+ yellow: "\x1B[33m",
29670
+ cyan: "\x1B[36m",
29671
+ reset: "\x1B[0m"
29672
+ };
29673
+ function outStream() {
29674
+ return process.stdout;
29675
+ }
29676
+ function out(text) {
29677
+ outStream().write(text);
29678
+ }
29679
+ function c(code, text, stream) {
29680
+ return colorEnabled(stream) ? `${code}${text}${C.reset}` : text;
29681
+ }
29682
+ function pad(str3, width) {
29683
+ const len = [...str3].length;
29684
+ return str3 + " ".repeat(Math.max(0, width - len));
29685
+ }
29686
+ var logger = {
29687
+ info(msg) {
29688
+ out(c(C.cyan, msg, outStream()) + "\n");
29689
+ },
29690
+ warn(msg) {
29691
+ process.stderr.write(c(C.yellow, "\u26A0 ", process.stderr) + msg + "\n");
29692
+ },
29693
+ error(msg) {
29694
+ process.stderr.write(c(C.red, "\u2717 ", process.stderr) + msg + "\n");
29695
+ },
29696
+ success(msg) {
29697
+ out(c(C.green, "\u2713 ", outStream()) + msg + "\n");
29698
+ },
29699
+ debug(msg) {
29700
+ if (process.env.AGENTSMESH_DEBUG === "1") {
29701
+ out(c(C.cyan, "[debug] ", outStream()) + msg + "\n");
29702
+ }
29703
+ },
29704
+ table(rows) {
29705
+ if (rows.length === 0) return;
29706
+ const cols = rows[0].length;
29707
+ const widths = [];
29708
+ for (let j = 0; j < cols; j++) {
29709
+ let max = 0;
29710
+ for (let i = 0; i < rows.length; i++) {
29711
+ const len = [...rows[i][j]].length;
29712
+ if (len > max) max = len;
29713
+ }
29714
+ widths[j] = max;
29715
+ }
29716
+ const border = "+" + widths.map((w) => "-".repeat(w + 2)).join("+") + "+";
29717
+ out(border + "\n");
29718
+ for (let i = 0; i < rows.length; i++) {
29719
+ const row = rows[i];
29720
+ const line = "| " + row.map((cell, j) => pad(cell, widths[j])).join(" | ") + " |";
29721
+ out(line + "\n");
29722
+ }
29723
+ out(border + "\n");
29724
+ }
29725
+ };
29726
+
29727
+ // src/canonical/features/empty-file.ts
29728
+ function isEmptyCanonicalFile(content, path) {
29729
+ if (content.trim() !== "") return false;
29730
+ logger.warn(`Skipping empty canonical file ${path.replaceAll("\\", "/")}`);
29731
+ return true;
29732
+ }
29733
+
29433
29734
  // src/canonical/features/rules.ts
29434
29735
  init_fs();
29435
29736
  init_markdown();
@@ -29525,87 +29826,6 @@ function assertNoBasenameCollisions(feature, paths2, stripExt) {
29525
29826
  seen.set(key, { path: p, slug });
29526
29827
  }
29527
29828
  }
29528
-
29529
- // src/utils/output/color.ts
29530
- function noColorRequested() {
29531
- const value = process.env.NO_COLOR;
29532
- return value !== void 0 && value !== "";
29533
- }
29534
- function forceColorRequested() {
29535
- const value = process.env.FORCE_COLOR;
29536
- if (value === void 0) return void 0;
29537
- return value !== "0" && value !== "false";
29538
- }
29539
- function colorEnabled(stream = process.stdout) {
29540
- const forced = forceColorRequested();
29541
- if (forced !== void 0) return forced;
29542
- if (noColorRequested()) return false;
29543
- return stream.isTTY === true;
29544
- }
29545
-
29546
- // src/utils/output/logger.ts
29547
- var C = {
29548
- green: "\x1B[32m",
29549
- red: "\x1B[31m",
29550
- yellow: "\x1B[33m",
29551
- cyan: "\x1B[36m",
29552
- reset: "\x1B[0m"
29553
- };
29554
- function outStream() {
29555
- return process.stdout;
29556
- }
29557
- function out(text) {
29558
- outStream().write(text);
29559
- }
29560
- function c(code, text, stream) {
29561
- return colorEnabled(stream) ? `${code}${text}${C.reset}` : text;
29562
- }
29563
- function pad(str3, width) {
29564
- const len = [...str3].length;
29565
- return str3 + " ".repeat(Math.max(0, width - len));
29566
- }
29567
- var logger = {
29568
- info(msg) {
29569
- out(c(C.cyan, msg, outStream()) + "\n");
29570
- },
29571
- warn(msg) {
29572
- process.stderr.write(c(C.yellow, "\u26A0 ", process.stderr) + msg + "\n");
29573
- },
29574
- error(msg) {
29575
- process.stderr.write(c(C.red, "\u2717 ", process.stderr) + msg + "\n");
29576
- },
29577
- success(msg) {
29578
- out(c(C.green, "\u2713 ", outStream()) + msg + "\n");
29579
- },
29580
- debug(msg) {
29581
- if (process.env.AGENTSMESH_DEBUG === "1") {
29582
- out(c(C.cyan, "[debug] ", outStream()) + msg + "\n");
29583
- }
29584
- },
29585
- table(rows) {
29586
- if (rows.length === 0) return;
29587
- const cols = rows[0].length;
29588
- const widths = [];
29589
- for (let j = 0; j < cols; j++) {
29590
- let max = 0;
29591
- for (let i = 0; i < rows.length; i++) {
29592
- const len = [...rows[i][j]].length;
29593
- if (len > max) max = len;
29594
- }
29595
- widths[j] = max;
29596
- }
29597
- const border = "+" + widths.map((w) => "-".repeat(w + 2)).join("+") + "+";
29598
- out(border + "\n");
29599
- for (let i = 0; i < rows.length; i++) {
29600
- const row = rows[i];
29601
- const line = "| " + row.map((cell, j) => pad(cell, widths[j])).join(" | ") + " |";
29602
- out(line + "\n");
29603
- }
29604
- out(border + "\n");
29605
- }
29606
- };
29607
-
29608
- // src/canonical/features/unrecognized-files-warning.ts
29609
29829
  var ALTERNATE_RESOURCE_FORMATS = /* @__PURE__ */ new Set([".toml", ".yaml", ".yml", ".json"]);
29610
29830
  function warnIfUnrecognizedResourceFormats(featureLabel, dir, allFiles, parsedFiles, opts = {}) {
29611
29831
  if (allFiles.length === 0) return;
@@ -29650,7 +29870,8 @@ async function parseRules(rulesDir, opts = {}) {
29650
29870
  const rules = [];
29651
29871
  for (const path of mdFiles) {
29652
29872
  const content = await readFileSafe(path);
29653
- if (!content) continue;
29873
+ if (content === null) continue;
29874
+ if (isEmptyCanonicalFile(content, path)) continue;
29654
29875
  const parsed = parseOrSkipFrontmatter(content, path, opts.onParseError);
29655
29876
  if (!parsed) continue;
29656
29877
  const { frontmatter, body } = parsed;
@@ -29704,7 +29925,8 @@ async function parseCommands(commandsDir, opts = {}) {
29704
29925
  const commands = [];
29705
29926
  for (const path of mdFiles) {
29706
29927
  const content = await readFileSafe(path);
29707
- if (!content) continue;
29928
+ if (content === null) continue;
29929
+ if (isEmptyCanonicalFile(content, path)) continue;
29708
29930
  const parsed = parseOrSkipFrontmatter(content, path, opts.onParseError);
29709
29931
  if (!parsed) continue;
29710
29932
  const { frontmatter, body } = parsed;
@@ -29764,15 +29986,14 @@ async function parseAgents(agentsDir, opts = {}) {
29764
29986
  const agents = [];
29765
29987
  for (const path of mdFiles) {
29766
29988
  const content = await readFileSafe(path);
29767
- if (!content) continue;
29989
+ if (content === null) continue;
29990
+ if (isEmptyCanonicalFile(content, path)) continue;
29768
29991
  const parsed = parseOrSkipFrontmatter(content, path, opts.onParseError);
29769
29992
  if (!parsed) continue;
29770
29993
  const { frontmatter, body } = parsed;
29771
29994
  const name = basename(path, ".md");
29772
29995
  assertCanonicalName("agent", name);
29773
- const toolsCamel = toStrArray2(frontmatter.tools);
29774
- const toolsKebab = toStrArray2(frontmatter["tools"]);
29775
- const tools = toolsCamel.length > 0 ? toolsCamel : toolsKebab;
29996
+ const tools = toStrArray2(frontmatter.tools);
29776
29997
  const disallowedCamel = toStrArray2(frontmatter.disallowedTools);
29777
29998
  const disallowedKebab = toStrArray2(frontmatter["disallowed-tools"]);
29778
29999
  const disallowedTools = disallowedCamel.length > 0 ? disallowedCamel : disallowedKebab;
@@ -29894,20 +30115,21 @@ async function parseSkills(skillsDir, opts = {}) {
29894
30115
  init_mcp();
29895
30116
 
29896
30117
  // src/canonical/features/permissions.ts
30118
+ init_syntax_error();
29897
30119
  init_fs();
29898
30120
  function ensureStringArray(val) {
29899
30121
  if (!Array.isArray(val)) return [];
29900
30122
  return val.filter((x) => typeof x === "string");
29901
30123
  }
29902
- async function parsePermissions(permissionsPath) {
30124
+ async function parsePermissions(permissionsPath, onParseError) {
29903
30125
  const content = await readFileSafe(permissionsPath);
29904
30126
  if (content === null) return null;
29905
30127
  if (!content.trim()) return { allow: [], deny: [], ask: [] };
29906
30128
  let parsed;
29907
30129
  try {
29908
30130
  parsed = parse(content);
29909
- } catch {
29910
- return null;
30131
+ } catch (err) {
30132
+ return failSyntax(permissionsPath, err, onParseError);
29911
30133
  }
29912
30134
  if (!parsed || typeof parsed !== "object") return null;
29913
30135
  const obj = parsed;
@@ -29917,52 +30139,8 @@ async function parsePermissions(permissionsPath) {
29917
30139
  return { allow, deny, ask };
29918
30140
  }
29919
30141
 
29920
- // src/canonical/features/hooks.ts
29921
- init_fs();
29922
- init_hook_command();
29923
- var VALID_TYPES = ["command", "prompt"];
29924
- function toHookEntry(raw) {
29925
- if (!raw || typeof raw !== "object") return null;
29926
- const obj = raw;
29927
- const matcher = obj.matcher;
29928
- if (typeof matcher !== "string") return null;
29929
- const command = getHookText(obj);
29930
- if (!command) return null;
29931
- const type = typeof obj.type === "string" && VALID_TYPES.includes(obj.type) ? obj.type : void 0;
29932
- const timeout = typeof obj.timeout === "number" && Number.isFinite(obj.timeout) ? obj.timeout : void 0;
29933
- const prompt = getHookPrompt(obj) || void 0;
29934
- return {
29935
- matcher,
29936
- command,
29937
- ...timeout !== void 0 && { timeout },
29938
- ...type && { type },
29939
- ...prompt && { prompt }
29940
- };
29941
- }
29942
- async function parseHooks(hooksPath) {
29943
- const content = await readFileSafe(hooksPath);
29944
- if (content === null) return null;
29945
- if (!content.trim()) return {};
29946
- let parsed;
29947
- try {
29948
- parsed = parse(content);
29949
- } catch {
29950
- return null;
29951
- }
29952
- if (!parsed || typeof parsed !== "object") return null;
29953
- const result2 = {};
29954
- const obj = parsed;
29955
- for (const [key, val] of Object.entries(obj)) {
29956
- if (!Array.isArray(val)) continue;
29957
- const entries = [];
29958
- for (const item of val) {
29959
- const entry = toHookEntry(item);
29960
- if (entry) entries.push(entry);
29961
- }
29962
- if (entries.length > 0) result2[key] = entries;
29963
- }
29964
- return result2;
29965
- }
30142
+ // src/canonical/load/loader.ts
30143
+ init_hooks();
29966
30144
 
29967
30145
  // src/canonical/features/ignore.ts
29968
30146
  init_fs();
@@ -29989,9 +30167,9 @@ async function loadCanonicalFiles(canonicalDirOrProjectRoot, opts = {}) {
29989
30167
  parseCommands(join(canonicalDir, "commands"), opts),
29990
30168
  parseAgents(join(canonicalDir, "agents"), opts),
29991
30169
  parseSkills(join(canonicalDir, "skills"), opts),
29992
- parseMcp(join(canonicalDir, "mcp.json")),
29993
- parsePermissions(join(canonicalDir, "permissions.yaml")),
29994
- parseHooks(join(canonicalDir, "hooks.yaml")),
30170
+ parseMcp(join(canonicalDir, "mcp.json"), opts.onParseError),
30171
+ parsePermissions(join(canonicalDir, "permissions.yaml"), opts.onParseError),
30172
+ parseHooks(join(canonicalDir, "hooks.yaml"), opts.onParseError),
29995
30173
  parseIgnore(join(canonicalDir, "ignore"))
29996
30174
  ]);
29997
30175
  return {
@@ -30894,6 +31072,7 @@ function gateExtendElevatedArtifacts(canonical, ext) {
30894
31072
  });
30895
31073
  }
30896
31074
  init_mcp();
31075
+ init_hooks();
30897
31076
 
30898
31077
  // src/install/pack/pack-reader.ts
30899
31078
  init_fs();
@@ -31217,8 +31396,46 @@ function mergeLocalConfig(project26, local) {
31217
31396
  if (Array.isArray(local.extends) && local.extends.length > 0) {
31218
31397
  merged.extends = [...project26.extends ?? [], ...local.extends];
31219
31398
  }
31399
+ if (Array.isArray(local.plugins)) {
31400
+ merged.plugins = mergeById(project26.plugins, local.plugins);
31401
+ }
31402
+ if (Array.isArray(local.pluginTargets)) {
31403
+ merged.pluginTargets = [.../* @__PURE__ */ new Set([...project26.pluginTargets, ...local.pluginTargets])];
31404
+ }
31405
+ if (typeof local.collaboration === "object" && local.collaboration !== null && !Array.isArray(local.collaboration)) {
31406
+ merged.collaboration = local.collaboration;
31407
+ }
31408
+ warnUnhandledLocalKeys(local);
31220
31409
  return merged;
31221
31410
  }
31411
+ var LOCAL_KEYS = /* @__PURE__ */ new Set([
31412
+ "version",
31413
+ "targets",
31414
+ "features",
31415
+ "overrides",
31416
+ "conversions",
31417
+ "extends",
31418
+ "plugins",
31419
+ "pluginTargets",
31420
+ "collaboration"
31421
+ ]);
31422
+ function warnUnhandledLocalKeys(local) {
31423
+ const unknown = Object.keys(local).filter((key) => !LOCAL_KEYS.has(key));
31424
+ if (unknown.length === 0) return;
31425
+ logger.warn(
31426
+ `agentsmesh.local.yaml: ignoring unknown key(s) ${unknown.join(", ")}; supported keys are ${[...LOCAL_KEYS].join(", ")}.`
31427
+ );
31428
+ }
31429
+ function mergeById(project26, local) {
31430
+ const byId = /* @__PURE__ */ new Map();
31431
+ const anonymous = [];
31432
+ for (const entry of [...project26, ...local]) {
31433
+ const id = typeof entry === "object" && entry !== null && typeof entry.id === "string" ? entry.id : void 0;
31434
+ if (id === void 0) anonymous.push(entry);
31435
+ else byId.set(id, entry);
31436
+ }
31437
+ return [...byId.values(), ...anonymous];
31438
+ }
31222
31439
  async function loadConfigFromExactDir(configDir) {
31223
31440
  const configPath = join(configDir, CONFIG_FILENAME);
31224
31441
  let config = await loadConfig(configPath);