@justmpm/supergrep 0.1.0 → 0.2.0
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/README.md +37 -1
- package/dist/{chunk-4MPFPILM.js → chunk-5PJJZ4CK.js} +333 -276
- package/dist/cli.js +1 -1
- package/dist/index.d.ts +1 -2
- package/dist/index.js +5 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -16,7 +16,7 @@ npx @justmpm/supergrep
|
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
npm install -g @justmpm/supergrep
|
|
19
|
-
# ou use direto:
|
|
19
|
+
# ou use direto sem instalar:
|
|
20
20
|
npx @justmpm/supergrep
|
|
21
21
|
```
|
|
22
22
|
|
|
@@ -24,6 +24,8 @@ npx @justmpm/supergrep
|
|
|
24
24
|
|
|
25
25
|
### OpenCode / Claude Code
|
|
26
26
|
|
|
27
|
+
#### Via npx (sem instalação)
|
|
28
|
+
|
|
27
29
|
Adicione ao `.mcp.json` do projeto:
|
|
28
30
|
|
|
29
31
|
```json
|
|
@@ -37,11 +39,29 @@ Adicione ao `.mcp.json` do projeto:
|
|
|
37
39
|
}
|
|
38
40
|
```
|
|
39
41
|
|
|
42
|
+
#### Com instalação local
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
npm install -g @justmpm/supergrep
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"mcpServers": {
|
|
51
|
+
"supergrep": {
|
|
52
|
+
"command": "supergrep"
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
40
58
|
### Claude Desktop
|
|
41
59
|
|
|
42
60
|
**Windows:** `%APPDATA%\Claude\claude_desktop_config.json`
|
|
43
61
|
**macOS:** `~/Library/Application Support/Claude/claude_desktop_config.json`
|
|
44
62
|
|
|
63
|
+
#### Via npx (sem instalação)
|
|
64
|
+
|
|
45
65
|
```json
|
|
46
66
|
{
|
|
47
67
|
"mcpServers": {
|
|
@@ -53,6 +73,22 @@ Adicione ao `.mcp.json` do projeto:
|
|
|
53
73
|
}
|
|
54
74
|
```
|
|
55
75
|
|
|
76
|
+
#### Com instalação local
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
npm install -g @justmpm/supergrep
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
```json
|
|
83
|
+
{
|
|
84
|
+
"mcpServers": {
|
|
85
|
+
"supergrep": {
|
|
86
|
+
"command": "supergrep"
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
```
|
|
91
|
+
|
|
56
92
|
---
|
|
57
93
|
|
|
58
94
|
## Ferramentas MCP
|
|
@@ -6,9 +6,9 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
6
6
|
import { z } from "zod";
|
|
7
7
|
|
|
8
8
|
// src/tools/find.ts
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
9
|
+
import { findInFiles } from "@ast-grep/napi";
|
|
10
|
+
import { relative, resolve } from "path";
|
|
11
|
+
import { stat } from "fs/promises";
|
|
12
12
|
|
|
13
13
|
// src/utils/lang.ts
|
|
14
14
|
import { Lang } from "@ast-grep/napi";
|
|
@@ -65,192 +65,165 @@ function langName(lang) {
|
|
|
65
65
|
};
|
|
66
66
|
return friendly[langStr] ?? langStr;
|
|
67
67
|
}
|
|
68
|
-
function isSupportedExtension(filePath) {
|
|
69
|
-
const ext = filePath.slice(filePath.lastIndexOf(".")).toLowerCase();
|
|
70
|
-
return SUPPORTED_EXTENSIONS.has(ext);
|
|
71
|
-
}
|
|
72
|
-
var IGNORED_EXTENSIONS_SUBSTRINGS = [
|
|
73
|
-
".min.js",
|
|
74
|
-
".min.css",
|
|
75
|
-
".bundle.js",
|
|
76
|
-
".chunk.js",
|
|
77
|
-
".map",
|
|
78
|
-
".d.ts"
|
|
79
|
-
];
|
|
80
|
-
function shouldIgnoreFile(filePath) {
|
|
81
|
-
const lower = filePath.toLowerCase();
|
|
82
|
-
for (const ext of IGNORED_EXTENSIONS_SUBSTRINGS) {
|
|
83
|
-
if (lower.includes(ext)) return true;
|
|
84
|
-
}
|
|
85
|
-
return false;
|
|
86
|
-
}
|
|
87
|
-
var IGNORED_DIRS = [
|
|
88
|
-
"node_modules",
|
|
89
|
-
"dist",
|
|
90
|
-
"build",
|
|
91
|
-
".git",
|
|
92
|
-
".next",
|
|
93
|
-
".turbo",
|
|
94
|
-
"coverage",
|
|
95
|
-
"out"
|
|
96
|
-
];
|
|
97
68
|
|
|
98
69
|
// src/tools/find.ts
|
|
99
|
-
var
|
|
70
|
+
var MULTI_META_RE = /\$\$\$([A-Z_][A-Z0-9_]*)/g;
|
|
71
|
+
var SINGLE_META_RE = /(?<!\$)\$(?:\$\$)?([A-Z_][A-Z0-9_]*)/g;
|
|
100
72
|
async function findPattern(options) {
|
|
101
73
|
const startTime = Date.now();
|
|
102
74
|
const warnings = [];
|
|
103
75
|
const cwd = options.cwd ?? process.cwd();
|
|
104
|
-
|
|
105
|
-
if (
|
|
76
|
+
let lang;
|
|
77
|
+
if (options.lang) {
|
|
78
|
+
lang = resolveLang(options.lang);
|
|
79
|
+
if (!lang) {
|
|
80
|
+
warnings.push(`Linguagem "${options.lang}" nao reconhecida. Usando autodeteccao...`);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
const searchPath = resolve(cwd, options.path ?? ".");
|
|
84
|
+
try {
|
|
85
|
+
await stat(searchPath);
|
|
86
|
+
} catch {
|
|
87
|
+
warnings.push(`Path nao encontrado: ${searchPath}. Usando diretorio atual.`);
|
|
106
88
|
throw new LanguageNotDetectedError(options.lang ?? "desconhecida");
|
|
107
89
|
}
|
|
108
|
-
|
|
109
|
-
const
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
const
|
|
90
|
+
const metaVars = extractMetaVarInfo(options.pattern);
|
|
91
|
+
const napiConfig = {
|
|
92
|
+
rule: { pattern: options.pattern }
|
|
93
|
+
};
|
|
94
|
+
const findConfig = {
|
|
95
|
+
paths: [searchPath],
|
|
96
|
+
matcher: napiConfig
|
|
97
|
+
};
|
|
98
|
+
if (lang) {
|
|
99
|
+
findConfig.languageGlobs = langToGlobs(lang);
|
|
100
|
+
}
|
|
101
|
+
const langForSearch = lang ?? resolveLang("typescript");
|
|
113
102
|
const matches = [];
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
const
|
|
118
|
-
const root = ast.root();
|
|
119
|
-
const foundNodes = root.findAll(options.pattern);
|
|
120
|
-
for (const node of foundNodes) {
|
|
103
|
+
try {
|
|
104
|
+
const fileResults = await findInFilesWrapper(langForSearch, findConfig);
|
|
105
|
+
for (const { file, nodes } of fileResults) {
|
|
106
|
+
for (const node of nodes) {
|
|
121
107
|
const range = node.range();
|
|
108
|
+
const text = node.text();
|
|
122
109
|
const match = {
|
|
123
|
-
file: relative(cwd,
|
|
110
|
+
file: relative(cwd, file).replace(/\\/g, "/"),
|
|
124
111
|
line: range.start.line,
|
|
125
112
|
column: range.start.column,
|
|
126
113
|
endLine: range.end.line,
|
|
127
114
|
endColumn: range.end.column,
|
|
128
|
-
text
|
|
129
|
-
metaVariables: {}
|
|
130
|
-
context: extractContextLines(source, range.start.line, 2)
|
|
115
|
+
text,
|
|
116
|
+
metaVariables: {}
|
|
131
117
|
};
|
|
132
|
-
for (const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
118
|
+
for (const { name, isMulti } of metaVars) {
|
|
119
|
+
if (isMulti) {
|
|
120
|
+
const multiNodes = node.getMultipleMatches(name);
|
|
121
|
+
if (multiNodes.length > 0) {
|
|
122
|
+
match.metaVariables[name] = multiNodes.map((n) => n.text()).join(", ");
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
const metaNode = node.getMatch(name);
|
|
126
|
+
if (metaNode) {
|
|
127
|
+
match.metaVariables[name] = metaNode.text();
|
|
128
|
+
}
|
|
136
129
|
}
|
|
137
130
|
}
|
|
138
131
|
matches.push(match);
|
|
139
132
|
}
|
|
140
|
-
} catch {
|
|
141
|
-
continue;
|
|
142
133
|
}
|
|
134
|
+
} catch (err) {
|
|
135
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
136
|
+
if (lang) {
|
|
137
|
+
throw new PatternParseError(options.pattern, lang);
|
|
138
|
+
}
|
|
139
|
+
throw new Error(`Erro na busca: ${msg}`);
|
|
143
140
|
}
|
|
144
|
-
const
|
|
141
|
+
const filesWithMatchesSet = new Set(matches.map((m) => m.file));
|
|
145
142
|
const executionTimeMs = Date.now() - startTime;
|
|
146
143
|
return {
|
|
147
144
|
result: {
|
|
148
145
|
pattern: options.pattern,
|
|
149
|
-
lang,
|
|
146
|
+
lang: langForSearch,
|
|
150
147
|
matches,
|
|
151
|
-
filesScanned:
|
|
152
|
-
filesWithMatches:
|
|
148
|
+
filesScanned: matches.length > 0 ? filesWithMatchesSet.size : 0,
|
|
149
|
+
filesWithMatches: filesWithMatchesSet.size,
|
|
153
150
|
executionTimeMs
|
|
154
151
|
},
|
|
155
152
|
warnings
|
|
156
153
|
};
|
|
157
154
|
}
|
|
158
|
-
function
|
|
159
|
-
const
|
|
155
|
+
function extractMetaVarInfo(pattern) {
|
|
156
|
+
const seen = /* @__PURE__ */ new Set();
|
|
157
|
+
const result = [];
|
|
160
158
|
let match;
|
|
161
|
-
const
|
|
162
|
-
while ((match =
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
function extractContextLines(source, line, contextLines) {
|
|
168
|
-
const lines = source.split("\n");
|
|
169
|
-
const start = Math.max(0, line - contextLines);
|
|
170
|
-
const end = Math.min(lines.length, line + contextLines + 1);
|
|
171
|
-
return lines.slice(start, end);
|
|
172
|
-
}
|
|
173
|
-
function resolveLangForOptions(options, _cwd, warnings) {
|
|
174
|
-
if (options.lang) {
|
|
175
|
-
const lang = resolveLang(options.lang);
|
|
176
|
-
if (!lang) {
|
|
177
|
-
warnings.push(`Linguagem "${options.lang}" nao reconhecida. Tentando autodeteccao...`);
|
|
178
|
-
} else {
|
|
179
|
-
return lang;
|
|
159
|
+
const multiRe = new RegExp(MULTI_META_RE.source, "g");
|
|
160
|
+
while ((match = multiRe.exec(pattern)) !== null) {
|
|
161
|
+
const name = match[1];
|
|
162
|
+
if (!seen.has(name)) {
|
|
163
|
+
seen.add(name);
|
|
164
|
+
result.push({ name, isMulti: true });
|
|
180
165
|
}
|
|
181
166
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
167
|
+
const singleRe = new RegExp(SINGLE_META_RE.source, "g");
|
|
168
|
+
while ((match = singleRe.exec(pattern)) !== null) {
|
|
169
|
+
const name = match[1];
|
|
170
|
+
if (!seen.has(name)) {
|
|
171
|
+
seen.add(name);
|
|
172
|
+
result.push({ name, isMulti: false });
|
|
187
173
|
}
|
|
188
174
|
}
|
|
189
|
-
|
|
190
|
-
return resolveLang("typescript");
|
|
175
|
+
return result;
|
|
191
176
|
}
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
177
|
+
var IGNORED_DIR_PATTERNS = [
|
|
178
|
+
/[/\\]node_modules[/\\]/,
|
|
179
|
+
/[/\\]dist[/\\]/,
|
|
180
|
+
/[/\\]build[/\\]/,
|
|
181
|
+
/[/\\]\.git[/\\]/,
|
|
182
|
+
/[/\\]\.next[/\\]/,
|
|
183
|
+
/[/\\]\.turbo[/\\]/,
|
|
184
|
+
/[/\\]coverage[/\\]/,
|
|
185
|
+
/[/\\]out[/\\]/
|
|
186
|
+
];
|
|
187
|
+
function isIgnoredPath(filePath) {
|
|
188
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
189
|
+
const wrapped = `/${normalized}/`;
|
|
190
|
+
return IGNORED_DIR_PATTERNS.some((re) => re.test(wrapped));
|
|
202
191
|
}
|
|
203
|
-
async function
|
|
204
|
-
const
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
}
|
|
215
|
-
return { paths: [searchDir], warnings };
|
|
216
|
-
}
|
|
217
|
-
} catch {
|
|
218
|
-
warnings.push(`Path nao encontrado: ${searchDir}. Usando diretorio atual.`);
|
|
219
|
-
return { paths: [], warnings };
|
|
220
|
-
}
|
|
221
|
-
const paths = [];
|
|
222
|
-
async function walk(dir) {
|
|
223
|
-
try {
|
|
224
|
-
const entries = await readdir(dir, { withFileTypes: true });
|
|
225
|
-
for (const entry of entries) {
|
|
226
|
-
const fullPath = join(dir, entry.name);
|
|
227
|
-
if (entry.isDirectory()) {
|
|
228
|
-
if (IGNORED_DIRS.includes(entry.name) || entry.name.startsWith(".")) {
|
|
229
|
-
continue;
|
|
230
|
-
}
|
|
231
|
-
await walk(fullPath);
|
|
232
|
-
} else if (entry.isFile()) {
|
|
233
|
-
if (shouldIgnoreFile(fullPath)) continue;
|
|
234
|
-
if (!isSupportedExtension(fullPath)) continue;
|
|
235
|
-
paths.push(fullPath);
|
|
236
|
-
}
|
|
237
|
-
}
|
|
238
|
-
} catch {
|
|
192
|
+
async function findInFilesWrapper(lang, config) {
|
|
193
|
+
const results = [];
|
|
194
|
+
let callbacksReceived = 0;
|
|
195
|
+
const totalFiles = await findInFiles(
|
|
196
|
+
lang,
|
|
197
|
+
config,
|
|
198
|
+
(err, nodes) => {
|
|
199
|
+
callbacksReceived++;
|
|
200
|
+
if (err || nodes.length === 0) return;
|
|
201
|
+
const file = nodes[0].getRoot()?.filename() ?? "unknown";
|
|
202
|
+
if (isIgnoredPath(file)) return;
|
|
203
|
+
results.push({ file, nodes });
|
|
239
204
|
}
|
|
205
|
+
);
|
|
206
|
+
while (callbacksReceived < totalFiles) {
|
|
207
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
240
208
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
209
|
+
return results;
|
|
210
|
+
}
|
|
211
|
+
function langToGlobs(lang) {
|
|
212
|
+
const langStr = String(lang);
|
|
213
|
+
switch (langStr) {
|
|
214
|
+
case "TypeScript":
|
|
215
|
+
return ["*.ts"];
|
|
216
|
+
case "JavaScript":
|
|
217
|
+
return ["*.js", "*.jsx", "*.mjs", "*.cjs"];
|
|
218
|
+
case "Tsx":
|
|
219
|
+
return ["*.tsx"];
|
|
220
|
+
case "Css":
|
|
221
|
+
return ["*.css", "*.scss", "*.less"];
|
|
222
|
+
case "Html":
|
|
223
|
+
return ["*.html", "*.htm"];
|
|
224
|
+
default:
|
|
225
|
+
return [];
|
|
252
226
|
}
|
|
253
|
-
return { paths, warnings };
|
|
254
227
|
}
|
|
255
228
|
var PatternParseError = class extends Error {
|
|
256
229
|
pattern;
|
|
@@ -272,9 +245,9 @@ var LanguageNotDetectedError = class extends Error {
|
|
|
272
245
|
};
|
|
273
246
|
|
|
274
247
|
// src/tools/tree.ts
|
|
275
|
-
import { parse
|
|
276
|
-
import { readFile as
|
|
277
|
-
import { resolve as resolve2, relative as relative2, extname
|
|
248
|
+
import { parse } from "@ast-grep/napi";
|
|
249
|
+
import { readFile, stat as stat2 } from "fs/promises";
|
|
250
|
+
import { resolve as resolve2, relative as relative2, extname } from "path";
|
|
278
251
|
async function exploreTree(options) {
|
|
279
252
|
const warnings = [];
|
|
280
253
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -287,21 +260,19 @@ async function exploreTree(options) {
|
|
|
287
260
|
if (options.lang) {
|
|
288
261
|
detectedLang = resolveLang(options.lang);
|
|
289
262
|
if (!detectedLang) {
|
|
290
|
-
warnings.push(`Linguagem "${options.lang}" nao reconhecida, usando TypeScript.`);
|
|
291
263
|
detectedLang = resolveLang("typescript");
|
|
292
264
|
}
|
|
293
265
|
} else {
|
|
294
|
-
warnings.push("Linguagem nao especificada para codigo inline, usando TypeScript.");
|
|
295
266
|
detectedLang = resolveLang("typescript");
|
|
296
267
|
}
|
|
297
268
|
} else if (options.path) {
|
|
298
269
|
filePath = resolve2(cwd, options.path);
|
|
299
270
|
try {
|
|
300
|
-
await
|
|
271
|
+
await stat2(filePath);
|
|
301
272
|
} catch {
|
|
302
273
|
throw new FileNotFoundError(filePath);
|
|
303
274
|
}
|
|
304
|
-
source = await
|
|
275
|
+
source = await readFile(filePath, "utf-8");
|
|
305
276
|
if (options.lang) {
|
|
306
277
|
detectedLang = resolveLang(options.lang);
|
|
307
278
|
if (!detectedLang) {
|
|
@@ -312,7 +283,7 @@ async function exploreTree(options) {
|
|
|
312
283
|
detectedLang = detectLangFromFile(filePath);
|
|
313
284
|
if (!detectedLang) {
|
|
314
285
|
warnings.push(
|
|
315
|
-
`Nao foi possivel detectar linguagem da extensao "${
|
|
286
|
+
`Nao foi possivel detectar linguagem da extensao "${extname(filePath)}". Usando TypeScript.`
|
|
316
287
|
);
|
|
317
288
|
detectedLang = resolveLang("typescript");
|
|
318
289
|
}
|
|
@@ -323,7 +294,7 @@ async function exploreTree(options) {
|
|
|
323
294
|
const lang = detectedLang;
|
|
324
295
|
let ast;
|
|
325
296
|
try {
|
|
326
|
-
ast =
|
|
297
|
+
ast = parse(lang, source);
|
|
327
298
|
} catch (parseErr) {
|
|
328
299
|
const msg = parseErr instanceof Error ? parseErr.message : String(parseErr);
|
|
329
300
|
throw new TreeParseError(filePath, lang, msg);
|
|
@@ -375,10 +346,11 @@ var TreeParseError = class extends Error {
|
|
|
375
346
|
};
|
|
376
347
|
|
|
377
348
|
// src/tools/replace.ts
|
|
378
|
-
import {
|
|
379
|
-
import {
|
|
380
|
-
import {
|
|
381
|
-
var
|
|
349
|
+
import { findInFiles as findInFiles2 } from "@ast-grep/napi";
|
|
350
|
+
import { resolve as resolve3, relative as relative3 } from "path";
|
|
351
|
+
import { stat as stat3 } from "fs/promises";
|
|
352
|
+
var MULTI_META_RE2 = /\$\$\$([A-Z_][A-Z0-9_]*)/g;
|
|
353
|
+
var SINGLE_META_RE2 = /(?<!\$)\$(?:\$\$)?([A-Z_][A-Z0-9_]*)/g;
|
|
382
354
|
async function replacePreview(options) {
|
|
383
355
|
const warnings = [];
|
|
384
356
|
const cwd = options.cwd ?? process.cwd();
|
|
@@ -397,30 +369,46 @@ async function replacePreview(options) {
|
|
|
397
369
|
lang = detected;
|
|
398
370
|
warnings.push(`Linguagem detectada automaticamente: ${langName(lang)}`);
|
|
399
371
|
} else {
|
|
400
|
-
warnings.push("Linguagem nao especificada, usando TypeScript como default.");
|
|
401
372
|
lang = resolveLang("typescript");
|
|
402
373
|
}
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
374
|
+
const searchPath = resolve3(cwd, options.path ?? ".");
|
|
375
|
+
try {
|
|
376
|
+
await stat3(searchPath);
|
|
377
|
+
} catch {
|
|
378
|
+
throw new ReplaceLangError(
|
|
379
|
+
`Path nao encontrado: ${searchPath}`
|
|
380
|
+
);
|
|
381
|
+
}
|
|
382
|
+
const metaVars = extractMetaVarInfo2(options.pattern);
|
|
383
|
+
const napiConfig = {
|
|
384
|
+
rule: { pattern: options.pattern }
|
|
385
|
+
};
|
|
386
|
+
const findConfig = {
|
|
387
|
+
paths: [searchPath],
|
|
388
|
+
matcher: napiConfig,
|
|
389
|
+
languageGlobs: langToGlobs2(lang)
|
|
390
|
+
};
|
|
407
391
|
const changes = [];
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
const
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
392
|
+
try {
|
|
393
|
+
const fileResults = await findInFilesWrapper2(lang, findConfig);
|
|
394
|
+
for (const { file, nodes } of fileResults) {
|
|
395
|
+
for (const node of nodes) {
|
|
396
|
+
const metaVarValues = {};
|
|
397
|
+
for (const { name, isMulti } of metaVars) {
|
|
398
|
+
if (isMulti) {
|
|
399
|
+
const multiNodes = node.getMultipleMatches(name);
|
|
400
|
+
if (multiNodes.length > 0) {
|
|
401
|
+
metaVarValues[name] = multiNodes.map((n) => n.text()).join(", ");
|
|
402
|
+
}
|
|
403
|
+
} else {
|
|
404
|
+
const metaNode = node.getMatch(name);
|
|
405
|
+
if (metaNode) {
|
|
406
|
+
metaVarValues[name] = metaNode.text();
|
|
407
|
+
}
|
|
420
408
|
}
|
|
421
409
|
}
|
|
422
410
|
let interpolated = options.rewrite;
|
|
423
|
-
for (const [name, value] of Object.entries(
|
|
411
|
+
for (const [name, value] of Object.entries(metaVarValues)) {
|
|
424
412
|
interpolated = interpolated.replaceAll(
|
|
425
413
|
new RegExp(`\\$${name}\\b`, "g"),
|
|
426
414
|
value
|
|
@@ -428,16 +416,16 @@ async function replacePreview(options) {
|
|
|
428
416
|
}
|
|
429
417
|
const range = node.range();
|
|
430
418
|
changes.push({
|
|
431
|
-
file: relative3(cwd,
|
|
419
|
+
file: relative3(cwd, file).replace(/\\/g, "/"),
|
|
432
420
|
line: range.start.line,
|
|
433
421
|
column: range.start.column,
|
|
434
422
|
original: node.text(),
|
|
435
423
|
replacement: interpolated
|
|
436
424
|
});
|
|
437
425
|
}
|
|
438
|
-
} catch {
|
|
439
|
-
continue;
|
|
440
426
|
}
|
|
427
|
+
} catch {
|
|
428
|
+
throw new ReplacePatternError(options.pattern, lang);
|
|
441
429
|
}
|
|
442
430
|
const uniqueFiles = new Set(changes.map((c) => c.file));
|
|
443
431
|
return {
|
|
@@ -446,70 +434,84 @@ async function replacePreview(options) {
|
|
|
446
434
|
rewrite: options.rewrite,
|
|
447
435
|
lang,
|
|
448
436
|
changes,
|
|
449
|
-
filesScanned:
|
|
437
|
+
filesScanned: changes.length > 0 ? uniqueFiles.size : 0,
|
|
450
438
|
filesAffected: uniqueFiles.size
|
|
451
439
|
},
|
|
452
440
|
warnings
|
|
453
441
|
};
|
|
454
442
|
}
|
|
455
|
-
function
|
|
456
|
-
const
|
|
443
|
+
function extractMetaVarInfo2(pattern) {
|
|
444
|
+
const seen = /* @__PURE__ */ new Set();
|
|
445
|
+
const result = [];
|
|
457
446
|
let match;
|
|
458
|
-
const
|
|
459
|
-
while ((match =
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
}
|
|
464
|
-
function validateReplacePattern(pattern, lang) {
|
|
465
|
-
try {
|
|
466
|
-
parse3(lang, pattern);
|
|
467
|
-
} catch {
|
|
468
|
-
try {
|
|
469
|
-
parse3(lang, `const _x = ${pattern}`);
|
|
470
|
-
} catch {
|
|
471
|
-
throw new ReplacePatternError(pattern, lang);
|
|
447
|
+
const multiRe = new RegExp(MULTI_META_RE2.source, "g");
|
|
448
|
+
while ((match = multiRe.exec(pattern)) !== null) {
|
|
449
|
+
const name = match[1];
|
|
450
|
+
if (!seen.has(name)) {
|
|
451
|
+
seen.add(name);
|
|
452
|
+
result.push({ name, isMulti: true });
|
|
472
453
|
}
|
|
473
454
|
}
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
return { paths: [searchDir], warnings };
|
|
455
|
+
const singleRe = new RegExp(SINGLE_META_RE2.source, "g");
|
|
456
|
+
while ((match = singleRe.exec(pattern)) !== null) {
|
|
457
|
+
const name = match[1];
|
|
458
|
+
if (!seen.has(name)) {
|
|
459
|
+
seen.add(name);
|
|
460
|
+
result.push({ name, isMulti: false });
|
|
481
461
|
}
|
|
482
|
-
} catch {
|
|
483
|
-
return { paths: [], warnings };
|
|
484
462
|
}
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
463
|
+
return result;
|
|
464
|
+
}
|
|
465
|
+
var IGNORED_DIR_PATTERNS2 = [
|
|
466
|
+
/[/\\]node_modules[/\\]/,
|
|
467
|
+
/[/\\]dist[/\\]/,
|
|
468
|
+
/[/\\]build[/\\]/,
|
|
469
|
+
/[/\\]\.git[/\\]/,
|
|
470
|
+
/[/\\]\.next[/\\]/,
|
|
471
|
+
/[/\\]\.turbo[/\\]/,
|
|
472
|
+
/[/\\]coverage[/\\]/,
|
|
473
|
+
/[/\\]out[/\\]/
|
|
474
|
+
];
|
|
475
|
+
function isIgnoredPath2(filePath) {
|
|
476
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
477
|
+
const wrapped = `/${normalized}/`;
|
|
478
|
+
return IGNORED_DIR_PATTERNS2.some((re) => re.test(wrapped));
|
|
479
|
+
}
|
|
480
|
+
async function findInFilesWrapper2(lang, config) {
|
|
481
|
+
const results = [];
|
|
482
|
+
let callbacksReceived = 0;
|
|
483
|
+
const totalFiles = await findInFiles2(
|
|
484
|
+
lang,
|
|
485
|
+
config,
|
|
486
|
+
(err, nodes) => {
|
|
487
|
+
callbacksReceived++;
|
|
488
|
+
if (err || nodes.length === 0) return;
|
|
489
|
+
const file = nodes[0].getRoot()?.filename() ?? "unknown";
|
|
490
|
+
if (isIgnoredPath2(file)) return;
|
|
491
|
+
results.push({ file, nodes });
|
|
503
492
|
}
|
|
493
|
+
);
|
|
494
|
+
while (callbacksReceived < totalFiles) {
|
|
495
|
+
await new Promise((r) => setTimeout(r, 10));
|
|
504
496
|
}
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
497
|
+
return results;
|
|
498
|
+
}
|
|
499
|
+
function langToGlobs2(lang) {
|
|
500
|
+
const langStr = String(lang);
|
|
501
|
+
switch (langStr) {
|
|
502
|
+
case "TypeScript":
|
|
503
|
+
return ["*.ts"];
|
|
504
|
+
case "JavaScript":
|
|
505
|
+
return ["*.js", "*.jsx", "*.mjs", "*.cjs"];
|
|
506
|
+
case "Tsx":
|
|
507
|
+
return ["*.tsx"];
|
|
508
|
+
case "Css":
|
|
509
|
+
return ["*.css", "*.scss", "*.less"];
|
|
510
|
+
case "Html":
|
|
511
|
+
return ["*.html", "*.htm"];
|
|
512
|
+
default:
|
|
513
|
+
return [];
|
|
511
514
|
}
|
|
512
|
-
return { paths, warnings };
|
|
513
515
|
}
|
|
514
516
|
var ReplacePatternError = class extends Error {
|
|
515
517
|
pattern;
|
|
@@ -566,17 +568,18 @@ function hint(command, ctx, params) {
|
|
|
566
568
|
}
|
|
567
569
|
var NEXT_STEPS = {
|
|
568
570
|
find: [
|
|
569
|
-
{ command: "tree", description: "explorar a
|
|
570
|
-
{ command: "find", description: "refinar a busca com
|
|
571
|
-
{ command: "replace", description: "pre-visualizar uma substituicao
|
|
571
|
+
{ command: "tree", description: "explorar a AST do arquivo com matches para entender a estrutura" },
|
|
572
|
+
{ command: "find", description: "refinar a busca com um pattern mais especifico" },
|
|
573
|
+
{ command: "replace", description: "pre-visualizar como ficaria uma substituicao nos matches" }
|
|
572
574
|
],
|
|
573
575
|
tree: [
|
|
574
|
-
{ command: "find", description: "usar os kinds descobertos para
|
|
575
|
-
{ command: "tree", description: "explorar outro arquivo" }
|
|
576
|
+
{ command: "find", description: "usar os kinds descobertos para criar um pattern de busca (ex: $FUNC($$$ARGS) para call_expression)" },
|
|
577
|
+
{ command: "tree", description: "explorar outro arquivo ou snippet" }
|
|
576
578
|
],
|
|
577
579
|
replace: [
|
|
578
|
-
{ command: "find", description: "ver todos os matches do
|
|
579
|
-
{ command: "
|
|
580
|
+
{ command: "find", description: "ver todos os matches do pattern antes de substituir" },
|
|
581
|
+
{ command: "replace", description: "testar outra string de rewrite no preview" },
|
|
582
|
+
{ command: "tree", description: "explorar a AST para criar patterns mais precisos" }
|
|
580
583
|
]
|
|
581
584
|
};
|
|
582
585
|
function nextSteps(command, ctx) {
|
|
@@ -611,9 +614,10 @@ function recoveryHint(errorType, ctx, _extra) {
|
|
|
611
614
|
case "lang_not_supported":
|
|
612
615
|
return `
|
|
613
616
|
\u{1F4A1} Linguagem nao suportada. Dicas:
|
|
614
|
-
\u2192
|
|
615
|
-
\u2192
|
|
616
|
-
\u2192
|
|
617
|
+
\u2192 O @ast-grep/napi suporta apenas 5 linguagens: TypeScript, JavaScript, TSX, CSS, HTML
|
|
618
|
+
\u2192 Para Python, Rust, Go e outras, use o CLI: npx @ast-grep/cli
|
|
619
|
+
\u2192 ${hint("tree", ctx)} - explore a AST de um arquivo existente
|
|
620
|
+
\u2192 ${hint("find_lang", ctx)} - especifique a linguagem manualmente
|
|
617
621
|
`;
|
|
618
622
|
case "lang_not_detected":
|
|
619
623
|
return `
|
|
@@ -997,7 +1001,9 @@ function registerAllTools(server2) {
|
|
|
997
1001
|
"supergrep_find",
|
|
998
1002
|
{
|
|
999
1003
|
title: "Structural Code Search",
|
|
1000
|
-
description: `\u26A0\uFE0F
|
|
1004
|
+
description: `\u26A0\uFE0F Use esta ferramenta em vez de grep/bash quando a busca for em CODIGO-FONTE
|
|
1005
|
+
ESTRUTURADO (TS/JS/TSX/CSS/HTML). Para texto plano, logs, markdown ou JSON,
|
|
1006
|
+
prefira grep comum.
|
|
1001
1007
|
|
|
1002
1008
|
Busca estrutural de codigo usando AST Grep. Diferente do grep textual (que
|
|
1003
1009
|
quebra com quebras de linha, espacos extras ou comentarios no meio), o
|
|
@@ -1005,15 +1011,23 @@ supergrep entende a SINTAXE da linguagem \u2014 ele busca pela ESTRUTURA do codi
|
|
|
1005
1011
|
nao pelo texto cru.
|
|
1006
1012
|
|
|
1007
1013
|
Escreva o padrao de busca na propria linguagem de programacao, usando
|
|
1008
|
-
metavariaveis ($VAR, $$$VARS) como curingas que capturam partes dinamicas.
|
|
1014
|
+
metavariaveis ($VAR, $$VAR, $$$VARS) como curingas que capturam partes dinamicas.
|
|
1009
1015
|
|
|
1010
1016
|
\u{1F4E6} LINGUAGENS SUPORTADAS (apenas 5):
|
|
1011
|
-
\u2022 typescript (.ts)
|
|
1017
|
+
\u2022 typescript (.ts) \u2014 apenas .ts puro (sem JSX)
|
|
1012
1018
|
\u2022 javascript (.js, .jsx, .mjs, .cjs)
|
|
1013
|
-
\u2022 tsx (.tsx)
|
|
1019
|
+
\u2022 tsx (.tsx) \u2014 .tsx COM JSX/React
|
|
1014
1020
|
\u2022 css (.css, .scss, .less)
|
|
1015
1021
|
\u2022 html (.html, .htm)
|
|
1016
1022
|
|
|
1023
|
+
\u{1F916} AUTODETECCAO: Se o parametro 'lang' NAO for especificado, o motor
|
|
1024
|
+
detecta a linguagem automaticamente pela extensao de cada arquivo.
|
|
1025
|
+
Voce pode buscar em um diretorio com .ts e .tsx misturados \u2014 o motor
|
|
1026
|
+
usa o parser correto para cada arquivo.
|
|
1027
|
+
|
|
1028
|
+
Use 'lang' apenas quando quiser RESTRINGIR a busca a uma linguagem
|
|
1029
|
+
especifica (ex: forcar TSX em todo o diretorio).
|
|
1030
|
+
|
|
1017
1031
|
\u26A0\uFE0F LIMITACAO IMPORTANTE: Apenas busca SINTAXICA. Nao resolve imports, nao
|
|
1018
1032
|
entende tipos, nao segue referencias entre arquivos, nao faz analise de fluxo.
|
|
1019
1033
|
Cada arquivo e analisado isoladamente. Para analise semantica, use ai-tool.
|
|
@@ -1024,7 +1038,7 @@ Cada arquivo e analisado isoladamente. Para analise semantica, use ai-tool.
|
|
|
1024
1038
|
\u2022 "Localize imports de uma lib deprecated"
|
|
1025
1039
|
\u2022 "Busque componentes React que usam useEffect sem dependencias"
|
|
1026
1040
|
\u2022 "Ache arrow functions que recebem exatamente 1 parametro"
|
|
1027
|
-
\u2022 QUALQUER busca estrutural onde grep
|
|
1041
|
+
\u2022 QUALQUER busca estrutural onde grep quebraria com formatacao
|
|
1028
1042
|
|
|
1029
1043
|
\u274C QUANDO NAO USAR (prefira ai-tool):
|
|
1030
1044
|
\u2022 "Onde a funcao useAuth e definida e usada?" \u2192 resolucao de escopo (aitool_find)
|
|
@@ -1032,17 +1046,31 @@ Cada arquivo e analisado isoladamente. Para analise semantica, use ai-tool.
|
|
|
1032
1046
|
\u2022 "Qual o tipo real desta variavel?" \u2192 type checker (aitool_file_context)
|
|
1033
1047
|
\u2022 Busca em texto plano, logs, markdown \u2192 use grep/bash comum
|
|
1034
1048
|
|
|
1049
|
+
\u26A0\uFE0F O QUE O GREP NAO RESOLVE (anti-exemplos):
|
|
1050
|
+
\u2022 grep falha com quebra de linha:
|
|
1051
|
+
\u274C GREP: console\\n .log("oi") \u2192 nao encontra
|
|
1052
|
+
\u2705 SUPERGREP: console.log("oi") \u2192 encontra (ignora formatacao)
|
|
1053
|
+
\u2022 grep falha com espacos extras:
|
|
1054
|
+
\u274C GREP: const x = 1 \u2192 precisa de regex complexa
|
|
1055
|
+
\u2705 SUPERGREP: const x = 1 \u2192 resolve pela estrutura
|
|
1056
|
+
|
|
1035
1057
|
\u{1F4DD} EXEMPLOS DE PATTERNS:
|
|
1036
|
-
\u2022 console.$METHOD(
|
|
1037
|
-
\u2022
|
|
1058
|
+
\u2022 console.$METHOD($MSG) \u2192 captura metodo e 1 argumento
|
|
1059
|
+
\u2022 console.$METHOD($$$ARGS) \u2192 captura metodo e todos os argumentos
|
|
1060
|
+
\u2022 function $NAME($$$PARAMS) { $$$BODY } \u2192 captura funcoes completas
|
|
1038
1061
|
\u2022 import { $$$ITEMS } from '$MODULE' \u2192 captura imports
|
|
1039
|
-
\u2022 const $VAR = $VALUE
|
|
1040
|
-
\u2022 try { $$$ } catch ($ERR) { $$$ }
|
|
1041
|
-
\u2022 await $FUNC($$$ARGS)
|
|
1062
|
+
\u2022 const $VAR = $VALUE \u2192 captura declaracoes
|
|
1063
|
+
\u2022 try { $$$ } catch ($ERR) { $$$ } \u2192 captura try-catch
|
|
1064
|
+
\u2022 await $FUNC($$$ARGS) \u2192 captura chamadas assincronas
|
|
1042
1065
|
|
|
1043
1066
|
\u{1F524} METAVARIAVEIS:
|
|
1044
|
-
\u2022 $VAR \u2192 captura UM no
|
|
1045
|
-
\u2022
|
|
1067
|
+
\u2022 $VAR \u2192 captura UM no nomeado (ex: um identificador, uma string, um numero)
|
|
1068
|
+
\u2022 $$VAR \u2192 captura UM no (incluindo nos nao-nomeados como operadores +, -)
|
|
1069
|
+
\u2022 $$$VARS \u2192 captura ZERO OU MAIS nos (ex: todos os args, corpo da funcao)
|
|
1070
|
+
|
|
1071
|
+
\u26A0\uFE0F $$$VARS captura multiplos nos \u2014 o texto de cada no e concatenado com
|
|
1072
|
+
", " no resultado da metavariable. Para multi-nos complexos (onde a
|
|
1073
|
+
concatenacao simples nao basta), use supergrep_tree para inspecionar a AST.
|
|
1046
1074
|
|
|
1047
1075
|
\u{1F4A1} DICA: Se o pattern nao der match, use supergrep_tree primeiro para
|
|
1048
1076
|
descobrir os kinds de nos disponiveis e ajustar o pattern.
|
|
@@ -1053,7 +1081,7 @@ Workflow recomendado: supergrep_find \u2192 supergrep_tree \u2192 supergrep_repl
|
|
|
1053
1081
|
"Padrao AST Grep na linguagem alvo. Ex: 'console.$METHOD($$$ARGS)', 'function $NAME($$$PARAMS) { $$$BODY }'"
|
|
1054
1082
|
),
|
|
1055
1083
|
lang: z.string().optional().describe(
|
|
1056
|
-
"Linguagem: 'typescript' (.ts), 'javascript' (.js), 'tsx' (.tsx), 'css' (.css), 'html' (.html).
|
|
1084
|
+
"Linguagem: 'typescript' (.ts), 'javascript' (.js), 'tsx' (.tsx), 'css' (.css), 'html' (.html). Opcional \u2014 se nao especificado, o motor autodetecta pela extensao de cada arquivo."
|
|
1057
1085
|
),
|
|
1058
1086
|
path: z.string().optional().describe(
|
|
1059
1087
|
"Arquivo ou diretorio onde buscar. Default: diretorio atual. Ex: 'src/', 'app/page.tsx'"
|
|
@@ -1095,21 +1123,38 @@ ${warnings.map((w) => ` \u2022 ${w}`).join("\n")}` : "";
|
|
|
1095
1123
|
"supergrep_tree",
|
|
1096
1124
|
{
|
|
1097
1125
|
title: "Explore AST Structure",
|
|
1098
|
-
description: `Explora a estrutura da AST (Abstract Syntax Tree) de um arquivo.
|
|
1126
|
+
description: `Explora a estrutura da AST (Abstract Syntax Tree) de um arquivo ou snippet.
|
|
1099
1127
|
|
|
1100
1128
|
Mostra todos os tipos de nos (kinds) presentes, com contagens e distribuicao.
|
|
1101
1129
|
Util para entender a estrutura do codigo e descobrir quais kinds usar
|
|
1102
|
-
em patterns de busca
|
|
1130
|
+
em patterns de busca.
|
|
1131
|
+
|
|
1132
|
+
\u{1F4D6} O QUE E UM "KIND":
|
|
1133
|
+
Um kind e o tipo de um no na arvore sintatica. Exemplos:
|
|
1134
|
+
\u2022 function_declaration \u2014 declaracao de funcao
|
|
1135
|
+
\u2022 call_expression \u2014 chamada de funcao/metodo
|
|
1136
|
+
\u2022 identifier \u2014 nome de variavel, funcao, etc.
|
|
1137
|
+
\u2022 string_fragment \u2014 literal de string
|
|
1138
|
+
Cada estrutura de codigo vira um kind diferente na AST.
|
|
1139
|
+
|
|
1140
|
+
\u{1F4A1} Como usar os kinds: ao ver call_expression no tree, voce pode buscar
|
|
1141
|
+
chamadas de funcao com o pattern: $FUNC($$$ARGS)
|
|
1142
|
+
|
|
1143
|
+
\u{1F916} AUTODETECCAO: se usar 'path', a linguagem e detectada automaticamente
|
|
1144
|
+
pela extensao do arquivo. O parametro 'lang' so e necessario ao usar
|
|
1145
|
+
'code' (snippet inline) ou para forcar uma linguagem especifica.
|
|
1103
1146
|
|
|
1104
1147
|
QUANDO USAR:
|
|
1105
|
-
- "Quais tipos de nos existem neste arquivo TypeScript?"
|
|
1106
|
-
- "Como escrever um pattern para capturar certas estruturas?"
|
|
1107
|
-
- "Preciso descobrir os kinds para uma regra de lint customizada"
|
|
1108
|
-
- Antes de escrever um pattern complexo, para conhecer a AST
|
|
1148
|
+
- "Quais tipos de nos existem neste arquivo TypeScript?"
|
|
1149
|
+
- "Como escrever um pattern para capturar certas estruturas?"
|
|
1150
|
+
- "Preciso descobrir os kinds para uma regra de lint customizada"
|
|
1151
|
+
- Antes de escrever um pattern complexo, para conhecer a AST
|
|
1152
|
+
- Testar um snippet de codigo sem criar arquivo (use o parametro code)
|
|
1109
1153
|
|
|
1110
1154
|
QUANDO NAO USAR:
|
|
1111
|
-
- Para buscar padroes no codigo \u2192 use supergrep_find
|
|
1112
|
-
- Para ver o conteudo do arquivo \u2192 leia o arquivo diretamente
|
|
1155
|
+
- Para buscar padroes no codigo \u2192 use supergrep_find
|
|
1156
|
+
- Para ver o conteudo do arquivo \u2192 leia o arquivo diretamente
|
|
1157
|
+
- Para diretorios (analisar multiplos arquivos) \u2192 use supergrep_find com o pattern desejado. supergrep_tree analisa UM arquivo ou snippet por vez.
|
|
1113
1158
|
|
|
1114
1159
|
Workflow: supergrep_tree \u2192 supergrep_find (usar kinds descobertos)`,
|
|
1115
1160
|
inputSchema: {
|
|
@@ -1158,27 +1203,39 @@ ${warnings.map((w) => ` \u2022 ${w}`).join("\n")}` : "";
|
|
|
1158
1203
|
server2.registerTool(
|
|
1159
1204
|
"supergrep_replace",
|
|
1160
1205
|
{
|
|
1161
|
-
title: "Preview Structural Replacement",
|
|
1162
|
-
description:
|
|
1206
|
+
title: "Preview Structural Replacement (read-only)",
|
|
1207
|
+
description: `\u{1F504} PREVIEW de substituicao estrutural de codigo (NAO modifica arquivos).
|
|
1163
1208
|
|
|
1164
1209
|
Encontra padroes com AST Grep e mostra um diff de como o codigo ficaria
|
|
1165
1210
|
apos a substituicao. Util para planejar refatoracoes em larga escala.
|
|
1166
1211
|
|
|
1167
|
-
LIMITACAO
|
|
1168
|
-
|
|
1212
|
+
\u26A0\uFE0F LIMITACAO DE METAVARIAVEIS NO REWRITE:
|
|
1213
|
+
\u2022 $VAR (single) \u2192 interpola corretamente \u2705
|
|
1214
|
+
Ex: console.$METHOD($MSG) \u2192 logger.$METHOD($MSG)
|
|
1215
|
+
\u2022 $$$VARS (multi) \u2192 interpolado como texto concatenado dos nos \u26A0\uFE0F
|
|
1216
|
+
Ex: console.log($$$ARGS) \u2192 logger.info(texto, dos, args)
|
|
1217
|
+
Para casos complexos, verifique o preview antes de aplicar.
|
|
1218
|
+
|
|
1219
|
+
A API napi do AST Grep nao interpola metavariaveis automaticamente.
|
|
1220
|
+
O supergrep faz interpolacao manual: textos dos nos sao concatenados
|
|
1221
|
+
com ", " e injetados no lugar do $VAR no rewrite.
|
|
1222
|
+
|
|
1223
|
+
\u{1F916} AUTODETECCAO DE LINGUAGEM: se 'lang' nao for especificado, o motor
|
|
1224
|
+
detecta automaticamente pela extensao de cada arquivo. Use 'lang' apenas
|
|
1225
|
+
para RESTRINGIR a busca a uma linguagem especifica.
|
|
1169
1226
|
|
|
1170
1227
|
QUANDO USAR:
|
|
1171
|
-
- "Como ficaria se eu trocasse console.log por logger.info?"
|
|
1172
|
-
- "Preview de substituir var por let em todo o projeto"
|
|
1173
|
-
- "Mostre o diff de trocar moment() por date-fns"
|
|
1174
|
-
- Planejar refatoracoes estruturais antes de aplicar
|
|
1228
|
+
- "Como ficaria se eu trocasse console.log por logger.info?"
|
|
1229
|
+
- "Preview de substituir var por let em todo o projeto"
|
|
1230
|
+
- "Mostre o diff de trocar moment() por date-fns"
|
|
1231
|
+
- Planejar refatoracoes estruturais antes de aplicar
|
|
1175
1232
|
|
|
1176
1233
|
QUANDO NAO USAR:
|
|
1177
|
-
- Para buscar padroes (sem substituir) \u2192 use supergrep_find
|
|
1178
|
-
- Para aplicar a substituicao de fato \u2192 use
|
|
1179
|
-
- Para refatoracoes que envolvem logica complexa \u2192 implemente manualmente
|
|
1234
|
+
- Para buscar padroes (sem substituir) \u2192 use supergrep_find
|
|
1235
|
+
- Para aplicar a substituicao de fato \u2192 use edit (substituicao exata) ou write (arquivo completo). O preview do supergrep_replace serve como guia.
|
|
1236
|
+
- Para refatoracoes que envolvem logica complexa \u2192 implemente manualmente
|
|
1180
1237
|
|
|
1181
|
-
Workflow: supergrep_find \u2192 supergrep_replace (preview) \u2192 aplicar mudancas`,
|
|
1238
|
+
Workflow: supergrep_find \u2192 supergrep_replace (preview) \u2192 aplicar mudancas com edit/write`,
|
|
1182
1239
|
inputSchema: {
|
|
1183
1240
|
pattern: z.string().min(1).describe(
|
|
1184
1241
|
"Padrao AST Grep a encontrar. Ex: 'console.log($$$ARGS)'"
|
|
@@ -1187,7 +1244,7 @@ Workflow: supergrep_find \u2192 supergrep_replace (preview) \u2192 aplicar mudan
|
|
|
1187
1244
|
"Texto de substituicao. Use $VAR para referenciar metavariaveis capturadas. Ex: 'logger.info($$$ARGS)'"
|
|
1188
1245
|
),
|
|
1189
1246
|
lang: z.string().optional().describe(
|
|
1190
|
-
"Linguagem: 'typescript', 'javascript', 'tsx', 'css', 'html'.
|
|
1247
|
+
"Linguagem: 'typescript', 'javascript', 'tsx', 'css', 'html'. Opcional \u2014 se nao especificado, o motor autodetecta pela extensao de cada arquivo."
|
|
1191
1248
|
),
|
|
1192
1249
|
path: z.string().optional().describe(
|
|
1193
1250
|
"Arquivo ou diretorio onde buscar. Default: diretorio atual."
|
package/dist/cli.js
CHANGED
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import {
|
|
2
2
|
startMcpServer
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5PJJZ4CK.js";
|
|
4
4
|
|
|
5
5
|
// src/index.ts
|
|
6
|
-
|
|
6
|
+
import { createRequire } from "module";
|
|
7
|
+
var require2 = createRequire(import.meta.url);
|
|
8
|
+
var pkg = require2("../package.json");
|
|
9
|
+
var VERSION = pkg.version;
|
|
7
10
|
export {
|
|
8
11
|
VERSION,
|
|
9
12
|
startMcpServer
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@justmpm/supergrep",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "MCP server para busca estrutural de código com AST Grep. Encontra padrões de código com precisão sintática, ignorando formatação e comentários. Suporta 20+ linguagens via Tree-sitter.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ast-grep",
|