memtrace-skills 1.1.4 → 1.1.6

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.
@@ -20,21 +20,41 @@ export interface SettingsMutationResult {
20
20
  * Never overwrites a malformed file — backs it up and returns registered=false.
21
21
  */
22
22
  export declare function registerMcpInSettingsAt(settingsPath: string, memtraceBinary: string): SettingsMutationResult;
23
+ /**
24
+ * The Claude Code PreToolUse hook command.
25
+ *
26
+ * POSIX hosts POST the hook payload to the running daemon's in-process router
27
+ * (`POST /api/rail/hook`) with one `curl` — ~5ms of process instead of the
28
+ * full memtrace binary (node shim + native pair, ~59MB alive for ~0.5s) per
29
+ * Grep/Glob/Bash call. Fail-open is structural: no daemon (connection
30
+ * refused, bounded by `--connect-timeout`) or an old daemon without the
31
+ * endpoint (`-f` on 404) produces no output, and `|| true` exits 0 — Claude
32
+ * Code treats that as allow-with-no-decision. The full binary is NEVER the
33
+ * fallback. The agent session's `MEMTRACE_RAIL` / `MEMTRACE_RAW_SEARCH` ride
34
+ * along as query params, so per-session mode/escape semantics are identical
35
+ * to the spawn era; `MEMTRACE_RAIL_SEARCH_URL` / `MEMTRACE_UI_PORT` override
36
+ * the endpoint the same way they steered the binary's own daemon lookup.
37
+ *
38
+ * Windows keeps the gen-1 binary spawn: hook commands there do not run under
39
+ * a POSIX shell, so the env-forwarding one-liner has no portable equivalent.
40
+ */
41
+ export declare function claudeRailHookCommand(memtraceBinary: string, platform?: NodeJS.Platform): string;
23
42
  /**
24
43
  * Register the Memtrace Rail PreToolUse hook in a Claude settings.json.
25
44
  *
26
45
  * This is what makes Rail "part of memtrace" rather than a manual install:
27
46
  * `memtrace install` wires a PreToolUse hook that pipes every Grep/Glob/Bash
28
- * attempt through `memtrace route --hook`. The hook defaults to **observe**
29
- * (zero behavior change) — `MEMTRACE_RAIL=nudge|rail|strict` opts into more.
30
- * Mode is intentionally NOT baked into the command so the user can change it
31
- * via env without re-installing.
47
+ * attempt through the Rail router (see {@link claudeRailHookCommand} for the
48
+ * transport). The hook defaults to **observe** (zero behavior change) —
49
+ * `MEMTRACE_RAIL=nudge|rail|strict` opts into more. Mode is intentionally NOT
50
+ * baked into the command so the user can change it via env without
51
+ * re-installing.
32
52
  *
33
- * Idempotent: any prior Rail entry (identified by {@link RAIL_HOOK_MARKER}) is
34
- * replaced, and unrelated PreToolUse hooks are preserved. Never overwrites a
35
- * malformed file.
53
+ * Idempotent: any prior Rail entry (identified by {@link RAIL_HOOK_MARKERS})
54
+ * is replaced, and unrelated PreToolUse hooks are preserved. Never overwrites
55
+ * a malformed file.
36
56
  */
37
- export declare function registerRailHookInSettingsAt(settingsPath: string, memtraceBinary: string): SettingsMutationResult;
57
+ export declare function registerRailHookInSettingsAt(settingsPath: string, memtraceBinary: string, platform?: NodeJS.Platform): SettingsMutationResult;
38
58
  /**
39
59
  * Remove the Memtrace Rail PreToolUse hook from a Claude settings.json.
40
60
  * Preserves unrelated hooks; prunes empty containers. Never touches a
@@ -214,26 +214,58 @@ export function registerMcpInSettingsAt(settingsPath, memtraceBinary) {
214
214
  const RAIL_HOOK_MATCHER = 'Grep|Glob|Bash';
215
215
  /** Substring that identifies a memtrace-owned Rail hook entry (for idempotent
216
216
  * re-register + clean removal). */
217
- const RAIL_HOOK_MARKER = 'route --hook';
217
+ const RAIL_HOOK_MARKERS = [
218
+ // gen-1: the hook spawned the full memtrace binary per tool call.
219
+ 'route --hook',
220
+ // gen-2: the hook POSTs the payload to the running daemon's in-process
221
+ // router (one curl, no binary spawn).
222
+ '/api/rail/hook',
223
+ ];
218
224
  const LEGACY_HOOK_SCRIPT_BASENAMES = [
219
225
  'userprompt-claude.sh',
220
226
  'posttool-mcp-telemetry.sh',
221
227
  ];
228
+ /**
229
+ * The Claude Code PreToolUse hook command.
230
+ *
231
+ * POSIX hosts POST the hook payload to the running daemon's in-process router
232
+ * (`POST /api/rail/hook`) with one `curl` — ~5ms of process instead of the
233
+ * full memtrace binary (node shim + native pair, ~59MB alive for ~0.5s) per
234
+ * Grep/Glob/Bash call. Fail-open is structural: no daemon (connection
235
+ * refused, bounded by `--connect-timeout`) or an old daemon without the
236
+ * endpoint (`-f` on 404) produces no output, and `|| true` exits 0 — Claude
237
+ * Code treats that as allow-with-no-decision. The full binary is NEVER the
238
+ * fallback. The agent session's `MEMTRACE_RAIL` / `MEMTRACE_RAW_SEARCH` ride
239
+ * along as query params, so per-session mode/escape semantics are identical
240
+ * to the spawn era; `MEMTRACE_RAIL_SEARCH_URL` / `MEMTRACE_UI_PORT` override
241
+ * the endpoint the same way they steered the binary's own daemon lookup.
242
+ *
243
+ * Windows keeps the gen-1 binary spawn: hook commands there do not run under
244
+ * a POSIX shell, so the env-forwarding one-liner has no portable equivalent.
245
+ */
246
+ export function claudeRailHookCommand(memtraceBinary, platform = process.platform) {
247
+ if (platform === 'win32')
248
+ return `${memtraceBinary} route --hook`;
249
+ const endpoint = '${MEMTRACE_RAIL_SEARCH_URL:-http://127.0.0.1:${MEMTRACE_UI_PORT:-3030}}';
250
+ const query = 'host=claude-code&mode=${MEMTRACE_RAIL:-}&escape=${MEMTRACE_RAW_SEARCH:-}';
251
+ return `curl -sf --connect-timeout 0.05 --max-time 2 --data-binary @- "${endpoint}/api/rail/hook?${query}" 2>/dev/null || true`;
252
+ }
222
253
  /**
223
254
  * Register the Memtrace Rail PreToolUse hook in a Claude settings.json.
224
255
  *
225
256
  * This is what makes Rail "part of memtrace" rather than a manual install:
226
257
  * `memtrace install` wires a PreToolUse hook that pipes every Grep/Glob/Bash
227
- * attempt through `memtrace route --hook`. The hook defaults to **observe**
228
- * (zero behavior change) — `MEMTRACE_RAIL=nudge|rail|strict` opts into more.
229
- * Mode is intentionally NOT baked into the command so the user can change it
230
- * via env without re-installing.
258
+ * attempt through the Rail router (see {@link claudeRailHookCommand} for the
259
+ * transport). The hook defaults to **observe** (zero behavior change) —
260
+ * `MEMTRACE_RAIL=nudge|rail|strict` opts into more. Mode is intentionally NOT
261
+ * baked into the command so the user can change it via env without
262
+ * re-installing.
231
263
  *
232
- * Idempotent: any prior Rail entry (identified by {@link RAIL_HOOK_MARKER}) is
233
- * replaced, and unrelated PreToolUse hooks are preserved. Never overwrites a
234
- * malformed file.
264
+ * Idempotent: any prior Rail entry (identified by {@link RAIL_HOOK_MARKERS})
265
+ * is replaced, and unrelated PreToolUse hooks are preserved. Never overwrites
266
+ * a malformed file.
235
267
  */
236
- export function registerRailHookInSettingsAt(settingsPath, memtraceBinary) {
268
+ export function registerRailHookInSettingsAt(settingsPath, memtraceBinary, platform = process.platform) {
237
269
  const { value, corrupted, backupPath } = safeReadJson(settingsPath);
238
270
  if (corrupted) {
239
271
  console.warn(`memtrace: ${settingsPath} is malformed; backed up to ${backupPath}. Skipped Rail hook registration.`);
@@ -252,7 +284,7 @@ export function registerRailHookInSettingsAt(settingsPath, memtraceBinary) {
252
284
  hooks: [
253
285
  {
254
286
  type: 'command',
255
- command: `${memtraceBinary} route --hook`,
287
+ command: claudeRailHookCommand(memtraceBinary, platform),
256
288
  },
257
289
  ],
258
290
  });
@@ -260,9 +292,9 @@ export function registerRailHookInSettingsAt(settingsPath, memtraceBinary) {
260
292
  writeJsonAtomic(settingsPath, settings);
261
293
  return { registered: true };
262
294
  }
263
- /** True when a PreToolUse entry is a memtrace-owned Rail hook. */
295
+ /** True when a PreToolUse entry is a memtrace-owned Rail hook (any generation). */
264
296
  function isRailHookEntry(entry) {
265
- return hookCommands(entry).some((command) => command.includes(RAIL_HOOK_MARKER));
297
+ return hookCommands(entry).some((command) => RAIL_HOOK_MARKERS.some((marker) => command.includes(marker)));
266
298
  }
267
299
  function isLegacyMemtraceHookEntry(entry) {
268
300
  return hookCommands(entry).some((command) => (LEGACY_HOOK_SCRIPT_BASENAMES.some((basename) => command.includes(basename))));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "memtrace-skills",
3
- "version": "1.1.4",
3
+ "version": "1.1.6",
4
4
  "description": "Memtrace skills for AI coding agents — codebase exploration, temporal evolution, impact analysis, and more.",
5
5
  "type": "module",
6
6
  "bin": {