agentsmesh 0.28.0 → 0.30.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,47 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.30.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 7b795b8: Lessons: measure effectiveness and sharpen recall
8
+
9
+ The lessons subsystem now tracks whether a recalled rule actually prevented the repeat, and improves recall precision — all opt-in and backward-compatible (no change to the CLI, MCP, or `lessons.json` surface):
10
+ - **Effectiveness signal (opt-in via `AGENTSMESH_LESSONS_TELEMETRY=1`).** An append-only outcome log records deliveries and failures; effectiveness is derived at read time — did a delivered lesson prevent the repeat? Recall **down-ranks** a lesson that fired but never helped, `lessons validate` gains advisory `INEFFECTIVE_LESSON` and `UNCOVERED_FAILURE` findings, and `lessons stats` gains a coarse **effectiveness block** (deliveries, held rate, ineffective count). The signal is deliberately coarse and labeled as such — never presented as proof of prevention.
11
+ - **Diff-aware recall.** The recall hook binds on the content being written, not just the file path, so a keyword lesson can fire on the change itself.
12
+ - **camelCase keyword reach.** Keyword recall now also splits camelCase/acronym identifier sub-words while retaining the whole token (fully backward compatible), so a keyword like `guard` reaches `useLeaveGuard.ts`.
13
+ - **Quieter injection.** Automatic recall caps the injected set to the most relevant few and stays silent on no match.
14
+ - **Recurrence-driven capture nudge.** A failure recurring on the same action with no covering lesson escalates the capture reminder, pre-filled from the real file/command.
15
+ - **Portable failure detection.** The recall hook records a failure from any tool event carrying an error payload, not only Claude Code's dedicated `PostToolUseFailure` event — so effectiveness can accumulate on more harnesses.
16
+ - **New capture warning `WIDE_GLOB_MATCH`** flags a `file_glob` that matches a large share of the working tree, complementing the existing structural broad-glob check.
17
+ - **`agentsmesh init --lessons`** now commits the `.gitattributes` binding for the `lessons.json` union merge driver and prints the per-clone `git config` setup, so a team's concurrent captures merge cleanly instead of conflicting.
18
+
19
+ - 7b795b8: Lessons: `--scope always` for universal always-on rules
20
+
21
+ A lesson can now be captured with `--scope always` (MCP: `scope: "always"`) for a universal standard that applies to every task — a comment/test/style convention that no single file or command names. Always-on lessons need no trigger and are delivered on every task automatically, alongside triggered recall, deduplicated once per session.
22
+
23
+ ### Patch Changes
24
+
25
+ - 13730ca: Lint no longer warns about agentsmesh-injected best-effort hook events
26
+
27
+ `agentsmesh lint` previously flagged the recall/capture hook events agentsmesh itself injects (`UserPromptSubmit`, `PostToolUseFailure`, `SessionStart`) as "unsupported" on targets whose hook model can't represent them — an unactionable warning, since agentsmesh injects those events and degrades them gracefully. They are now excluded from the unsupported-hook warning across every whitelist-style target.
28
+
29
+ ## 0.29.0
30
+
31
+ ### Minor Changes
32
+
33
+ - 663f0e6: Strip elevated artifacts from remote `extends` sources by default
34
+
35
+ `hooks`, `permissions`, and `mcp` contributed by a **remote** `extends` source (`github:`, `gitlab:`, `git+…`, including `git+file://`) are now stripped during config load unless the entry opts in with `accept: [hooks, permissions, mcp]`. This closes a gap where a remote `extends` could inject shell-executing config (settings hooks, MCP launch specs) without the per-artifact consent that `agentsmesh install` already requires. Local `extends` remain trusted as-is, and a warning reports anything that was stripped.
36
+
37
+ ### Patch Changes
38
+
39
+ - 11f90bc: Harden plugin source containment against intermediate-symlink escapes
40
+
41
+ The plugin loader now canonicalizes an unresolvable source path against its nearest existing ancestor instead of falling back to the raw path. This closes a gap where a local plugin `source` routed through a symlinked directory that resolves outside the project root could slip past the trust-boundary check when its entry file did not exist yet. Legitimate in-project plugins are unaffected.
42
+
43
+ - c647c9b: Docs: the README now leads with lessons (the shared agent memory), adds a head-to-head comparison with Ruler and rulesync, embeds demo GIFs, leads the quickstart with the import-first path for existing repos, and documents the new `extends` `accept:` consent. Documentation-only — no behavior change.
44
+
3
45
  ## 0.28.0
4
46
 
5
47
  ### Minor Changes
package/README.md CHANGED
@@ -1,8 +1,10 @@
1
1
  <div align="center">
2
2
 
3
- # AgentsMesh — One `.agentsmesh/` Directory for Every AI Coding Tool
3
+ # AgentsMesh
4
4
 
5
- <img src="https://raw.githubusercontent.com/sampleXbro/agentsmesh/master/assets/agentsmesh-banner.jpeg" alt="AgentsMesh — One source. Every AI coding tool. Always in sync." width="100%" />
5
+ ### One source for every AI coding tool with a shared agent memory that learns from your repo.
6
+
7
+ <img src="https://raw.githubusercontent.com/sampleXbro/agentsmesh/master/assets/agentsmesh-banner.jpeg" alt="AgentsMesh — One source. Every AI coding tool. Agents that learn." width="100%" />
6
8
 
7
9
  [![CI](https://github.com/sampleXbro/agentsmesh/actions/workflows/ci.yml/badge.svg)](https://github.com/sampleXbro/agentsmesh/actions/workflows/ci.yml)
8
10
  [![npm version](https://img.shields.io/npm/v/agentsmesh.svg)](https://www.npmjs.com/package/agentsmesh)
@@ -23,6 +25,28 @@ Every AI coding assistant has its own config format — `CLAUDE.md`, `AGENTS.md`
23
25
  > [!NOTE]
24
26
  > Full documentation, guides, and the per-tool reference live at **[samplexbro.github.io/agentsmesh](https://samplexbro.github.io/agentsmesh/)**.
25
27
 
28
+ ## How AgentsMesh compares
29
+
30
+ Most config tools stop at copying rule files into each assistant's folder. AgentsMesh is the only one that also gives your agents a **shared memory that learns**, round-trips **losslessly** in both directions, and lets anyone **add a new tool as a plugin** — no release required.
31
+
32
+ | Capability | AgentsMesh | [Ruler](https://github.com/intellectronica/ruler) | [rulesync](https://github.com/dyoshikawa/rulesync) |
33
+ |---|:---:|:---:|:---:|
34
+ | Generate native config for many tools | ✅ | ✅ | ✅ |
35
+ | Import existing tool config back to source | ✅ | — | ✅ |
36
+ | **Lossless round-trip** (re-import restores originals) | ✅ | — | partial |
37
+ | Convert one tool's config directly to another | ✅ | — | ✅ |
38
+ | **Automatic cross-tool link rebasing** | ✅ | — | — |
39
+ | **Lessons — a shared agent memory that learns** | ✅ | — | — |
40
+ | **Plugins — add a target without a release** | ✅ | — | — |
41
+ | Cross-target lint (warn before silent data loss) | ✅ | — | — |
42
+ | CI drift gate + git-merge recovery | ✅ | partial | partial |
43
+ | Self-serve MCP server (agent-operable config) | ✅ | — | — |
44
+ | `--json` everywhere + typed programmatic API | ✅ | — | partial |
45
+
46
+ <sub>Reflects the public projects as of June 2026 — see the [alternatives guide](https://samplexbro.github.io/agentsmesh/reference/alternatives/) for sources and detail. Corrections welcome.</sub>
47
+
48
+ Built to be depended on: **8,000+ tests** run on Linux, macOS, and Windows CI (Node 22 & 24), configs are JSON-Schema-validated, npm releases ship with [provenance](https://docs.npmjs.com/generating-provenance-statements), and standalone binaries are published with SHA-256 checksums.
49
+
26
50
  ## Install
27
51
 
28
52
  Every install method ships the same CLI (`agentsmesh`, plus the shorter `amsh` alias) and the same TypeScript library.
@@ -45,11 +69,14 @@ Standalone binaries are also on [GitHub Releases](https://github.com/sampleXbro/
45
69
 
46
70
  ## 60-second quickstart
47
71
 
48
- Works on Linux, macOS, and Windows. After [installing](#install):
72
+ Works on Linux, macOS, and Windows. After [installing](#install), `agentsmesh init` adapts to your repo:
73
+
74
+ - **Already using AI tools?** It detects your existing `.cursor/`, `.claude/`, `.github/copilot-instructions.md`, and more, and offers to import them all into one `.agentsmesh/` source — nothing rewritten by hand. (`agentsmesh init --yes` does it non-interactively.)
75
+ - **Starting fresh?** It scaffolds a minimal `.agentsmesh/` to grow from.
49
76
 
50
77
  ```bash
51
- agentsmesh init # scaffold .agentsmesh/ + agentsmesh.yaml
52
- agentsmesh generate # write native configs for every enabled tool
78
+ agentsmesh init # detect & import existing configs, or scaffold fresh
79
+ agentsmesh generate # write each tool's native config from the one source
53
80
  agentsmesh check # CI-friendly drift gate against .agentsmesh/.lock
54
81
  ```
55
82
 
@@ -99,20 +126,44 @@ AGENTS.md
99
126
 
100
127
  Edit canonical sources, run `agentsmesh generate`, and every native file above is (re)written for you — always in sync. Alongside the directory, `agentsmesh.yaml` selects which targets and features are enabled, `agentsmesh.local.yaml` holds per-developer overrides (gitignored), and `.agentsmesh/.lock` records the checksums that `agentsmesh check` enforces.
101
128
 
129
+ <div align="center">
130
+ <img src="https://raw.githubusercontent.com/sampleXbro/agentsmesh/master/assets/generate-demo.gif" alt="agentsmesh detects existing tool configs, imports them into one source, and generates native config for every tool" width="100%" />
131
+ </div>
132
+
102
133
  ## Teach your agents: lessons
103
134
 
104
- Lessons give your AI agents a **memory of past mistakes** — read *before* they touch anything, written *after* something goes wrong, so the same mistake doesn't happen twice in any tool.
135
+ This is the part no other config-sync tool has. Lessons give your AI agents a **memory of past mistakes** — read *before* they touch anything, written *after* something goes wrong, so the same mistake doesn't happen twice in any tool. It's what turns a static pile of rules into an agent that gets better at *your* codebase over time.
136
+
137
+ <div align="center">
138
+ <img src="https://raw.githubusercontent.com/sampleXbro/agentsmesh/master/assets/lessons-demo.gif" alt="an AI agent captures a lesson after a failure, then recalls it before editing the same file — triggered by file, command, or keyword" width="100%" />
139
+ </div>
105
140
 
106
141
  The memory is one git-tracked file, `.agentsmesh/lessons/lessons.json`, and every agent talks to it through two commands:
107
142
 
108
143
  - **Recall** — before an edit or a state-changing command, the agent runs `agentsmesh lessons query --file <path> --cmd <command>` and follows the rules that match.
109
144
  - **Capture** — right after a failure (red test, lint error, review comment, wrong assumption), it saves the rule with `agentsmesh lessons add "<rule>" --topic <id> --trigger-file <glob>`.
110
145
 
146
+ Rules can be **scoped** (fire only on a matching file, command, or keyword) or **always-on** (`--scope always`) for universal standards that apply to every task — delivered on each task automatically, no trigger needed.
147
+
148
+ Opt in with `AGENTSMESH_LESSONS_TELEMETRY=1` and agentsmesh goes a step further — it tracks whether a recalled lesson actually **prevented** the repeat, quietly down-ranks rules that never help, and flags them in `agentsmesh lessons validate`. Off by default and per-machine; the shared graph is your rules, the effectiveness is your own history.
149
+
150
+ In practice:
151
+
152
+ ```bash
153
+ # An agent breaks the Windows build, finds the cause, and writes the lesson down:
154
+ agentsmesh lessons add "Normalize CLI display paths to forward slashes" \
155
+ --topic windows-paths --trigger-file "src/cli/**/*.ts"
156
+
157
+ # Days later, any agent — in any tool — about to touch that path recalls it first:
158
+ agentsmesh lessons query --file src/cli/output.ts
159
+ # → "Normalize CLI display paths to forward slashes"
160
+ ```
161
+
111
162
  ```bash
112
163
  agentsmesh init --lessons && agentsmesh generate # wire the recall/capture loop once
113
164
  ```
114
165
 
115
- `init --lessons` drops a small always-on rule into `.agentsmesh/rules/_root.md` (so every target gets the habit), seeds the full operating manual as a `lessons` skill where supported, and wires a recall hook (a `PreToolUse` first-touch guard plus a `PostToolUse` fallback) on hook-capable tools; agents without shell access use the matching MCP tools (`lessons_query` / `lessons_add`). Because the graph is a normal git-tracked file, a lesson one agent learns today helps every teammate's agent tomorrow, and every change is reviewable like any other diff.
166
+ `init --lessons` drops a small always-on rule into `.agentsmesh/rules/_root.md` (so every target gets the habit), seeds the full operating manual as a `lessons` skill where supported, and wires a recall hook on hook-capable tools — a `PreToolUse` first-touch guard plus a `PostToolUse` fallback for file/command scope, a `UserPromptSubmit` pass so conceptual (`keyword`) and always-on lessons recall against the **task text**, a `PostToolUseFailure` advisory nudge to capture at the moment something breaks, and a `SessionStart` reset so recall re-surfaces lessons a long chat has summarized away. Agents without shell access use the matching MCP tools (`lessons_query` / `lessons_add`). Because the graph is a normal git-tracked file, a lesson one agent learns today helps every teammate's agent tomorrow, and every change is reviewable like any other diff.
116
167
 
117
168
  Full walkthrough: [Teach your AI agents with lessons](https://samplexbro.github.io/agentsmesh/guides/lessons/) · [`agentsmesh lessons` reference](https://samplexbro.github.io/agentsmesh/cli/lessons/).
118
169
 
@@ -125,8 +176,8 @@ Full walkthrough: [Teach your AI agents with lessons](https://samplexbro.github.
125
176
  - **Migrate between tools** — `convert --from <a> --to <b>` rewrites one tool's config directly into another's native format. [convert →](https://samplexbro.github.io/agentsmesh/cli/convert/)
126
177
  - **Global mode** — `~/.agentsmesh/` syncs your personal config to `~/.claude/`, `~/.cursor/`, `~/.codex/`, and more. Every command accepts `--global`. [Global paths →](https://samplexbro.github.io/agentsmesh/reference/supported-tools/#global-mode)
127
178
  - **Team-safe and CI-ready** — `check` is a drift gate against `.agentsmesh/.lock`, `diff` previews changes, `merge` rebuilds the lock after a 3-way Git conflict, and `lock_features` + per-feature `strategy` prevent accidental overrides. `lint` adds cross-target warnings (`silent-drop-guard`, `hook-script-references`, `rule-scope-inversion`) for content a tool would silently mishandle. [check →](https://samplexbro.github.io/agentsmesh/cli/check/) · [lint →](https://samplexbro.github.io/agentsmesh/cli/lint/)
128
- - **Community packs and `extends`** — install shared rules, skills, agents, and commands from any git repo (`install`, `--sync`, `refresh`, remote `extends`); a multi-signal classifier auto-detects Anthropic-style skill packs. Elevated artifacts (hooks, permissions, MCP) from remote sources are stripped unless you opt in with `--accept-*`. [Install reference →](https://samplexbro.github.io/agentsmesh/cli/install/)
129
- - **Plugins** — ship support for a new tool as a standalone npm package, with full parity to built-in targets (project + global, conversions, lint hooks, hook post-processing). [Build a plugin →](https://samplexbro.github.io/agentsmesh/guides/building-plugins/)
179
+ - **Community packs and `extends`** — install shared rules, skills, agents, and commands from any git repo (`install`, `--sync`, `refresh`, remote `extends`); a multi-signal classifier auto-detects Anthropic-style skill packs. Elevated artifacts (hooks, permissions, MCP) from remote sources are stripped unless you opt in with `--accept-*` on `install`, or `accept:` on a remote `extends` entry. [Install reference →](https://samplexbro.github.io/agentsmesh/cli/install/)
180
+ - **Plugins — add a tool without waiting for a release** — ship support for a new AI tool as a standalone npm package, with full parity to built-in targets (project + global, conversions, lint hooks, hook post-processing). When a tool ships or changes its format, you don't file an issue and wait on the maintainer — you publish a plugin. [Build a plugin →](https://samplexbro.github.io/agentsmesh/guides/building-plugins/)
130
181
  - **Schema-validated configs** — each config ships a JSON Schema, so editors give you autocomplete and validation out of the box. [JSON schemas →](https://samplexbro.github.io/agentsmesh/reference/json-schemas/)
131
182
  - **Typed programmatic API** — drive `generate` / `import` / `lint` / `diff` / `check` from scripts or CI via `agentsmesh`, `/engine`, `/canonical`, `/targets`, `/lessons`. [API reference →](https://samplexbro.github.io/agentsmesh/reference/programmatic-api/)
132
183
  - **Self-serve MCP server** — `agentsmesh mcp` (seeded by `init`) exposes canonical config as MCP tools so agents can introspect rules, commands, and skills and trigger `generate` in-conversation. [MCP server →](https://samplexbro.github.io/agentsmesh/reference/mcp-server/)
@@ -135,9 +186,11 @@ Full walkthrough: [Teach your AI agents with lessons](https://samplexbro.github.
135
186
  > [!TIP]
136
187
  > Commit **both** `.agentsmesh/` and the generated tool files, the same way you commit `package-lock.json`: they're deterministic build output that the AI tools read directly, and `agentsmesh check` guards the two from drifting.
137
188
 
138
- ## Why not just `AGENTS.md`?
189
+ ## Why not Ruler, rulesync, or just `AGENTS.md`?
190
+
191
+ **vs. `AGENTS.md`** — [`AGENTS.md`](https://agents.md) is a great shared instruction file, and AgentsMesh emits it natively wherever a tool supports it. But a single markdown file isn't enough on its own: most assistants expose configuration *beyond* it — Cursor's `.cursor/rules/*.mdc` and MCP config, Claude Code's agents/skills/commands/hooks/permissions, Copilot's `.github/instructions/`, Gemini's `.gemini/settings.json`, and so on — and those surfaces don't overlap. AgentsMesh canonicalizes all of them so you never have to pick one tool's surface as the lowest common denominator.
139
192
 
140
- [`AGENTS.md`](https://agents.md) is a great shared instruction file, and AgentsMesh emits it natively wherever a tool supports it. But a single markdown file isn't enough on its own: most assistants expose configuration *beyond* it — Cursor's `.cursor/rules/*.mdc` and MCP config, Claude Code's agents/skills/commands/hooks/permissions, Copilot's `.github/instructions/`, Gemini's `.gemini/settings.json`, and so on and those surfaces don't overlap. AgentsMesh canonicalizes all of them so you never have to pick one tool's surface as the lowest common denominator.
193
+ **vs. other sync tools** — [Ruler](https://github.com/intellectronica/ruler) and [rulesync](https://github.com/dyoshikawa/rulesync) solve the rules-distribution half well. AgentsMesh goes further on the parts that are hardest to keep in sync: a shared [lessons](#teach-your-agents-lessons) memory your agents learn from, lossless two-way [import](https://samplexbro.github.io/agentsmesh/cli/import/), automatic cross-tool [link rebasing](https://samplexbro.github.io/agentsmesh/reference/generation-pipeline/), [plugins](https://samplexbro.github.io/agentsmesh/guides/building-plugins/) so a new tool ships without a release, and [lint](https://samplexbro.github.io/agentsmesh/cli/lint/) that warns before a tool would silently drop content. See the [full comparison](#how-agentsmesh-compares) and the [alternatives guide](https://samplexbro.github.io/agentsmesh/reference/alternatives/).
141
194
 
142
195
  ## Commands
143
196
 
@@ -1,5 +1,5 @@
1
- import { b as CanonicalFiles, V as ValidatedConfig } from './schema-CzaoYJlG.js';
2
- export { C as CanonicalAgent, a as CanonicalCommand, c as CanonicalRule, d as CanonicalSkill, H as HookEntry, e as Hooks, I as IgnorePatterns, M as McpConfig, f as McpServer, P as Permissions, S as SkillSupportingFile, g as StdioMcpServer, U as UrlMcpServer } from './schema-CzaoYJlG.js';
1
+ import { b as CanonicalFiles, V as ValidatedConfig } from './schema-CH_JJep8.js';
2
+ export { C as CanonicalAgent, a as CanonicalCommand, c as CanonicalRule, d as CanonicalSkill, H as HookEntry, e as Hooks, I as IgnorePatterns, M as McpConfig, f as McpServer, P as Permissions, S as SkillSupportingFile, g as StdioMcpServer, U as UrlMcpServer } from './schema-CH_JJep8.js';
3
3
  import 'zod';
4
4
 
5
5
  interface UnrecognizedFormatsWarningOptions {
package/dist/canonical.js CHANGED
@@ -2874,6 +2874,18 @@ var init_linter = __esm({
2874
2874
  }
2875
2875
  });
2876
2876
 
2877
+ // src/core/hook-types.ts
2878
+ var BEST_EFFORT_HOOK_EVENTS;
2879
+ var init_hook_types = __esm({
2880
+ "src/core/hook-types.ts"() {
2881
+ BEST_EFFORT_HOOK_EVENTS = /* @__PURE__ */ new Set([
2882
+ "UserPromptSubmit",
2883
+ "PostToolUseFailure",
2884
+ "SessionStart"
2885
+ ]);
2886
+ }
2887
+ });
2888
+
2877
2889
  // src/core/lint/shared/helpers.ts
2878
2890
  function createWarning(file, target31, message) {
2879
2891
  return {
@@ -2889,6 +2901,13 @@ function formatOxfordComma(items) {
2889
2901
  if (items.length === 2) return `${items[0]} and ${items[1]}`;
2890
2902
  return `${items.slice(0, -1).join(", ")}, and ${items[items.length - 1]}`;
2891
2903
  }
2904
+ function unsupportedHookEventNames(hooks, supportedEvents) {
2905
+ if (!hooks) return [];
2906
+ const supported = new Set(supportedEvents);
2907
+ return Object.keys(hooks).filter(
2908
+ (event) => !supported.has(event) && !BEST_EFFORT_HOOK_EVENTS.has(event)
2909
+ );
2910
+ }
2892
2911
  function createUnsupportedHookWarning(event, target31, supportedEvents, options) {
2893
2912
  const by = options?.unsupportedBy ?? target31;
2894
2913
  const supported = formatOxfordComma(supportedEvents);
@@ -2900,6 +2919,7 @@ function createUnsupportedHookWarning(event, target31, supportedEvents, options)
2900
2919
  }
2901
2920
  var init_helpers = __esm({
2902
2921
  "src/core/lint/shared/helpers.ts"() {
2922
+ init_hook_types();
2903
2923
  }
2904
2924
  });
2905
2925
 
@@ -6217,8 +6237,7 @@ var init_linter5 = __esm({
6217
6237
  // src/targets/augment-code/lint.ts
6218
6238
  function lintHooks3(canonical) {
6219
6239
  if (!canonical.hooks || Object.keys(canonical.hooks).length === 0) return [];
6220
- const supportedSet = new Set(SUPPORTED_HOOK_EVENTS);
6221
- return Object.keys(canonical.hooks).filter((event) => !supportedSet.has(event)).map(
6240
+ return unsupportedHookEventNames(canonical.hooks, SUPPORTED_HOOK_EVENTS).map(
6222
6241
  (event) => createUnsupportedHookWarning(event, "augment-code", SUPPORTED_HOOK_EVENTS, {
6223
6242
  unsupportedBy: "AugmentCode hooks"
6224
6243
  })
@@ -10223,8 +10242,7 @@ function lintCommands3(canonical) {
10223
10242
  function lintHooks5(canonical) {
10224
10243
  if (!canonical.hooks || Object.keys(canonical.hooks).length === 0) return [];
10225
10244
  const supported = ["PreToolUse", "PostToolUse", "Notification", "UserPromptSubmit"];
10226
- const supportedSet = new Set(supported);
10227
- const diagnostics = Object.keys(canonical.hooks).filter((event) => !supportedSet.has(event)).map(
10245
+ const diagnostics = unsupportedHookEventNames(canonical.hooks, supported).map(
10228
10246
  (event) => createUnsupportedHookWarning(event, "copilot", supported, {
10229
10247
  unsupportedBy: "Copilot hooks"
10230
10248
  })
@@ -11341,7 +11359,7 @@ var init_permissions2 = __esm({
11341
11359
  // src/targets/cursor/hook-format.ts
11342
11360
  function unmappedCursorHookEvents(hooks) {
11343
11361
  return Object.keys(hooks).filter(
11344
- (event) => Array.isArray(hooks[event]) && hooks[event].length > 0 && !(event in CANONICAL_TO_CURSOR)
11362
+ (event) => Array.isArray(hooks[event]) && hooks[event].length > 0 && !(event in CANONICAL_TO_CURSOR) && !BEST_EFFORT_HOOK_EVENTS.has(event)
11345
11363
  );
11346
11364
  }
11347
11365
  function toCursorHooks(hooks) {
@@ -11389,6 +11407,7 @@ function cursorHooksToCanonical(hooks) {
11389
11407
  var CANONICAL_TO_CURSOR, CURSOR_TO_CANONICAL;
11390
11408
  var init_hook_format = __esm({
11391
11409
  "src/targets/cursor/hook-format.ts"() {
11410
+ init_hook_types();
11392
11411
  init_hook_command();
11393
11412
  CANONICAL_TO_CURSOR = {
11394
11413
  PreToolUse: "preToolUse",
@@ -13839,8 +13858,9 @@ function lintCommands6(canonical) {
13839
13858
  function lintHooks7(canonical) {
13840
13859
  if (!canonical.hooks || Object.keys(canonical.hooks).length === 0) return [];
13841
13860
  const supported = ["PreToolUse", "PostToolUse", "Notification"];
13842
- const supportedSet = new Set(supported);
13843
- return Object.keys(canonical.hooks).filter((event) => !supportedSet.has(event)).map((event) => createUnsupportedHookWarning(event, "gemini-cli", supported));
13861
+ return unsupportedHookEventNames(canonical.hooks, supported).map(
13862
+ (event) => createUnsupportedHookWarning(event, "gemini-cli", supported)
13863
+ );
13844
13864
  }
13845
13865
  var init_lint14 = __esm({
13846
13866
  "src/targets/gemini-cli/lint.ts"() {
@@ -16092,8 +16112,7 @@ var init_linter20 = __esm({
16092
16112
  function lintHooks10(canonical) {
16093
16113
  if (!canonical.hooks || Object.keys(canonical.hooks).length === 0) return [];
16094
16114
  const supported = ["PreToolUse", "PostToolUse", "UserPromptSubmit", "SubagentStop"];
16095
- const supportedSet = new Set(supported);
16096
- return Object.keys(canonical.hooks).filter((event) => !supportedSet.has(event)).map(
16115
+ return unsupportedHookEventNames(canonical.hooks, supported).map(
16097
16116
  (event) => createUnsupportedHookWarning(event, "kiro", supported, { unsupportedBy: "Kiro hooks" })
16098
16117
  );
16099
16118
  }
@@ -20874,7 +20893,9 @@ async function resolveExtendPaths(config, configDir, options = {}) {
20874
20893
  as: ext.as,
20875
20894
  version: fetched.version,
20876
20895
  path: ext.path,
20877
- pick: ext.pick
20896
+ pick: ext.pick,
20897
+ isRemote: true,
20898
+ accept: ext.accept
20878
20899
  });
20879
20900
  continue;
20880
20901
  }
@@ -20891,7 +20912,9 @@ async function resolveExtendPaths(config, configDir, options = {}) {
20891
20912
  target: ext.target,
20892
20913
  as: ext.as,
20893
20914
  path: ext.path,
20894
- pick: ext.pick
20915
+ pick: ext.pick,
20916
+ isRemote: false,
20917
+ accept: ext.accept
20895
20918
  });
20896
20919
  }
20897
20920
  return result;
@@ -22421,6 +22444,47 @@ function applyExtendPick(canonical, features, pick, extendName) {
22421
22444
  return next;
22422
22445
  }
22423
22446
 
22447
+ // src/install/core/elevated-artifacts.ts
22448
+ function stripUntrustedElevatedArtifacts(canonical, options) {
22449
+ if (options.sourceKind === "local") {
22450
+ return { canonical, stripped: [] };
22451
+ }
22452
+ const stripped = [];
22453
+ let hooks = canonical.hooks;
22454
+ let permissions = canonical.permissions;
22455
+ let mcp = canonical.mcp;
22456
+ if (hooks !== null && !options.acceptHooks) {
22457
+ hooks = null;
22458
+ stripped.push("hooks");
22459
+ }
22460
+ if (permissions !== null && !options.acceptPermissions) {
22461
+ permissions = null;
22462
+ stripped.push("permissions");
22463
+ }
22464
+ if (mcp !== null && !options.acceptMcp) {
22465
+ mcp = null;
22466
+ stripped.push("mcp");
22467
+ }
22468
+ if (stripped.length === 0) {
22469
+ return { canonical, stripped: [] };
22470
+ }
22471
+ return {
22472
+ canonical: { ...canonical, hooks, permissions, mcp },
22473
+ stripped
22474
+ };
22475
+ }
22476
+
22477
+ // src/canonical/extends/extend-elevated.ts
22478
+ function gateExtendElevatedArtifacts(canonical, ext) {
22479
+ const accept = ext.accept ?? [];
22480
+ return stripUntrustedElevatedArtifacts(canonical, {
22481
+ sourceKind: ext.isRemote ? "git" : "local",
22482
+ acceptHooks: accept.includes("hooks"),
22483
+ acceptPermissions: accept.includes("permissions"),
22484
+ acceptMcp: accept.includes("mcp")
22485
+ });
22486
+ }
22487
+
22424
22488
  // src/install/pack/pack-reader.ts
22425
22489
  init_fs();
22426
22490
 
@@ -22454,7 +22518,14 @@ var extendSourceSchema = z.object({
22454
22518
  features: z.array(featureSchema),
22455
22519
  /** Repo-relative POSIX path for discovery (skill packs, nested .agentsmesh). */
22456
22520
  path: z.string().optional(),
22457
- pick: extendPickSchema.optional()
22521
+ pick: extendPickSchema.optional(),
22522
+ /**
22523
+ * Per-entry consent for "elevated" artifacts (`hooks`, `permissions`, `mcp`)
22524
+ * from a remote source. These control local code execution at generate time,
22525
+ * so for non-local extends they are stripped by default and only merged for
22526
+ * the artifacts listed here. Local extends are trusted and ignore this field.
22527
+ */
22528
+ accept: z.array(z.enum(["hooks", "permissions", "mcp"])).optional()
22458
22529
  });
22459
22530
  var collaborationSchema = z.object({
22460
22531
  strategy: z.enum(["merge", "lock", "last-wins"]).default("merge"),
@@ -22646,7 +22717,14 @@ async function loadCanonicalWithExtends(config, configDir, options = {}, canonic
22646
22717
  for (const ext of resolvedExtends) {
22647
22718
  const extCanonical = await loadCanonicalForExtend(ext);
22648
22719
  const filtered = filterCanonicalByFeatures(extCanonical, ext.features);
22649
- const picked = applyExtendPick(filtered, ext.features, ext.pick, ext.name);
22720
+ const gated = gateExtendElevatedArtifacts(filtered, ext);
22721
+ if (gated.stripped.length > 0) {
22722
+ const list = gated.stripped.join(", ");
22723
+ logger.warn(
22724
+ `[agentsmesh] Extend "${ext.name}": stripped elevated artifacts from a remote source: ${list}. Add \`accept: [${list}]\` to this extends entry in agentsmesh.yaml to keep them.`
22725
+ );
22726
+ }
22727
+ const picked = applyExtendPick(gated.canonical, ext.features, ext.pick, ext.name);
22650
22728
  merged = mergeCanonicalFiles(merged, picked);
22651
22729
  }
22652
22730
  const packsCanonical = await loadPacksCanonical(canonicalDir);