@hasna/terminal 3.3.4 → 3.3.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.
- package/dist/mcp/server.js +2 -2
- package/dist/search/semantic.js +28 -2
- package/package.json +1 -1
- package/src/mcp/server.ts +2 -2
- package/src/search/semantic.ts +25 -2
package/dist/mcp/server.js
CHANGED
|
@@ -232,8 +232,8 @@ export function createServer() {
|
|
|
232
232
|
return { content: [{ type: "text", text: JSON.stringify(result) }] };
|
|
233
233
|
});
|
|
234
234
|
// ── search_semantic: AST-powered code search ───────────────────────────────
|
|
235
|
-
server.tool("search_semantic", "
|
|
236
|
-
query: z.string().describe("
|
|
235
|
+
server.tool("search_semantic", "Find functions, classes, components, hooks, types by NAME or SIGNATURE. Searches symbol declarations, NOT code behavior or content. Use search_content (grep) instead for pattern matching inside code (e.g., security audits, string searches, imports).", {
|
|
236
|
+
query: z.string().describe("Symbol name to search for (e.g., 'auth', 'login', 'UserService'). Matches function/class/type names, not code content."),
|
|
237
237
|
path: z.string().optional().describe("Search root (default: cwd)"),
|
|
238
238
|
kinds: z.array(z.enum(["function", "class", "interface", "type", "variable", "export", "import", "component", "hook"])).optional().describe("Filter by symbol kind"),
|
|
239
239
|
exportedOnly: z.boolean().optional().describe("Only show exported symbols (default: false)"),
|
package/dist/search/semantic.js
CHANGED
|
@@ -149,14 +149,40 @@ function extractSymbols(filePath) {
|
|
|
149
149
|
});
|
|
150
150
|
continue;
|
|
151
151
|
}
|
|
152
|
-
// Classes
|
|
152
|
+
// Classes — also extract methods inside the class body
|
|
153
153
|
const classMatch = line.match(/(?:export\s+)?class\s+(\w+)(?:\s+extends\s+(\w+))?/);
|
|
154
154
|
if (classMatch) {
|
|
155
|
+
const className = classMatch[1];
|
|
155
156
|
symbols.push({
|
|
156
|
-
name:
|
|
157
|
+
name: className, kind: "class", file, line: lineNum,
|
|
157
158
|
signature: line.trim().replace(/\{.*$/, "").trim(),
|
|
158
159
|
exported: isExported,
|
|
159
160
|
});
|
|
161
|
+
// Walk class body to find methods
|
|
162
|
+
let braceDepth = 0;
|
|
163
|
+
for (let j = i; j < lines.length; j++) {
|
|
164
|
+
for (const ch of lines[j]) {
|
|
165
|
+
if (ch === "{")
|
|
166
|
+
braceDepth++;
|
|
167
|
+
if (ch === "}")
|
|
168
|
+
braceDepth--;
|
|
169
|
+
}
|
|
170
|
+
if (j > i) {
|
|
171
|
+
const memberLine = lines[j];
|
|
172
|
+
// Class methods: async methodName(...), methodName(...), get/set name(...)
|
|
173
|
+
const methodMatch = memberLine.match(/^\s+(?:async\s+)?(?:(?:public|private|protected|static|readonly|override|abstract)\s+)*(?:get\s+|set\s+)?(\w+)\s*[\(<]/);
|
|
174
|
+
const RESERVED = new Set(["if", "for", "while", "switch", "catch", "return", "throw", "new", "delete", "typeof", "void", "yield", "await", "try", "else"]);
|
|
175
|
+
if (methodMatch && !RESERVED.has(methodMatch[1])) {
|
|
176
|
+
symbols.push({
|
|
177
|
+
name: `${className}.${methodMatch[1]}`, kind: "function", file, line: j + 1,
|
|
178
|
+
signature: memberLine.trim().replace(/\{.*$/, "").trim(),
|
|
179
|
+
exported: isExported,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
if (braceDepth === 0 && j > i)
|
|
184
|
+
break; // end of class
|
|
185
|
+
}
|
|
160
186
|
continue;
|
|
161
187
|
}
|
|
162
188
|
// Interfaces
|
package/package.json
CHANGED
package/src/mcp/server.ts
CHANGED
|
@@ -301,9 +301,9 @@ export function createServer(): McpServer {
|
|
|
301
301
|
|
|
302
302
|
server.tool(
|
|
303
303
|
"search_semantic",
|
|
304
|
-
"
|
|
304
|
+
"Find functions, classes, components, hooks, types by NAME or SIGNATURE. Searches symbol declarations, NOT code behavior or content. Use search_content (grep) instead for pattern matching inside code (e.g., security audits, string searches, imports).",
|
|
305
305
|
{
|
|
306
|
-
query: z.string().describe("
|
|
306
|
+
query: z.string().describe("Symbol name to search for (e.g., 'auth', 'login', 'UserService'). Matches function/class/type names, not code content."),
|
|
307
307
|
path: z.string().optional().describe("Search root (default: cwd)"),
|
|
308
308
|
kinds: z.array(z.enum(["function", "class", "interface", "type", "variable", "export", "import", "component", "hook"])).optional().describe("Filter by symbol kind"),
|
|
309
309
|
exportedOnly: z.boolean().optional().describe("Only show exported symbols (default: false)"),
|
package/src/search/semantic.ts
CHANGED
|
@@ -172,14 +172,37 @@ function extractSymbols(filePath: string): CodeSymbol[] {
|
|
|
172
172
|
continue;
|
|
173
173
|
}
|
|
174
174
|
|
|
175
|
-
// Classes
|
|
175
|
+
// Classes — also extract methods inside the class body
|
|
176
176
|
const classMatch = line.match(/(?:export\s+)?class\s+(\w+)(?:\s+extends\s+(\w+))?/);
|
|
177
177
|
if (classMatch) {
|
|
178
|
+
const className = classMatch[1];
|
|
178
179
|
symbols.push({
|
|
179
|
-
name:
|
|
180
|
+
name: className, kind: "class", file, line: lineNum,
|
|
180
181
|
signature: line.trim().replace(/\{.*$/, "").trim(),
|
|
181
182
|
exported: isExported,
|
|
182
183
|
});
|
|
184
|
+
// Walk class body to find methods
|
|
185
|
+
let braceDepth = 0;
|
|
186
|
+
for (let j = i; j < lines.length; j++) {
|
|
187
|
+
for (const ch of lines[j]) {
|
|
188
|
+
if (ch === "{") braceDepth++;
|
|
189
|
+
if (ch === "}") braceDepth--;
|
|
190
|
+
}
|
|
191
|
+
if (j > i) {
|
|
192
|
+
const memberLine = lines[j];
|
|
193
|
+
// Class methods: async methodName(...), methodName(...), get/set name(...)
|
|
194
|
+
const methodMatch = memberLine.match(/^\s+(?:async\s+)?(?:(?:public|private|protected|static|readonly|override|abstract)\s+)*(?:get\s+|set\s+)?(\w+)\s*[\(<]/);
|
|
195
|
+
const RESERVED = new Set(["if", "for", "while", "switch", "catch", "return", "throw", "new", "delete", "typeof", "void", "yield", "await", "try", "else"]);
|
|
196
|
+
if (methodMatch && !RESERVED.has(methodMatch[1])) {
|
|
197
|
+
symbols.push({
|
|
198
|
+
name: `${className}.${methodMatch[1]}`, kind: "function", file, line: j + 1,
|
|
199
|
+
signature: memberLine.trim().replace(/\{.*$/, "").trim(),
|
|
200
|
+
exported: isExported,
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
if (braceDepth === 0 && j > i) break; // end of class
|
|
205
|
+
}
|
|
183
206
|
continue;
|
|
184
207
|
}
|
|
185
208
|
|