fix: split shared 'jsts' tree-sitter query into separate 'js' and 'ts' queries (fixes #1654)#1673
fix: split shared 'jsts' tree-sitter query into separate 'js' and 'ts' queries (fixes #1654)#1673octo-patch wants to merge 3 commits intothedotmack:mainfrom
Conversation
thedotmack#1644) When the worker daemon is started from launchd, cron, or nohup, ~/.local/bin is not in PATH, so `uvx` cannot be found and ChromaDB silently fails. Add getUvxPath() utility (mirroring getBunPath()) that checks PATH first, then probes common installation locations including ~/.local/bin/uvx and ~/.cargo/bin/uvx. Use the resolved absolute path when spawning uvx on non-Windows platforms. Falls back to bare 'uvx' if not found anywhere (preserving the previous error message) rather than silently failing with no diagnostic.
Summary by CodeRabbit
WalkthroughHook command invocations are updated to use Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/services/smart-file-read/parser.ts`:
- Around line 105-109: The JS capture for class names in the Tree-sitter query
is using TypeScript's type_identifier and thus misses JS classes; update the js
query string so the class capture uses (class_declaration name: (identifier)
`@name`) `@cls` instead of (class_declaration name: (type_identifier) `@name`)
`@cls`—i.e., locate the query text containing (class_declaration name:
(type_identifier) `@name`) `@cls` in parser.ts and replace type_identifier with
identifier so JavaScript class names are correctly matched.
In `@src/utils/uvx-path.ts`:
- Around line 22-26: The spawnSync call used to probe uvx in getUvxPath
currently has no timeout and can block initialization; modify the spawnSync
invocation in getUvxPath to include a timeout (e.g., 2000 ms) in its options so
the call fails fast, and update the subsequent handling of the result variable
to treat a timeout (check result.error or result.status for timeout/ETIMEDOUT)
the same as other failures so the function falls back correctly; keep the
existing stdio/encoding options and ensure getUvxPath returns the existing
fallback when the probe times out.
- Around line 45-49: The loop that returns a uvx path currently only checks
existsSync(uvxPath) and may return a non-executable file; update the check
inside the uvxPaths iteration to also ensure the path is a regular file and
executable by using fs.statSync or statSync.isFile() and fs.accessSync(uvxPath,
fs.constants.X_OK) (catching access exceptions) before logging and returning;
reference the uvxPaths iteration and the logger debug call so the fix is applied
where existsSync(uvxPath) is used to prevent StdioClientTransport from later
failing to spawn a non-executable file.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 6e8af42c-d8d6-47dd-ade7-52aa568989bf
📒 Files selected for processing (5)
plugin/hooks/hooks.jsonplugin/scripts/bun-runner.mjssrc/services/smart-file-read/parser.tssrc/services/sync/ChromaMcpManager.tssrc/utils/uvx-path.ts
| js: ` | ||
| (function_declaration name: (identifier) @name) @func | ||
| (lexical_declaration (variable_declarator name: (identifier) @name value: [(arrow_function) (function_expression)])) @const_func | ||
| (class_declaration name: (type_identifier) @name) @cls | ||
| (method_definition name: (property_identifier) @name) @method |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Verify current tree-sitter-javascript grammar shape (read-only, no repo files required).
python - <<'PY'
import re
import urllib.request
url = "https://raw.githubusercontent.com/tree-sitter/tree-sitter-javascript/master/grammar.js"
text = urllib.request.urlopen(url, timeout=20).read().decode("utf-8").replace("\n", " ")
print("contains type_identifier:", "type_identifier" in text)
m = re.search(r"class_declaration:\s*\$ =>.*?field\('name',\s*\$\.[a-z_]+\)", text)
print("class_declaration name field snippet:", m.group(0) if m else "not found")
PYRepository: thedotmack/claude-mem
Length of output: 2646
🏁 Script executed:
cat -n src/services/smart-file-read/parser.ts | sed -n '100,130p'Repository: thedotmack/claude-mem
Length of output: 1443
🏁 Script executed:
cat -n src/services/smart-file-read/parser.ts | sed -n '170,185p'Repository: thedotmack/claude-mem
Length of output: 540
🌐 Web query:
tree-sitter-javascript class_declaration grammar node name type
💡 Result:
In tree-sitter-javascript, a class declaration is the named node type class_declaration, and its class name is exposed via the field name, whose node type is identifier:
- Grammar rule:
class_declaration: ... field('name', $.identifier) ...[1] - Static node types:
class_declaration.fields.name.types = [{ type: "identifier", named: true }][2]
So a Tree-sitter query to capture the class name is:
(class_declaration
name: (identifier) `@class.name`)Tree-sitter “field names” like name: come from the grammar’s field(...) annotations. [3]
Sources: [1] [2] [3]
Use JavaScript identifier for class names in the js query.
Line 108 references type_identifier, which is TypeScript-oriented and does not match JavaScript class names. The tree-sitter-javascript grammar defines class declaration names as identifier. This keeps the JS query invalid and silently misses JavaScript class symbols.
Suggested fix
- (class_declaration name: (type_identifier) `@name`) `@cls`
+ (class_declaration name: (identifier) `@name`) `@cls`📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| js: ` | |
| (function_declaration name: (identifier) @name) @func | |
| (lexical_declaration (variable_declarator name: (identifier) @name value: [(arrow_function) (function_expression)])) @const_func | |
| (class_declaration name: (type_identifier) @name) @cls | |
| (method_definition name: (property_identifier) @name) @method | |
| js: ` | |
| (function_declaration name: (identifier) `@name`) `@func` | |
| (lexical_declaration (variable_declarator name: (identifier) `@name` value: [(arrow_function) (function_expression)])) `@const_func` | |
| (class_declaration name: (identifier) `@name`) `@cls` | |
| (method_definition name: (property_identifier) `@name`) `@method` |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/services/smart-file-read/parser.ts` around lines 105 - 109, The JS
capture for class names in the Tree-sitter query is using TypeScript's
type_identifier and thus misses JS classes; update the js query string so the
class capture uses (class_declaration name: (identifier) `@name`) `@cls` instead of
(class_declaration name: (type_identifier) `@name`) `@cls`—i.e., locate the query
text containing (class_declaration name: (type_identifier) `@name`) `@cls` in
parser.ts and replace type_identifier with identifier so JavaScript class names
are correctly matched.
| const result = spawnSync('uvx', ['--version'], { | ||
| encoding: 'utf-8', | ||
| stdio: ['pipe', 'pipe', 'pipe'], | ||
| shell: false | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether the sync probe has a timeout configured.
rg -n --type=ts -C3 "spawnSync\('uvx'|timeout:" src/utils/uvx-path.tsRepository: thedotmack/claude-mem
Length of output: 295
🏁 Script executed:
cat -n src/utils/uvx-path.tsRepository: thedotmack/claude-mem
Length of output: 2594
🏁 Script executed:
rg -n "getUvxPath" --type ts -A 2 -B 2 | head -40Repository: thedotmack/claude-mem
Length of output: 1677
Add a timeout to the sync uvx --version probe.
The spawnSync call on line 22 has no timeout. Since getUvxPath() is invoked during service initialization (in ChromaMcpManager), an unresponsive uvx process would block initialization indefinitely. Although the function includes error handling and fallback paths, these don't prevent the hang—they only catch exceptions. Add a timeout to fail fast.
Proposed fix
const result = spawnSync('uvx', ['--version'], {
encoding: 'utf-8',
stdio: ['pipe', 'pipe', 'pipe'],
- shell: false
+ shell: false,
+ timeout: 5000
});📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| const result = spawnSync('uvx', ['--version'], { | |
| encoding: 'utf-8', | |
| stdio: ['pipe', 'pipe', 'pipe'], | |
| shell: false | |
| }); | |
| const result = spawnSync('uvx', ['--version'], { | |
| encoding: 'utf-8', | |
| stdio: ['pipe', 'pipe', 'pipe'], | |
| shell: false, | |
| timeout: 5000 | |
| }); |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/utils/uvx-path.ts` around lines 22 - 26, The spawnSync call used to probe
uvx in getUvxPath currently has no timeout and can block initialization; modify
the spawnSync invocation in getUvxPath to include a timeout (e.g., 2000 ms) in
its options so the call fails fast, and update the subsequent handling of the
result variable to treat a timeout (check result.error or result.status for
timeout/ETIMEDOUT) the same as other failures so the function falls back
correctly; keep the existing stdio/encoding options and ensure getUvxPath
returns the existing fallback when the probe times out.
| for (const uvxPath of uvxPaths) { | ||
| if (existsSync(uvxPath)) { | ||
| logger.debug('SYSTEM', 'Found uvx at known location', { path: uvxPath }); | ||
| return uvxPath; | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify whether fallback validation checks executability (X_OK) vs existence only.
rg -n --type=ts -C3 "existsSync\(uvxPath\)|accessSync|X_OK|statSync" src/utils/uvx-path.tsRepository: thedotmack/claude-mem
Length of output: 266
🏁 Script executed:
cat -n src/utils/uvx-path.tsRepository: thedotmack/claude-mem
Length of output: 2594
🏁 Script executed:
rg -n --type=ts "getUvxPath|uvxPath" src/ -A2 -B2Repository: thedotmack/claude-mem
Length of output: 2641
🏁 Script executed:
rg -n --type=ts "spawnSync.*uvx\|getUvxPath" src/ -B5 -A5Repository: thedotmack/claude-mem
Length of output: 47
🏁 Script executed:
rg -n --type=ts "getUvxPath\(\)" src/ -B3 -A3Repository: thedotmack/claude-mem
Length of output: 1660
🏁 Script executed:
rg -n --type=ts "spawnSync.*resolvedUvxPath\|spawn.*uvxPath" src/services/ -B2 -A10Repository: thedotmack/claude-mem
Length of output: 47
🏁 Script executed:
rg -n --type=ts "spawnSync.*uvxSpawnCommand" src/services/sync/ChromaMcpManager.ts -B5 -A10Repository: thedotmack/claude-mem
Length of output: 47
🏁 Script executed:
grep -n "spawnSync" src/services/sync/ChromaMcpManager.ts | head -20Repository: thedotmack/claude-mem
Length of output: 47
🏁 Script executed:
sed -n '120,140p' src/services/sync/ChromaMcpManager.tsRepository: thedotmack/claude-mem
Length of output: 771
🏁 Script executed:
cat -n src/services/sync/ChromaMcpManager.ts | sed -n '115,135p'Repository: thedotmack/claude-mem
Length of output: 1118
Validate executability before returning a fallback path.
The code at lines 45–49 checks existence only. A file that exists but lacks execute permissions would be returned and fail later when StdioClientTransport attempts to spawn it. Use accessSync with fs.constants.X_OK and verify it's a file to catch this before returning.
Proposed fix
-import { existsSync } from 'fs';
+import { accessSync, constants as fsConstants, existsSync, statSync } from 'fs';
@@
for (const uvxPath of uvxPaths) {
- if (existsSync(uvxPath)) {
- logger.debug('SYSTEM', 'Found uvx at known location', { path: uvxPath });
- return uvxPath;
- }
+ if (!existsSync(uvxPath)) continue;
+ try {
+ if (!statSync(uvxPath).isFile()) continue;
+ accessSync(uvxPath, fsConstants.X_OK);
+ logger.debug('SYSTEM', 'Found uvx at known location', { path: uvxPath });
+ return uvxPath;
+ } catch {
+ // Not executable; continue searching.
+ }
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| for (const uvxPath of uvxPaths) { | |
| if (existsSync(uvxPath)) { | |
| logger.debug('SYSTEM', 'Found uvx at known location', { path: uvxPath }); | |
| return uvxPath; | |
| } | |
| import { accessSync, constants as fsConstants, existsSync, statSync } from 'fs'; | |
| // ... other code ... | |
| for (const uvxPath of uvxPaths) { | |
| if (!existsSync(uvxPath)) continue; | |
| try { | |
| if (!statSync(uvxPath).isFile()) continue; | |
| accessSync(uvxPath, fsConstants.X_OK); | |
| logger.debug('SYSTEM', 'Found uvx at known location', { path: uvxPath }); | |
| return uvxPath; | |
| } catch { | |
| // Not executable; continue searching. | |
| } | |
| } |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/utils/uvx-path.ts` around lines 45 - 49, The loop that returns a uvx path
currently only checks existsSync(uvxPath) and may return a non-executable file;
update the check inside the uvxPaths iteration to also ensure the path is a
regular file and executable by using fs.statSync or statSync.isFile() and
fs.accessSync(uvxPath, fs.constants.X_OK) (catching access exceptions) before
logging and returning; reference the uvxPaths iteration and the logger debug
call so the fix is applied where existsSync(uvxPath) is used to prevent
StdioClientTransport from later failing to spawn a non-executable file.
|
Closed by @thedotmack. Please reopen if still relevant. Thank you for your contribution! |
Fixes #1654
Problem
The
QUERIESobject insrc/services/smart-file-read/parser.tsdefined a single"jsts"query used for JavaScript, TypeScript, and TSX files. This query included three TypeScript-only node types:interface_declarationtype_alias_declarationenum_declarationThese node types do not exist in the
tree-sitter-javascriptgrammar. While tree-sitter currently silently ignores unmatched patterns, relying on this behavior is fragile — a future tree-sitter version that strictly validates patterns would silently return zero results for all.jsfile parses (the error handler inrunBatchQuerycatches all exceptions and returns an emptyMap).Solution
Split
"jsts"into two separate queries:"js": JavaScript-only patterns (functions, classes, methods, imports, exports — no TS-only types)"ts": All patterns including TypeScript-specificinterface,type alias, andenumUpdated
getQueryKey()to map:"javascript"→"js""typescript"/"tsx"→"ts"Testing
No behavior change for TypeScript/TSX files. JavaScript files now use only patterns that are valid for the
tree-sitter-javascriptgrammar, eliminating the silent-ignore dependency.