agentsmesh 0.29.0 → 0.30.1

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,37 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.30.1
4
+
5
+ ### Patch Changes
6
+
7
+ - d4e496a: Reject MCP read, write, and delete paths that escape the project directory through symlinks. Containment is anchored at the project root (not `.agentsmesh`), so a symlinked config file **or** a symlinked `.agentsmesh` parent directory can no longer leak or overwrite files outside the project. Covers skill and canonical (rules/commands/agents) list/get/create/update/delete, plus all config reads and writes (`get_config`/`update_config`, permissions, hooks, ignore, and MCP-server tools). Note: config reads that escape through a symlink now raise `PATH_TRAVERSAL` instead of returning `null`.
8
+
9
+ ## 0.30.0
10
+
11
+ ### Minor Changes
12
+
13
+ - 7b795b8: Lessons: measure effectiveness and sharpen recall
14
+
15
+ 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):
16
+ - **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.
17
+ - **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.
18
+ - **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`.
19
+ - **Quieter injection.** Automatic recall caps the injected set to the most relevant few and stays silent on no match.
20
+ - **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.
21
+ - **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.
22
+ - **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.
23
+ - **`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.
24
+
25
+ - 7b795b8: Lessons: `--scope always` for universal always-on rules
26
+
27
+ 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.
28
+
29
+ ### Patch Changes
30
+
31
+ - 13730ca: Lint no longer warns about agentsmesh-injected best-effort hook events
32
+
33
+ `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.
34
+
3
35
  ## 0.29.0
4
36
 
5
37
  ### Minor Changes
package/README.md CHANGED
@@ -143,6 +143,10 @@ The memory is one git-tracked file, `.agentsmesh/lessons/lessons.json`, and ever
143
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.
144
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>`.
145
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
+
146
150
  In practice:
147
151
 
148
152
  ```bash
@@ -159,7 +163,7 @@ agentsmesh lessons query --file src/cli/output.ts
159
163
  agentsmesh init --lessons && agentsmesh generate # wire the recall/capture loop once
160
164
  ```
161
165
 
162
- `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.
163
167
 
164
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/).
165
169
 
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
  }