trace-mcp 1.2.1 → 1.5.3

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.
@@ -0,0 +1,195 @@
1
+ @echo off
2
+ REM trace-mcp-guard v0.4.0
3
+ REM trace-mcp PreToolUse guard (Windows)
4
+ REM Blocks Read/Grep/Glob/Bash on source code files - redirects to trace-mcp tools.
5
+ REM Allows: non-code files, Read before Edit, safe Bash commands (git, npm, build, test).
6
+ REM
7
+ REM Consultation markers: trace-mcp server writes markers when tools access files.
8
+ REM If a marker exists, Read is allowed immediately.
9
+ REM
10
+ REM Install: add to ~\.claude\settings.json or .claude\settings.local.json
11
+ REM See README.md for setup instructions.
12
+
13
+ setlocal enabledelayedexpansion
14
+
15
+ REM Read JSON from stdin into a temp file
16
+ set "TMPINPUT=%TEMP%\trace-mcp-guard-input-%RANDOM%.json"
17
+ more > "%TMPINPUT%"
18
+
19
+ REM Get tool name from env or parse from JSON
20
+ if defined CLAUDE_TOOL_NAME (
21
+ set "TOOL_NAME=%CLAUDE_TOOL_NAME%"
22
+ ) else (
23
+ for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_name"`) do set "TOOL_NAME=%%i"
24
+ )
25
+
26
+ if "%TOOL_NAME%"=="" goto :allow
27
+
28
+ REM --- Read ---
29
+ if /i not "%TOOL_NAME%"=="Read" goto :check_grep
30
+
31
+ for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_input.file_path"`) do set "FILE_PATH=%%i"
32
+
33
+ REM Block .env files
34
+ echo "%FILE_PATH%" | findstr /i /r "\.env" >nul 2>&1
35
+ if %errorlevel%==0 (
36
+ set "REL_PATH=%FILE_PATH%"
37
+ call :deny "Use get_env_vars for .env files - it masks sensitive values (passwords, API keys, tokens)." "trace-mcp alternatives: get_env_vars to list keys + types without exposing secrets."
38
+ goto :cleanup
39
+ )
40
+
41
+ REM Allow non-code files
42
+ echo "%FILE_PATH%" | findstr /i /r "\.md$ \.json$ \.jsonc$ \.yaml$ \.yml$ \.toml$ \.ini$ \.cfg$ \.txt$ \.html$ \.xml$ \.csv$ \.svg$ \.lock$ \.log$ \.sh$ \.bash$ \.zsh$ \.fish$ \.ps1$ \.bat$ \.cmd$" >nul 2>&1
43
+ if %errorlevel%==0 goto :allow
44
+
45
+ REM Allow files in non-source dirs
46
+ echo "%FILE_PATH%" | findstr /i /r "node_modules\\ vendor\\ dist\\ build\\ \.git\\" >nul 2>&1
47
+ if %errorlevel%==0 goto :allow
48
+
49
+ REM Block code file reads - redirect to trace-mcp
50
+ echo "%FILE_PATH%" | findstr /i /r "\.ts$ \.tsx$ \.js$ \.jsx$ \.mjs$ \.cjs$ \.py$ \.pyi$ \.go$ \.rs$ \.java$ \.kt$ \.kts$ \.rb$ \.php$ \.cs$ \.cpp$ \.c$ \.h$ \.hpp$ \.swift$ \.scala$ \.vue$ \.svelte$ \.astro$" >nul 2>&1
51
+ if not %errorlevel%==0 goto :allow
52
+
53
+ REM Check consultation markers (trace-mcp server writes these when get_outline/get_symbol called)
54
+ for /f "usebackq delims=" %%h in (`powershell -NoProfile -Command "[System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes('%CD%'))).Replace('-','').Substring(0,12).ToLower()"`) do set "PROJ_HASH=%%h"
55
+ set "REL_FOR_HASH=%FILE_PATH%"
56
+ set "REL_FOR_HASH=!REL_FOR_HASH:%CD%\=!"
57
+ set "REL_FOR_HASH=!REL_FOR_HASH:\=/!"
58
+ for /f "usebackq delims=" %%h in (`powershell -NoProfile -Command "[System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes('!REL_FOR_HASH!'))).Replace('-','').ToLower()"`) do set "FILE_HASH=%%h"
59
+ if exist "%TEMP%\trace-mcp-consulted-!PROJ_HASH!\!FILE_HASH!" goto :allow
60
+
61
+ REM Allow on second attempt (agent needs full content for Edit)
62
+ set "SESSION_ID=default"
63
+ for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "try { (Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).session_id } catch { 'default' }"`) do set "SESSION_ID=%%i"
64
+ set "DENY_DIR=%TEMP%\trace-mcp-guard-%SESSION_ID%"
65
+ if not exist "%DENY_DIR%" mkdir "%DENY_DIR%" 2>nul
66
+
67
+ REM Create a hash of the file path for the marker
68
+ for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "[System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes('%FILE_PATH%'))).Replace('-','').ToLower()"`) do set "MARKER_HASH=%%i"
69
+ set "DENY_MARKER=%DENY_DIR%\%MARKER_HASH%"
70
+
71
+ if exist "%DENY_MARKER%" (
72
+ del "%DENY_MARKER%" 2>nul
73
+ goto :allow
74
+ )
75
+ echo.> "%DENY_MARKER%"
76
+
77
+ call :deny "Use trace-mcp for code reading - it returns only what you need, saving tokens." "trace-mcp alternatives: get_outline, get_symbol, search, get_feature_context. If you need full file content before editing, retry Read - it will be allowed."
78
+ goto :cleanup
79
+
80
+ :check_grep
81
+ REM --- Grep ---
82
+ if /i not "%TOOL_NAME%"=="Grep" goto :check_glob
83
+
84
+ for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_input.path"`) do set "GREP_PATH=%%i"
85
+ for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_input.glob"`) do set "GREP_GLOB=%%i"
86
+ for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_input.type"`) do set "GREP_TYPE=%%i"
87
+
88
+ REM Block grep on .env files
89
+ echo "%GREP_GLOB%" | findstr /i /r "\.env" >nul 2>&1
90
+ if %errorlevel%==0 (
91
+ call :deny "Use get_env_vars for .env files - it masks sensitive values." "trace-mcp alternatives: get_env_vars with pattern filter."
92
+ goto :cleanup
93
+ )
94
+ echo "%GREP_PATH%" | findstr /i /r "\.env" >nul 2>&1
95
+ if %errorlevel%==0 (
96
+ call :deny "Use get_env_vars for .env files - it masks sensitive values." "trace-mcp alternatives: get_env_vars with pattern filter."
97
+ goto :cleanup
98
+ )
99
+
100
+ REM Allow grep on non-code file types
101
+ echo "%GREP_GLOB%" | findstr /i /r "\.md \.json \.ya*ml \.toml \.txt \.html \.xml \.csv \.cfg \.ini \.lock \.log" >nul 2>&1
102
+ if %errorlevel%==0 goto :allow
103
+
104
+ REM Allow grep on non-code type filter
105
+ if /i "%GREP_TYPE%"=="md" goto :allow
106
+ if /i "%GREP_TYPE%"=="json" goto :allow
107
+ if /i "%GREP_TYPE%"=="yaml" goto :allow
108
+ if /i "%GREP_TYPE%"=="toml" goto :allow
109
+ if /i "%GREP_TYPE%"=="xml" goto :allow
110
+ if /i "%GREP_TYPE%"=="html" goto :allow
111
+ if /i "%GREP_TYPE%"=="csv" goto :allow
112
+
113
+ REM Allow grep on config dirs
114
+ echo "%GREP_PATH%" | findstr /i /r "node_modules vendor dist build \.git" >nul 2>&1
115
+ if %errorlevel%==0 goto :allow
116
+
117
+ for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_input.pattern"`) do set "PATTERN=%%i"
118
+ call :deny "Use trace-mcp for code search - it understands symbols and relationships." "trace-mcp alternatives: search, find_usages, get_call_graph. Use Grep only for non-code files (.md, .json, .yaml, config)."
119
+ goto :cleanup
120
+
121
+ :check_glob
122
+ REM --- Glob ---
123
+ if /i not "%TOOL_NAME%"=="Glob" goto :check_bash
124
+
125
+ for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_input.pattern"`) do set "GLOB_PATTERN=%%i"
126
+
127
+ REM Block glob on .env patterns
128
+ echo "%GLOB_PATTERN%" | findstr /i /r "\.env" >nul 2>&1
129
+ if %errorlevel%==0 (
130
+ call :deny "Use get_env_vars for .env files - it masks sensitive values." "trace-mcp alternatives: get_env_vars to list all env vars across all .env files."
131
+ goto :cleanup
132
+ )
133
+
134
+ REM Allow glob for non-code patterns
135
+ echo "%GLOB_PATTERN%" | findstr /i /r "\.md \.json \.ya*ml \.toml \.txt \.html \.xml \.csv \.cfg \.ini \.lock \.log" >nul 2>&1
136
+ if %errorlevel%==0 goto :allow
137
+
138
+ call :deny "Use trace-mcp for code file discovery - it knows your project structure." "trace-mcp alternatives: get_project_map, search with file_pattern, get_outline. Use Glob only for non-code file patterns."
139
+ goto :cleanup
140
+
141
+ :check_bash
142
+ REM --- Bash ---
143
+ if /i not "%TOOL_NAME%"=="Bash" goto :allow
144
+
145
+ for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_input.command"`) do set "COMMAND=%%i"
146
+
147
+ REM Allow safe commands
148
+ echo "%COMMAND%" | findstr /i /r /c:"^git " /c:"^npm " /c:"^npx " /c:"^pnpm " /c:"^yarn " /c:"^bun " /c:"^node " /c:"^deno " /c:"^cargo " /c:"^go " /c:"^make " /c:"^mvn " /c:"^gradle " /c:"^docker " /c:"^kubectl " /c:"^helm " /c:"^terraform " /c:"^pip " /c:"^poetry " /c:"^uv " /c:"^pytest " /c:"^vitest " /c:"^jest " /c:"^phpunit " /c:"^composer " /c:"^artisan " /c:"^rails " /c:"^bundle " /c:"^mix " /c:"^dotnet " /c:"^cmake " >nul 2>&1
149
+ if %errorlevel%==0 goto :allow
150
+
151
+ REM Block bash commands targeting .env files - prevent secret leakage
152
+ echo "%COMMAND%" | findstr /i /r "\.env" >nul 2>&1
153
+ if %errorlevel%==0 (
154
+ call :deny "Use get_env_vars for .env files - it masks sensitive values (passwords, API keys, tokens)." "trace-mcp alternatives: get_env_vars to list keys + types without exposing secrets. Never access .env files via shell."
155
+ goto :cleanup
156
+ )
157
+
158
+ REM Block code exploration via bash
159
+ set "HAS_EXPLORE=0"
160
+ echo "%COMMAND%" | findstr /i /r "grep rg find cat head tail less more awk sed" >nul 2>&1
161
+ if %errorlevel%==0 set "HAS_EXPLORE=1"
162
+
163
+ set "HAS_CODE=0"
164
+ echo "%COMMAND%" | findstr /i /r "\.ts \.tsx \.js \.jsx \.py \.go \.rs \.java \.rb \.php \.cs \.cpp \.c \.h \.swift \.scala \.vue \.svelte" >nul 2>&1
165
+ if %errorlevel%==0 set "HAS_CODE=1"
166
+
167
+ if "%HAS_EXPLORE%"=="1" if "%HAS_CODE%"=="1" (
168
+ call :deny "Use trace-mcp instead of shell commands for code exploration." "trace-mcp has structured tools: search, get_symbol, get_outline, find_usages. Use Bash only for builds, tests, git, and system commands."
169
+ goto :cleanup
170
+ )
171
+
172
+ goto :allow
173
+
174
+ REM --- Helpers ---
175
+
176
+ :deny
177
+ set "REASON=%~1"
178
+ set "CONTEXT=%~2"
179
+ echo {
180
+ echo "hookSpecificOutput": {
181
+ echo "hookEventName": "PreToolUse",
182
+ echo "permissionDecision": "deny",
183
+ echo "permissionDecisionReason": "%REASON%",
184
+ echo "additionalContext": "%CONTEXT%"
185
+ echo }
186
+ echo }
187
+ goto :eof
188
+
189
+ :allow
190
+ del "%TMPINPUT%" 2>nul
191
+ exit /b 0
192
+
193
+ :cleanup
194
+ del "%TMPINPUT%" 2>nul
195
+ exit /b 0
@@ -1,9 +1,13 @@
1
1
  #!/usr/bin/env bash
2
- # trace-mcp-guard v0.1.0
2
+ # trace-mcp-guard v0.4.0
3
3
  # trace-mcp PreToolUse guard
4
4
  # Blocks Read/Grep/Glob/Bash on source code files → redirects to trace-mcp tools.
5
5
  # Allows: non-code files, Read before Edit, safe Bash commands (git, npm, build, test).
6
6
  #
7
+ # Consultation markers: trace-mcp server writes markers when tools access files
8
+ # (get_outline, get_symbol, etc.). If a marker exists for a file, Read is allowed
9
+ # immediately — the agent already consulted trace-mcp for this file.
10
+ #
7
11
  # Install: add to ~/.claude/settings.json or .claude/settings.local.json
8
12
  # See README.md for setup instructions.
9
13
 
@@ -16,11 +20,19 @@ TOOL_NAME="${CLAUDE_TOOL_NAME:-$(echo "$INPUT" | jq -r '.tool_name // empty')}"
16
20
  CODE_EXT_RE='\.(ts|tsx|js|jsx|mjs|cjs|py|pyi|go|rs|java|kt|kts|rb|php|cs|cpp|c|h|hpp|swift|scala|vue|svelte|astro|blade\.php)$'
17
21
 
18
22
  # Non-code extensions — always allow
19
- NONCODE_EXT_RE='\.(md|json|jsonc|yaml|yml|toml|ini|cfg|env|txt|html|xml|csv|svg|lock|log|sh|bash|zsh|fish|ps1|bat|cmd|dockerfile|dockerignore|gitignore|gitattributes|editorconfig|prettierrc|eslintrc|stylelintrc)$'
23
+ NONCODE_EXT_RE='\.(md|json|jsonc|yaml|yml|toml|ini|cfg|txt|html|xml|csv|svg|lock|log|sh|bash|zsh|fish|ps1|bat|cmd|dockerfile|dockerignore|gitignore|gitattributes|editorconfig|prettierrc|eslintrc|stylelintrc)$'
24
+
25
+ # .env files — always route through trace-mcp to prevent secret leakage
26
+ ENV_FILE_RE='\.env(\.[a-zA-Z0-9._-]+)?$'
20
27
 
21
28
  # Safe bash command prefixes — never block
22
29
  SAFE_BASH_RE='^(git |npm |npx |pnpm |yarn |bun |node |deno |cargo |go |make |mvn |gradle |docker |kubectl |helm |terraform |pip |poetry |uv |pytest |vitest |jest |phpunit |composer |artisan |rails |bundle |mix |dotnet |cmake |ninja |meson )'
23
30
 
31
+ # Cross-platform sha256 hash (Linux: sha256sum, macOS: shasum)
32
+ file_sha256() {
33
+ echo -n "$1" | sha256sum 2>/dev/null | cut -d' ' -f1 || echo -n "$1" | shasum -a 256 2>/dev/null | cut -d' ' -f1
34
+ }
35
+
24
36
  deny() {
25
37
  local reason="$1"
26
38
  local context="$2"
@@ -42,10 +54,31 @@ SESSION_ID=$(echo "$INPUT" | jq -r '.session_id // "default"')
42
54
  DENY_DIR="/tmp/trace-mcp-guard-${SESSION_ID}"
43
55
  mkdir -p "$DENY_DIR" 2>/dev/null
44
56
 
57
+ # Consultation markers: trace-mcp server writes these when get_outline/get_symbol/etc. are called.
58
+ # If a file was already consulted via trace-mcp, allow Read immediately (agent needs full content for Edit).
59
+ # Dir format: $TMPDIR/trace-mcp-consulted-{sha256(projectRoot)[:12]}/{sha256(relPath)}
60
+ PROJECT_ROOT="$(pwd)"
61
+ if command -v sha256sum >/dev/null 2>&1; then
62
+ PROJECT_HASH=$(echo -n "$PROJECT_ROOT" | sha256sum | cut -c1-12)
63
+ elif command -v shasum >/dev/null 2>&1; then
64
+ PROJECT_HASH=$(echo -n "$PROJECT_ROOT" | shasum -a 256 | cut -c1-12)
65
+ else
66
+ PROJECT_HASH=""
67
+ fi
68
+ CONSULTED_DIR="${TMPDIR:-/tmp}/trace-mcp-consulted-${PROJECT_HASH}"
69
+
45
70
  # --- Read ---
46
71
  if [[ "$TOOL_NAME" == "Read" ]]; then
47
72
  FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
48
73
 
74
+ # Block .env files — prevent secret leakage to AI model context
75
+ if echo "$FILE_PATH" | grep -qiE "$ENV_FILE_RE"; then
76
+ REL_PATH=$(echo "$FILE_PATH" | sed "s|^$(pwd)/||")
77
+ deny \
78
+ "Use get_env_vars for .env files — it masks sensitive values (passwords, API keys, tokens)." \
79
+ "trace-mcp alternatives for ${REL_PATH}:\\n- get_env_vars { \\\"file\\\": \\\"${REL_PATH}\\\" } — list keys + types without exposing secrets\\n- get_env_vars { \\\"pattern\\\": \\\"DB_\\\" } — filter by key prefix\\nNever read .env files directly — secrets will leak into AI model context."
80
+ fi
81
+
49
82
  # Allow non-code files
50
83
  if echo "$FILE_PATH" | grep -qiE "$NONCODE_EXT_RE"; then
51
84
  exit 0
@@ -59,8 +92,18 @@ if [[ "$TOOL_NAME" == "Read" ]]; then
59
92
 
60
93
  # Block code file reads → redirect to trace-mcp
61
94
  if echo "$FILE_PATH" | grep -qiE "$CODE_EXT_RE"; then
95
+ # Compute relative path for consultation marker check (server writes markers keyed by relative path)
96
+ REL_PATH_FOR_HASH=$(echo "$FILE_PATH" | sed "s|^${PROJECT_ROOT}/||")
97
+ CONSULTED_HASH=$(file_sha256 "$REL_PATH_FOR_HASH")
98
+
99
+ # Check if file was already consulted via trace-mcp (get_outline, get_symbol, etc.)
100
+ if [[ -n "$PROJECT_HASH" && -f "$CONSULTED_DIR/$CONSULTED_HASH" ]]; then
101
+ # File was consulted via trace-mcp → allow Read (agent needs full content for Edit)
102
+ exit 0
103
+ fi
104
+
62
105
  # Allow on second attempt — agent needs full content for Edit
63
- DENY_MARKER="$DENY_DIR/$(echo "$FILE_PATH" | md5sum | cut -d' ' -f1)"
106
+ DENY_MARKER="$DENY_DIR/$(file_sha256 "$FILE_PATH")"
64
107
  if [[ -f "$DENY_MARKER" ]]; then
65
108
  rm -f "$DENY_MARKER"
66
109
  exit 0
@@ -82,8 +125,15 @@ if [[ "$TOOL_NAME" == "Grep" ]]; then
82
125
  GREP_GLOB=$(echo "$INPUT" | jq -r '.tool_input.glob // empty')
83
126
  GREP_TYPE=$(echo "$INPUT" | jq -r '.tool_input.type // empty')
84
127
 
128
+ # Block grep on .env files — prevent secret leakage
129
+ if echo "$GREP_GLOB" | grep -qiE '\.env' || echo "$GREP_PATH" | grep -qiE "$ENV_FILE_RE"; then
130
+ deny \
131
+ "Use get_env_vars for .env files — it masks sensitive values." \
132
+ "trace-mcp alternatives:\\n- get_env_vars { \\\"pattern\\\": \\\"search_term\\\" } — find env vars by key pattern without exposing values"
133
+ fi
134
+
85
135
  # Allow grep on non-code file types
86
- if echo "$GREP_GLOB" | grep -qiE '\.(md|json|ya?ml|toml|env|txt|html|xml|csv|cfg|ini|lock|log)'; then
136
+ if echo "$GREP_GLOB" | grep -qiE '\.(md|json|ya?ml|toml|txt|html|xml|csv|cfg|ini|lock|log)'; then
87
137
  exit 0
88
138
  fi
89
139
 
@@ -108,8 +158,15 @@ fi
108
158
  if [[ "$TOOL_NAME" == "Glob" ]]; then
109
159
  GLOB_PATTERN=$(echo "$INPUT" | jq -r '.tool_input.pattern // empty')
110
160
 
161
+ # Block glob on .env patterns
162
+ if echo "$GLOB_PATTERN" | grep -qiE '\.env'; then
163
+ deny \
164
+ "Use get_env_vars for .env files — it masks sensitive values." \
165
+ "trace-mcp alternatives:\\n- get_env_vars {} — list all env vars across all .env files"
166
+ fi
167
+
111
168
  # Allow glob for non-code patterns
112
- if echo "$GLOB_PATTERN" | grep -qiE '\.(md|json|ya?ml|toml|env|txt|html|xml|csv|cfg|ini|lock|log)'; then
169
+ if echo "$GLOB_PATTERN" | grep -qiE '\.(md|json|ya?ml|toml|txt|html|xml|csv|cfg|ini|lock|log)'; then
113
170
  exit 0
114
171
  fi
115
172
 
@@ -128,6 +185,13 @@ if [[ "$TOOL_NAME" == "Bash" ]]; then
128
185
  exit 0
129
186
  fi
130
187
 
188
+ # Block bash commands targeting .env files — prevent secret leakage
189
+ if echo "$COMMAND" | grep -qiE "$ENV_FILE_RE"; then
190
+ deny \
191
+ "Use get_env_vars for .env files — it masks sensitive values (passwords, API keys, tokens)." \
192
+ "trace-mcp alternatives:\\n- get_env_vars {} — list all env vars across all .env files\\n- get_env_vars { \\\"pattern\\\": \\\"DB_\\\" } — filter by key prefix\\nNever access .env files via shell — secrets will leak into AI model context."
193
+ fi
194
+
131
195
  # Block code exploration via bash (grep, find, cat, head, tail on code files)
132
196
  if echo "$COMMAND" | grep -qE '(^|\|)\s*(grep|rg|find|cat|head|tail|less|more|awk|sed)\s' && echo "$COMMAND" | grep -qiE "$CODE_EXT_RE"; then
133
197
  deny \
@@ -0,0 +1,33 @@
1
+ @echo off
2
+ REM trace-mcp-precompact v0.1.0
3
+ REM trace-mcp PreCompact hook (Windows)
4
+ REM Injects session snapshot into compacted context to prevent "compaction amnesia".
5
+ REM Reads the live snapshot file written by the running trace-mcp MCP server and
6
+ REM returns it via the systemMessage field in Claude Code's hook output schema.
7
+ REM
8
+ REM Install: add to ~\.claude\settings.json or .claude\settings.local.json under PreCompact
9
+ REM See README.md for setup instructions.
10
+
11
+ setlocal enabledelayedexpansion
12
+
13
+ REM Determine project root from working directory
14
+ set "PROJECT_ROOT=%CD%"
15
+
16
+ REM Compute project hash using PowerShell (sha256, first 12 hex chars)
17
+ for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "[System.BitConverter]::ToString([System.Security.Cryptography.SHA256]::Create().ComputeHash([System.Text.Encoding]::UTF8.GetBytes('%PROJECT_ROOT%'))).Replace('-','').Substring(0,12).ToLower()"`) do set "PROJECT_HASH=%%i"
18
+
19
+ if "%PROJECT_HASH%"=="" goto :done
20
+
21
+ set "SNAPSHOT_FILE=%USERPROFILE%\.trace-mcp\sessions\%PROJECT_HASH%-snapshot.json"
22
+
23
+ if not exist "%SNAPSHOT_FILE%" goto :done
24
+
25
+ REM Read markdown from snapshot file and output as systemMessage
26
+ for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "$j = Get-Content '%SNAPSHOT_FILE%' -Raw | ConvertFrom-Json; if ($j.markdown) { $j.markdown }"`) do set "MARKDOWN=%%i"
27
+
28
+ if "%MARKDOWN%"=="" goto :done
29
+
30
+ powershell -NoProfile -Command "$m = (Get-Content '%SNAPSHOT_FILE%' -Raw | ConvertFrom-Json).markdown; @{systemMessage=$m} | ConvertTo-Json -Compress"
31
+
32
+ :done
33
+ exit /b 0
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bash
2
+ # trace-mcp-precompact v0.1.0
3
+ # trace-mcp PreCompact hook
4
+ # Injects session snapshot into compacted context to prevent "compaction amnesia".
5
+ # Reads the live snapshot file written by the running trace-mcp MCP server and
6
+ # returns it via the systemMessage field in Claude Code's hook output schema.
7
+ #
8
+ # Install: add to ~/.claude/settings.json or .claude/settings.local.json under PreCompact
9
+ # See README.md for setup instructions.
10
+
11
+ set -euo pipefail
12
+
13
+ # Determine project root from working directory
14
+ PROJECT_ROOT="$(pwd)"
15
+
16
+ # Compute project hash (same algorithm as trace-mcp: sha256, first 12 hex chars)
17
+ if command -v sha256sum >/dev/null 2>&1; then
18
+ PROJECT_HASH=$(echo -n "$PROJECT_ROOT" | sha256sum | cut -c1-12)
19
+ elif command -v shasum >/dev/null 2>&1; then
20
+ PROJECT_HASH=$(echo -n "$PROJECT_ROOT" | shasum -a 256 | cut -c1-12)
21
+ else
22
+ # Fallback: no hash available, exit silently
23
+ exit 0
24
+ fi
25
+
26
+ SNAPSHOT_FILE="$HOME/.trace-mcp/sessions/${PROJECT_HASH}-snapshot.json"
27
+
28
+ # If no snapshot file exists, exit silently (no session data yet)
29
+ if [[ ! -f "$SNAPSHOT_FILE" ]]; then
30
+ exit 0
31
+ fi
32
+
33
+ # Check file freshness — skip if older than 10 minutes (stale session)
34
+ if command -v stat >/dev/null 2>&1; then
35
+ if [[ "$(uname)" == "Darwin" ]]; then
36
+ FILE_MTIME=$(stat -f %m "$SNAPSHOT_FILE" 2>/dev/null || echo 0)
37
+ else
38
+ FILE_MTIME=$(stat -c %Y "$SNAPSHOT_FILE" 2>/dev/null || echo 0)
39
+ fi
40
+ NOW=$(date +%s)
41
+ AGE=$(( NOW - FILE_MTIME ))
42
+ if [[ $AGE -gt 600 ]]; then
43
+ exit 0
44
+ fi
45
+ fi
46
+
47
+ # Read the markdown snapshot from the JSON file
48
+ MARKDOWN=$(jq -r '.markdown // empty' "$SNAPSHOT_FILE" 2>/dev/null)
49
+
50
+ if [[ -z "$MARKDOWN" ]]; then
51
+ exit 0
52
+ fi
53
+
54
+ # Output systemMessage for Claude Code to inject into compacted context
55
+ jq -n --arg msg "$MARKDOWN" '{ "systemMessage": $msg }'
56
+
57
+ exit 0
@@ -0,0 +1,43 @@
1
+ @echo off
2
+ REM trace-mcp-reindex v0.1.0
3
+ REM trace-mcp PostToolUse auto-reindex hook (Windows)
4
+ REM Triggers incremental reindex after Edit/Write/MultiEdit on code files.
5
+ REM Runs trace-mcp index-file in the background - non-blocking.
6
+ REM
7
+ REM Install: add to ~\.claude\settings.json or .claude\settings.local.json under PostToolUse
8
+ REM See README.md for setup instructions.
9
+
10
+ setlocal enabledelayedexpansion
11
+
12
+ REM Read JSON from stdin into a temp file
13
+ set "TMPINPUT=%TEMP%\trace-mcp-reindex-input-%RANDOM%.json"
14
+ more > "%TMPINPUT%"
15
+
16
+ REM Get tool name from env or parse from JSON
17
+ if defined CLAUDE_TOOL_NAME (
18
+ set "TOOL_NAME=%CLAUDE_TOOL_NAME%"
19
+ ) else (
20
+ for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_name"`) do set "TOOL_NAME=%%i"
21
+ )
22
+
23
+ REM Only handle edit-like tools
24
+ if /i not "%TOOL_NAME%"=="Edit" if /i not "%TOOL_NAME%"=="Write" if /i not "%TOOL_NAME%"=="MultiEdit" goto :done
25
+
26
+ for /f "usebackq delims=" %%i in (`powershell -NoProfile -Command "(Get-Content '%TMPINPUT%' -Raw | ConvertFrom-Json).tool_input.file_path"`) do set "FILE_PATH=%%i"
27
+
28
+ if "%FILE_PATH%"=="" goto :done
29
+
30
+ REM Skip non-code files
31
+ echo "%FILE_PATH%" | findstr /i /r "\.md$ \.json$ \.jsonc$ \.yaml$ \.yml$ \.toml$ \.ini$ \.cfg$ \.env \.txt$ \.html$ \.xml$ \.csv$ \.svg$ \.lock$ \.log$ \.sh$ \.bash$ \.zsh$ \.fish$ \.ps1$ \.bat$ \.cmd$" >nul 2>&1
32
+ if %errorlevel%==0 goto :done
33
+
34
+ REM Skip if not a recognised code file
35
+ echo "%FILE_PATH%" | findstr /i /r "\.ts$ \.tsx$ \.js$ \.jsx$ \.mjs$ \.cjs$ \.py$ \.pyi$ \.go$ \.rs$ \.java$ \.kt$ \.kts$ \.rb$ \.php$ \.cs$ \.cpp$ \.c$ \.h$ \.hpp$ \.swift$ \.scala$ \.vue$ \.svelte$ \.astro$" >nul 2>&1
36
+ if not %errorlevel%==0 goto :done
37
+
38
+ REM Reindex in background - non-blocking, silent
39
+ start /b "" cmd /c "trace-mcp index-file "%FILE_PATH%" >nul 2>&1"
40
+
41
+ :done
42
+ del "%TMPINPUT%" 2>nul
43
+ exit /b 0
@@ -0,0 +1,33 @@
1
+ @echo off
2
+ REM trace-mcp-worktree v0.1.0
3
+ REM trace-mcp WorktreeCreate / WorktreeRemove hook
4
+ REM On WorktreeCreate: registers and indexes the new worktree.
5
+ REM On WorktreeRemove: deregisters the worktree project.
6
+
7
+ setlocal enabledelayedexpansion
8
+
9
+ set "INPUT="
10
+ for /f "delims=" %%i in ('more') do set "INPUT=!INPUT!%%i"
11
+
12
+ REM Extract event type
13
+ for /f "delims=" %%e in ('echo !INPUT! ^| jq -r ".hook_event_name // .event // empty" 2^>nul') do set "EVENT=%%e"
14
+ if not defined CLAUDE_HOOK_EVENT set "CLAUDE_HOOK_EVENT=%EVENT%"
15
+ if defined CLAUDE_HOOK_EVENT set "EVENT=%CLAUDE_HOOK_EVENT%"
16
+
17
+ REM Extract worktree path
18
+ for /f "delims=" %%p in ('echo !INPUT! ^| jq -r ".tool_input.path // .worktree_path // .path // .cwd // empty" 2^>nul') do set "WPATH=%%p"
19
+
20
+ if "%WPATH%"=="" exit /b 0
21
+
22
+ if "%EVENT%"=="WorktreeCreate" (
23
+ start /b trace-mcp add "%WPATH%" --force --json >nul 2>&1
24
+ ) else if "%EVENT%"=="WorktreeRemove" (
25
+ REM Clean up worktree DB file if it exists
26
+ for /f "delims=" %%h in ('echo %WPATH%^| certutil -hashfile - SHA256 2^>nul ^| findstr /v "hash"') do set "PHASH=%%h"
27
+ if defined PHASH (
28
+ set "PHASH=!PHASH:~0,12!"
29
+ del /q "%USERPROFILE%\.trace-mcp\db\!PHASH!.db" "%USERPROFILE%\.trace-mcp\db\!PHASH!.db-shm" "%USERPROFILE%\.trace-mcp\db\!PHASH!.db-wal" 2>nul
30
+ )
31
+ )
32
+
33
+ exit /b 0
@@ -0,0 +1,57 @@
1
+ #!/usr/bin/env bash
2
+ # trace-mcp-worktree v0.1.0
3
+ # trace-mcp WorktreeCreate / WorktreeRemove hook
4
+ # On WorktreeCreate: registers and indexes the new worktree so trace-mcp tools work immediately.
5
+ # On WorktreeRemove: deregisters the worktree project from the registry.
6
+ #
7
+ # Install: add to ~/.claude/settings.json or .claude/settings.local.json
8
+ # See README.md for setup instructions.
9
+
10
+ set -euo pipefail
11
+
12
+ INPUT=$(cat)
13
+
14
+ # Claude Code passes the event type via CLAUDE_HOOK_EVENT or hook_event_name
15
+ EVENT="${CLAUDE_HOOK_EVENT:-}"
16
+ if [[ -z "$EVENT" ]]; then
17
+ EVENT=$(echo "$INPUT" | jq -r '.hook_event_name // .event // empty' 2>/dev/null)
18
+ fi
19
+
20
+ # Extract worktree path from hook input
21
+ WORKTREE_PATH=$(echo "$INPUT" | jq -r '.tool_input.path // .worktree_path // .path // empty' 2>/dev/null)
22
+
23
+ if [[ -z "$WORKTREE_PATH" ]]; then
24
+ # Fallback: use working directory from input
25
+ WORKTREE_PATH=$(echo "$INPUT" | jq -r '.cwd // empty' 2>/dev/null)
26
+ fi
27
+
28
+ if [[ -z "$WORKTREE_PATH" ]]; then
29
+ exit 0
30
+ fi
31
+
32
+ # Resolve to absolute path
33
+ WORKTREE_PATH=$(cd "$WORKTREE_PATH" 2>/dev/null && pwd || echo "$WORKTREE_PATH")
34
+
35
+ case "$EVENT" in
36
+ WorktreeCreate|worktree_create|create)
37
+ # Register and index the new worktree in the background
38
+ # --force: re-register even if parent repo is already known (worktree is a different root)
39
+ nohup trace-mcp add "$WORKTREE_PATH" --force --json >/dev/null 2>&1 &
40
+ ;;
41
+ WorktreeRemove|worktree_remove|remove)
42
+ # Clean up the worktree DB file if it exists.
43
+ # The global registry entry becomes stale but is harmless — next `trace-mcp add` overwrites it.
44
+ PROJECT_HASH=""
45
+ if command -v sha256sum >/dev/null 2>&1; then
46
+ PROJECT_HASH=$(echo -n "$WORKTREE_PATH" | sha256sum | cut -c1-12)
47
+ elif command -v shasum >/dev/null 2>&1; then
48
+ PROJECT_HASH=$(echo -n "$WORKTREE_PATH" | shasum -a 256 | cut -c1-12)
49
+ fi
50
+ if [[ -n "$PROJECT_HASH" ]]; then
51
+ DB_FILE="$HOME/.trace-mcp/db/${PROJECT_HASH}.db"
52
+ rm -f "$DB_FILE" "$DB_FILE-shm" "$DB_FILE-wal" 2>/dev/null
53
+ fi
54
+ ;;
55
+ esac
56
+
57
+ exit 0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "trace-mcp",
3
- "version": "1.2.1",
3
+ "version": "1.5.3",
4
4
  "description": "Framework-aware code intelligence MCP server — 48+ frameworks, 68 languages",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -18,7 +18,7 @@
18
18
  "prepublishOnly": "npm run build"
19
19
  },
20
20
  "engines": {
21
- "node": ">=20.0.0"
21
+ "node": ">=18.0.0"
22
22
  },
23
23
  "license": "ELv2",
24
24
  "author": "Nikolai Vysotskyi",