grepmax 0.17.11 → 0.17.13
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/commands/mcp.js +174 -0
- package/dist/lib/graph/graph-builder.js +97 -0
- package/dist/lib/graph/graph-traversal.js +0 -0
- package/dist/lib/index/ignore-patterns.js +18 -0
- package/dist/lib/utils/file-utils.js +18 -0
- package/dist/lib/workers/orchestrator.js +4 -0
- package/package.json +2 -2
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
package/dist/commands/mcp.js
CHANGED
|
@@ -257,6 +257,65 @@ const TOOLS = [
|
|
|
257
257
|
},
|
|
258
258
|
},
|
|
259
259
|
},
|
|
260
|
+
{
|
|
261
|
+
name: "get_neighbors",
|
|
262
|
+
description: "Graph primitive: symbols reachable from a node along call edges within N hops. direction 'callees' = what it calls (outbound), 'callers' = what calls it (inbound). Each result carries its hop distance and definition location. Static call graph — same caveats as `dead`/`audit`.",
|
|
263
|
+
inputSchema: {
|
|
264
|
+
type: "object",
|
|
265
|
+
properties: {
|
|
266
|
+
symbol: { type: "string", description: "Starting symbol" },
|
|
267
|
+
direction: {
|
|
268
|
+
type: "string",
|
|
269
|
+
enum: ["callers", "callees"],
|
|
270
|
+
description: "Edge direction (default callees)",
|
|
271
|
+
},
|
|
272
|
+
max_hops: {
|
|
273
|
+
type: "number",
|
|
274
|
+
description: "Max hops (default 2, max 5)",
|
|
275
|
+
},
|
|
276
|
+
root: { type: "string", description: "Project root (absolute path)" },
|
|
277
|
+
},
|
|
278
|
+
required: ["symbol"],
|
|
279
|
+
},
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: "find_paths",
|
|
283
|
+
description: "Graph primitive: shortest call-graph path between two symbols, as a symbol sequence. direction 'callees' searches outward from `from` (does `from` transitively call `to`?), 'callers' searches inward. Returns 'no path' if unreachable within max_hops.",
|
|
284
|
+
inputSchema: {
|
|
285
|
+
type: "object",
|
|
286
|
+
properties: {
|
|
287
|
+
from: { type: "string", description: "Start symbol" },
|
|
288
|
+
to: { type: "string", description: "Target symbol" },
|
|
289
|
+
direction: {
|
|
290
|
+
type: "string",
|
|
291
|
+
enum: ["callers", "callees"],
|
|
292
|
+
description: "Search direction (default callees)",
|
|
293
|
+
},
|
|
294
|
+
max_hops: {
|
|
295
|
+
type: "number",
|
|
296
|
+
description: "Max hops (default 6, max 10)",
|
|
297
|
+
},
|
|
298
|
+
root: { type: "string", description: "Project root (absolute path)" },
|
|
299
|
+
},
|
|
300
|
+
required: ["from", "to"],
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
name: "subgraph_for_files",
|
|
305
|
+
description: "Graph primitive: the local dependency subgraph for a set of files — symbols they define, the call edges among those symbols, and their outbound external dependencies. Paths may be absolute or project-root-relative.",
|
|
306
|
+
inputSchema: {
|
|
307
|
+
type: "object",
|
|
308
|
+
properties: {
|
|
309
|
+
files: {
|
|
310
|
+
type: "array",
|
|
311
|
+
items: { type: "string" },
|
|
312
|
+
description: "File paths (absolute or root-relative)",
|
|
313
|
+
},
|
|
314
|
+
root: { type: "string", description: "Project root (absolute path)" },
|
|
315
|
+
},
|
|
316
|
+
required: ["files"],
|
|
317
|
+
},
|
|
318
|
+
},
|
|
260
319
|
{
|
|
261
320
|
name: "list_symbols",
|
|
262
321
|
description: "List indexed symbols with role and export status.",
|
|
@@ -1425,6 +1484,112 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
1425
1484
|
}
|
|
1426
1485
|
});
|
|
1427
1486
|
}
|
|
1487
|
+
function handleGetNeighbors(args) {
|
|
1488
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1489
|
+
ensureWatcher();
|
|
1490
|
+
const symbol = String(args.symbol || "");
|
|
1491
|
+
if (!symbol)
|
|
1492
|
+
return err("Missing required parameter: symbol");
|
|
1493
|
+
const direction = args.direction === "callers" ? "callers" : "callees";
|
|
1494
|
+
const maxHops = Math.min(Math.max(Number(args.max_hops) || 2, 1), 5);
|
|
1495
|
+
try {
|
|
1496
|
+
const root = typeof args.root === "string" && args.root ? args.root : projectRoot;
|
|
1497
|
+
const builder = new graph_builder_1.GraphBuilder(getVectorDb(), root);
|
|
1498
|
+
const hits = yield builder.getNeighbors(symbol, direction, maxHops);
|
|
1499
|
+
if (hits.length === 0) {
|
|
1500
|
+
return ok(`No ${direction} found for '${symbol}' within ${maxHops} hop(s). Check it is indexed (\`gmax status\`) or try the \`dead\` tool.`);
|
|
1501
|
+
}
|
|
1502
|
+
const rel = (p) => p.startsWith(root) ? p.slice(root.length + 1) : p;
|
|
1503
|
+
const lines = [
|
|
1504
|
+
`${direction} of ${symbol} (≤${maxHops} hops, ${hits.length} found):`,
|
|
1505
|
+
];
|
|
1506
|
+
for (const h of hits.slice(0, 100)) {
|
|
1507
|
+
const loc = h.file ? ` ${rel(h.file)}:${h.line + 1}` : " (external)";
|
|
1508
|
+
lines.push(` [${h.hops}h] ${h.symbol}${loc}`);
|
|
1509
|
+
}
|
|
1510
|
+
if (hits.length > 100)
|
|
1511
|
+
lines.push(` … and ${hits.length - 100} more`);
|
|
1512
|
+
return ok(lines.join("\n"));
|
|
1513
|
+
}
|
|
1514
|
+
catch (e) {
|
|
1515
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1516
|
+
return err(`get_neighbors failed: ${msg}`);
|
|
1517
|
+
}
|
|
1518
|
+
});
|
|
1519
|
+
}
|
|
1520
|
+
function handleFindPaths(args) {
|
|
1521
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1522
|
+
ensureWatcher();
|
|
1523
|
+
const from = String(args.from || "");
|
|
1524
|
+
const to = String(args.to || "");
|
|
1525
|
+
if (!from || !to) {
|
|
1526
|
+
return err("Missing required parameters: from and to");
|
|
1527
|
+
}
|
|
1528
|
+
const direction = args.direction === "callers" ? "callers" : "callees";
|
|
1529
|
+
const maxHops = Math.min(Math.max(Number(args.max_hops) || 6, 1), 10);
|
|
1530
|
+
try {
|
|
1531
|
+
const root = typeof args.root === "string" && args.root ? args.root : projectRoot;
|
|
1532
|
+
const builder = new graph_builder_1.GraphBuilder(getVectorDb(), root);
|
|
1533
|
+
const pathSyms = yield builder.findPaths(from, to, direction, maxHops);
|
|
1534
|
+
if (!pathSyms) {
|
|
1535
|
+
return ok(`No ${direction} path from '${from}' to '${to}' within ${maxHops} hops.`);
|
|
1536
|
+
}
|
|
1537
|
+
const arrow = direction === "callees" ? " → " : " ← ";
|
|
1538
|
+
const hops = pathSyms.length - 1;
|
|
1539
|
+
return ok(`Path (${hops} hop${hops === 1 ? "" : "s"}): ${pathSyms.join(arrow)}`);
|
|
1540
|
+
}
|
|
1541
|
+
catch (e) {
|
|
1542
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1543
|
+
return err(`find_paths failed: ${msg}`);
|
|
1544
|
+
}
|
|
1545
|
+
});
|
|
1546
|
+
}
|
|
1547
|
+
function handleSubgraphForFiles(args) {
|
|
1548
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
1549
|
+
ensureWatcher();
|
|
1550
|
+
const filesIn = Array.isArray(args.files)
|
|
1551
|
+
? args.files.map((f) => String(f)).filter(Boolean)
|
|
1552
|
+
: [];
|
|
1553
|
+
if (filesIn.length === 0) {
|
|
1554
|
+
return err("Missing required parameter: files (non-empty array)");
|
|
1555
|
+
}
|
|
1556
|
+
try {
|
|
1557
|
+
const root = typeof args.root === "string" && args.root ? args.root : projectRoot;
|
|
1558
|
+
const abs = filesIn.map((f) => path.isAbsolute(f) ? f : path.resolve(root, f));
|
|
1559
|
+
const builder = new graph_builder_1.GraphBuilder(getVectorDb(), root);
|
|
1560
|
+
const sg = yield builder.subgraphForFiles(abs);
|
|
1561
|
+
if (sg.symbols.length === 0) {
|
|
1562
|
+
return ok(`No indexed symbols found in: ${filesIn.join(", ")}. Check the paths and \`gmax status\`.`);
|
|
1563
|
+
}
|
|
1564
|
+
const rel = (p) => p.startsWith(root) ? p.slice(root.length + 1) : p;
|
|
1565
|
+
const cap = (arr, n) => arr.length > n
|
|
1566
|
+
? `${arr.slice(0, n).join(", ")} … (+${arr.length - n})`
|
|
1567
|
+
: arr.join(", ");
|
|
1568
|
+
const lines = [];
|
|
1569
|
+
lines.push(`Subgraph for ${sg.files.length} file(s): ${sg.symbols.length} symbols, ${sg.internalEdges.length} internal edges, ${sg.externalDeps.length} external deps`);
|
|
1570
|
+
lines.push(`Files: ${sg.files.map(rel).join(", ")}`);
|
|
1571
|
+
lines.push("");
|
|
1572
|
+
lines.push(`Symbols: ${cap(sg.symbols, 50)}`);
|
|
1573
|
+
lines.push("");
|
|
1574
|
+
lines.push("Internal edges:");
|
|
1575
|
+
for (const e of sg.internalEdges.slice(0, 80)) {
|
|
1576
|
+
lines.push(` ${e.from} → ${e.to}`);
|
|
1577
|
+
}
|
|
1578
|
+
if (sg.internalEdges.length === 0)
|
|
1579
|
+
lines.push(" none");
|
|
1580
|
+
if (sg.internalEdges.length > 80) {
|
|
1581
|
+
lines.push(` … and ${sg.internalEdges.length - 80} more`);
|
|
1582
|
+
}
|
|
1583
|
+
lines.push("");
|
|
1584
|
+
lines.push(`External deps: ${cap(sg.externalDeps, 50) || "none"}`);
|
|
1585
|
+
return ok(lines.join("\n"));
|
|
1586
|
+
}
|
|
1587
|
+
catch (e) {
|
|
1588
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
1589
|
+
return err(`subgraph_for_files failed: ${msg}`);
|
|
1590
|
+
}
|
|
1591
|
+
});
|
|
1592
|
+
}
|
|
1428
1593
|
function handleListSymbols(args) {
|
|
1429
1594
|
return __awaiter(this, void 0, void 0, function* () {
|
|
1430
1595
|
ensureWatcher();
|
|
@@ -2170,6 +2335,15 @@ exports.mcp = new commander_1.Command("mcp")
|
|
|
2170
2335
|
case "audit":
|
|
2171
2336
|
result = yield handleAudit(toolArgs);
|
|
2172
2337
|
break;
|
|
2338
|
+
case "get_neighbors":
|
|
2339
|
+
result = yield handleGetNeighbors(toolArgs);
|
|
2340
|
+
break;
|
|
2341
|
+
case "find_paths":
|
|
2342
|
+
result = yield handleFindPaths(toolArgs);
|
|
2343
|
+
break;
|
|
2344
|
+
case "subgraph_for_files":
|
|
2345
|
+
result = yield handleSubgraphForFiles(toolArgs);
|
|
2346
|
+
break;
|
|
2173
2347
|
case "list_symbols":
|
|
2174
2348
|
result = yield handleListSymbols(toolArgs);
|
|
2175
2349
|
break;
|
|
@@ -11,6 +11,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
12
|
exports.GraphBuilder = void 0;
|
|
13
13
|
const filter_builder_1 = require("../utils/filter-builder");
|
|
14
|
+
const graph_traversal_1 = require("./graph-traversal");
|
|
14
15
|
class GraphBuilder {
|
|
15
16
|
constructor(db, pathPrefix, excludePrefixes) {
|
|
16
17
|
this.db = db;
|
|
@@ -185,6 +186,102 @@ class GraphBuilder {
|
|
|
185
186
|
return trees;
|
|
186
187
|
});
|
|
187
188
|
}
|
|
189
|
+
// --- MCP graph primitives (Phase 7) -----------------------------------
|
|
190
|
+
/** Symbols the given symbol references (outbound edges). */
|
|
191
|
+
calleesOf(symbol) {
|
|
192
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
193
|
+
return this.getCallees(symbol);
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
/** Distinct symbols that reference the given symbol (inbound edges). */
|
|
197
|
+
callersOf(symbol) {
|
|
198
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
199
|
+
const nodes = yield this.getCallers(symbol);
|
|
200
|
+
const out = [];
|
|
201
|
+
for (const n of nodes) {
|
|
202
|
+
if (n.symbol && n.symbol !== "unknown" && !out.includes(n.symbol)) {
|
|
203
|
+
out.push(n.symbol);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return out;
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
neighborFn(direction) {
|
|
210
|
+
return direction === "callers"
|
|
211
|
+
? (s) => this.callersOf(s)
|
|
212
|
+
: (s) => this.calleesOf(s);
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* Symbols reachable from `symbol` along `direction` within `maxHops`, each
|
|
216
|
+
* annotated with hop distance and resolved to a definition location when one
|
|
217
|
+
* is indexed.
|
|
218
|
+
*/
|
|
219
|
+
getNeighbors(symbol, direction, maxHops) {
|
|
220
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
221
|
+
var _a, _b;
|
|
222
|
+
const hits = yield (0, graph_traversal_1.bfsNeighbors)(symbol, this.neighborFn(direction), maxHops);
|
|
223
|
+
const out = [];
|
|
224
|
+
for (const h of hits) {
|
|
225
|
+
const loc = yield this.resolveLocation(h.symbol);
|
|
226
|
+
out.push(Object.assign(Object.assign({}, h), { file: (_a = loc === null || loc === void 0 ? void 0 : loc.file) !== null && _a !== void 0 ? _a : "", line: (_b = loc === null || loc === void 0 ? void 0 : loc.line) !== null && _b !== void 0 ? _b : 0 }));
|
|
227
|
+
}
|
|
228
|
+
return out;
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
/** Shortest path `[from, …, to]` along `direction`, or null. */
|
|
232
|
+
findPaths(from_1, to_1, direction_1) {
|
|
233
|
+
return __awaiter(this, arguments, void 0, function* (from, to, direction, maxHops = 6) {
|
|
234
|
+
return (0, graph_traversal_1.findPath)(from, to, this.neighborFn(direction), maxHops);
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
/** Resolve a symbol to its first defining chunk's file:line, if indexed. */
|
|
238
|
+
resolveLocation(symbol) {
|
|
239
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
240
|
+
const table = yield this.db.ensureTable();
|
|
241
|
+
const escaped = (0, filter_builder_1.escapeSqlString)(symbol);
|
|
242
|
+
const rows = yield table
|
|
243
|
+
.query()
|
|
244
|
+
.select(["path", "start_line"])
|
|
245
|
+
.where(this.scopeWhere(`array_contains(defined_symbols, '${escaped}')`))
|
|
246
|
+
.limit(1)
|
|
247
|
+
.toArray();
|
|
248
|
+
if (rows.length === 0)
|
|
249
|
+
return null;
|
|
250
|
+
const r = rows[0];
|
|
251
|
+
return { file: String(r.path || ""), line: Number(r.start_line || 0) };
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Build the local dependency subgraph for a set of files: every symbol they
|
|
256
|
+
* define, the edges among those symbols, and their outbound external deps.
|
|
257
|
+
*/
|
|
258
|
+
subgraphForFiles(files) {
|
|
259
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
260
|
+
if (files.length === 0) {
|
|
261
|
+
return { files: [], symbols: [], internalEdges: [], externalDeps: [] };
|
|
262
|
+
}
|
|
263
|
+
const table = yield this.db.ensureTable();
|
|
264
|
+
const orClause = files
|
|
265
|
+
.map((f) => `path = '${(0, filter_builder_1.escapeSqlString)(f)}'`)
|
|
266
|
+
.join(" OR ");
|
|
267
|
+
const rows = yield table
|
|
268
|
+
.query()
|
|
269
|
+
.select(["path", "defined_symbols", "referenced_symbols"])
|
|
270
|
+
.where(this.scopeWhere(`(${orClause})`))
|
|
271
|
+
.limit(100000)
|
|
272
|
+
.toArray();
|
|
273
|
+
const toArray = (val) => {
|
|
274
|
+
if (val && typeof val.toArray === "function")
|
|
275
|
+
return val.toArray();
|
|
276
|
+
return Array.isArray(val) ? val : [];
|
|
277
|
+
};
|
|
278
|
+
return (0, graph_traversal_1.buildFileSubgraph)(rows.map((r) => ({
|
|
279
|
+
path: String(r.path || ""),
|
|
280
|
+
defined_symbols: toArray(r.defined_symbols),
|
|
281
|
+
referenced_symbols: toArray(r.referenced_symbols),
|
|
282
|
+
})));
|
|
283
|
+
});
|
|
284
|
+
}
|
|
188
285
|
mapRowToNode(row, targetSymbol, type) {
|
|
189
286
|
// Helper to convert Arrow Vector to array if needed
|
|
190
287
|
const toArray = (val) => {
|
|
Binary file
|
|
@@ -41,6 +41,24 @@ exports.DEFAULT_IGNORE_PATTERNS = [
|
|
|
41
41
|
"*.min.css",
|
|
42
42
|
"*.map",
|
|
43
43
|
"*.wasm",
|
|
44
|
+
// Machine-generated source (floods the index + ranks codegen as god nodes).
|
|
45
|
+
// Content-based @generated/DO-NOT-EDIT header sniff (file-utils.ts) catches the
|
|
46
|
+
// rest; these are the unambiguous filename/dir conventions.
|
|
47
|
+
"**/__generated__/**", // Relay / graphql-codegen
|
|
48
|
+
"**/Generated/**", // Apollo iOS, Xcode codegen
|
|
49
|
+
"*.graphql.swift", // Apollo iOS operations
|
|
50
|
+
"*.pb.go", // protobuf (Go)
|
|
51
|
+
"*.pb.cc",
|
|
52
|
+
"*.pb.h", // protobuf (C++)
|
|
53
|
+
"*_pb2.py",
|
|
54
|
+
"*_pb2.pyi",
|
|
55
|
+
"*_pb2_grpc.py", // protobuf (Python)
|
|
56
|
+
"*.g.dart",
|
|
57
|
+
"*.freezed.dart",
|
|
58
|
+
"*.gr.dart", // Dart codegen
|
|
59
|
+
"*.designer.cs", // C# designer
|
|
60
|
+
"*.generated.ts",
|
|
61
|
+
"*.generated.tsx", // graphql-codegen / common TS
|
|
44
62
|
// Test fixtures and benchmark data
|
|
45
63
|
"**/fixtures/**",
|
|
46
64
|
"**/benchmark/**",
|
|
@@ -47,6 +47,7 @@ exports.hasNullByte = hasNullByte;
|
|
|
47
47
|
exports.readFileSnapshot = readFileSnapshot;
|
|
48
48
|
exports.isIndexableFile = isIndexableFile;
|
|
49
49
|
exports.isIndexablePath = isIndexablePath;
|
|
50
|
+
exports.isGeneratedContent = isGeneratedContent;
|
|
50
51
|
exports.formatDenseSnippet = formatDenseSnippet;
|
|
51
52
|
exports.isDevelopment = isDevelopment;
|
|
52
53
|
const node_crypto_1 = require("node:crypto");
|
|
@@ -111,6 +112,23 @@ function isIndexableFile(filePath, size) {
|
|
|
111
112
|
function isIndexablePath(filePath) {
|
|
112
113
|
return isIndexableFile(filePath);
|
|
113
114
|
}
|
|
115
|
+
// Well-known machine-generated markers emitted at the top of codegen output.
|
|
116
|
+
// Tools (protoc, graphql-codegen, Apollo, Relay, Go `stringer`, OpenAPI, etc.)
|
|
117
|
+
// stamp one of these in a banner comment. Matched against only the first few KB
|
|
118
|
+
// because the banner is always at the very top — this keeps a hand-written
|
|
119
|
+
// "do not edit this section" note deep in a real file from being misclassified.
|
|
120
|
+
const GENERATED_MARKER = /@generated\b|\bDO NOT EDIT\b|Code generated by |auto-?generated by |This (?:file|code) (?:is|was) (?:\w+[ -])?generated|machine generated|@autogenerated\b/i;
|
|
121
|
+
const GENERATED_SNIFF_BYTES = 2048;
|
|
122
|
+
/**
|
|
123
|
+
* Detect machine-generated source by its banner. Generated files flood both the
|
|
124
|
+
* vector index and the symbol graph (codegen types rank as god nodes), so they
|
|
125
|
+
* are skipped at index time. Path globs in `ignore-patterns.ts` catch the
|
|
126
|
+
* obvious filenames; this catches the rest by content.
|
|
127
|
+
*/
|
|
128
|
+
function isGeneratedContent(buffer) {
|
|
129
|
+
const head = buffer.subarray(0, GENERATED_SNIFF_BYTES).toString("utf-8");
|
|
130
|
+
return GENERATED_MARKER.test(head);
|
|
131
|
+
}
|
|
114
132
|
function formatDenseSnippet(text, maxLength = 1500) {
|
|
115
133
|
const clean = text !== null && text !== void 0 ? text : "";
|
|
116
134
|
if (clean.length <= maxLength)
|
|
@@ -252,6 +252,10 @@ class WorkerOrchestrator {
|
|
|
252
252
|
(0, logger_1.debug)("orch", `skip ${input.path} (empty or binary)`);
|
|
253
253
|
return { vectors: [], hash, mtimeMs, size, shouldDelete: true };
|
|
254
254
|
}
|
|
255
|
+
if ((0, file_utils_1.isGeneratedContent)(buffer)) {
|
|
256
|
+
(0, logger_1.debug)("orch", `skip ${input.path} (machine-generated header)`);
|
|
257
|
+
return { vectors: [], hash, mtimeMs, size, shouldDelete: true };
|
|
258
|
+
}
|
|
255
259
|
onProgress === null || onProgress === void 0 ? void 0 : onProgress();
|
|
256
260
|
yield this.ensureReady();
|
|
257
261
|
onProgress === null || onProgress === void 0 ? void 0 : onProgress();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "grepmax",
|
|
3
|
-
"version": "0.17.
|
|
3
|
+
"version": "0.17.13",
|
|
4
4
|
"author": "Robert Owens <78518764+reowens@users.noreply.github.com>",
|
|
5
5
|
"homepage": "https://github.com/reowens/grepmax",
|
|
6
6
|
"bugs": {
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"prepublishOnly": "pnpm build",
|
|
38
38
|
"preversion": "pnpm test && pnpm typecheck",
|
|
39
39
|
"version": "bash scripts/sync-versions.sh && git add -A",
|
|
40
|
-
"postversion": "
|
|
40
|
+
"postversion": "bash scripts/postrelease.sh"
|
|
41
41
|
},
|
|
42
42
|
"keywords": [
|
|
43
43
|
"grepmax",
|