grepmax 0.16.2 → 0.16.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.
- package/dist/commands/diff.js +8 -140
- package/dist/commands/log.js +231 -0
- package/dist/commands/recent.js +7 -129
- package/dist/index.js +2 -0
- package/dist/lib/utils/git.js +90 -0
- package/package.json +1 -1
- package/plugins/grepmax/.claude-plugin/plugin.json +1 -1
- package/plugins/grepmax/skills/grepmax/SKILL.md +11 -11
package/dist/commands/diff.js
CHANGED
|
@@ -1,145 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
-
});
|
|
10
|
-
};
|
|
11
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
3
|
exports.diff = void 0;
|
|
13
4
|
const commander_1 = require("commander");
|
|
14
|
-
const searcher_1 = require("../lib/search/searcher");
|
|
15
|
-
const vector_db_1 = require("../lib/store/vector-db");
|
|
16
|
-
const filter_builder_1 = require("../lib/utils/filter-builder");
|
|
17
|
-
const exit_1 = require("../lib/utils/exit");
|
|
18
|
-
const git_1 = require("../lib/utils/git");
|
|
19
|
-
const project_registry_1 = require("../lib/utils/project-registry");
|
|
20
|
-
const project_root_1 = require("../lib/utils/project-root");
|
|
21
|
-
const arrow_1 = require("../lib/utils/arrow");
|
|
22
5
|
exports.diff = new commander_1.Command("diff")
|
|
23
|
-
.description("
|
|
24
|
-
.argument("[ref]", "
|
|
25
|
-
.
|
|
26
|
-
.
|
|
27
|
-
.
|
|
28
|
-
.
|
|
29
|
-
.
|
|
30
|
-
|
|
31
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
|
|
32
|
-
const limit = Math.min(Math.max(Number.parseInt(opts.maxCount || "10", 10), 1), 50);
|
|
33
|
-
let vectorDb = null;
|
|
34
|
-
try {
|
|
35
|
-
const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
|
|
36
|
-
if (root === null)
|
|
37
|
-
return;
|
|
38
|
-
const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
|
|
39
|
-
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
40
|
-
vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
41
|
-
const changedFiles = (0, git_1.getChangedFiles)(ref, projectRoot);
|
|
42
|
-
if (changedFiles.length === 0) {
|
|
43
|
-
console.log(ref ? `No changes found relative to ${ref}.` : "No uncommitted changes found.");
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
const rel = (p) => p.startsWith(`${projectRoot}/`) ? p.slice(projectRoot.length + 1) : p;
|
|
47
|
-
if (opts.query) {
|
|
48
|
-
// Semantic search scoped to changed files
|
|
49
|
-
const searcher = new searcher_1.Searcher(vectorDb);
|
|
50
|
-
const response = yield searcher.search(opts.query, limit, { rerank: true }, Object.assign({}, (opts.role ? { role: opts.role } : {})), projectRoot);
|
|
51
|
-
// Filter results to only changed files
|
|
52
|
-
const changedSet = new Set(changedFiles);
|
|
53
|
-
const filtered = response.data.filter((r) => {
|
|
54
|
-
var _a;
|
|
55
|
-
const p = ((_a = r.metadata) === null || _a === void 0 ? void 0 : _a.path) || r.path || "";
|
|
56
|
-
return changedSet.has(p);
|
|
57
|
-
});
|
|
58
|
-
if (filtered.length === 0) {
|
|
59
|
-
console.log("No indexed results found in changed files for that query.");
|
|
60
|
-
return;
|
|
61
|
-
}
|
|
62
|
-
if (opts.agent) {
|
|
63
|
-
for (const r of filtered.slice(0, limit)) {
|
|
64
|
-
const p = String(r.path || ((_b = r.metadata) === null || _b === void 0 ? void 0 : _b.path) || "");
|
|
65
|
-
const line = Number((_c = r.start_line) !== null && _c !== void 0 ? _c : 0);
|
|
66
|
-
const sym = (_e = (_d = (0, arrow_1.toArr)(r.defined_symbols)) === null || _d === void 0 ? void 0 : _d[0]) !== null && _e !== void 0 ? _e : "";
|
|
67
|
-
const role = String(r.role || "IMPL");
|
|
68
|
-
console.log(`${rel(p)}:${line + 1} ${sym} [${role}]`);
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
else {
|
|
72
|
-
console.log(`Changed files matching "${opts.query}":\n`);
|
|
73
|
-
for (const r of filtered.slice(0, limit)) {
|
|
74
|
-
const p = String(r.path || ((_f = r.metadata) === null || _f === void 0 ? void 0 : _f.path) || "");
|
|
75
|
-
const line = Number((_g = r.start_line) !== null && _g !== void 0 ? _g : 0);
|
|
76
|
-
const sym = (_j = (_h = (0, arrow_1.toArr)(r.defined_symbols)) === null || _h === void 0 ? void 0 : _h[0]) !== null && _j !== void 0 ? _j : "";
|
|
77
|
-
const role = String(r.role || "IMPLEMENTATION");
|
|
78
|
-
const score = (_l = (_k = r.score) === null || _k === void 0 ? void 0 : _k.toFixed(3)) !== null && _l !== void 0 ? _l : "?";
|
|
79
|
-
console.log(` ${rel(p)}:${line + 1} ${sym} [${role}] (${score})`);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
else {
|
|
84
|
-
// No query — list changed files with their indexed symbols
|
|
85
|
-
const table = yield vectorDb.ensureTable();
|
|
86
|
-
if (opts.agent) {
|
|
87
|
-
for (const file of changedFiles) {
|
|
88
|
-
const chunks = yield table
|
|
89
|
-
.query()
|
|
90
|
-
.select(["path", "start_line", "defined_symbols", "role"])
|
|
91
|
-
.where(`path = '${(0, filter_builder_1.escapeSqlString)(file)}'`)
|
|
92
|
-
.limit(50)
|
|
93
|
-
.toArray();
|
|
94
|
-
if (chunks.length === 0) {
|
|
95
|
-
console.log(`${rel(file)}\t(not indexed)`);
|
|
96
|
-
}
|
|
97
|
-
else {
|
|
98
|
-
for (const chunk of chunks) {
|
|
99
|
-
const sym = (_o = (_m = (0, arrow_1.toArr)(chunk.defined_symbols)) === null || _m === void 0 ? void 0 : _m[0]) !== null && _o !== void 0 ? _o : "";
|
|
100
|
-
const line = (_p = chunk.start_line) !== null && _p !== void 0 ? _p : 0;
|
|
101
|
-
const role = (chunk.role || "IMPL").slice(0, 4);
|
|
102
|
-
if (sym) {
|
|
103
|
-
console.log(`${rel(file)}:${line + 1}\t${sym}\t[${role}]`);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
console.log(`${changedFiles.length} changed file${changedFiles.length === 1 ? "" : "s"}${ref ? ` (vs ${ref})` : ""}:\n`);
|
|
111
|
-
for (const file of changedFiles) {
|
|
112
|
-
const chunks = yield table
|
|
113
|
-
.query()
|
|
114
|
-
.select(["defined_symbols", "role"])
|
|
115
|
-
.where(`path = '${(0, filter_builder_1.escapeSqlString)(file)}'`)
|
|
116
|
-
.limit(50)
|
|
117
|
-
.toArray();
|
|
118
|
-
const symbols = chunks
|
|
119
|
-
.flatMap((c) => (0, arrow_1.toArr)(c.defined_symbols))
|
|
120
|
-
.filter(Boolean);
|
|
121
|
-
if (symbols.length > 0) {
|
|
122
|
-
console.log(` ${rel(file)} (${symbols.length} symbol${symbols.length === 1 ? "" : "s"}: ${symbols.slice(0, 5).join(", ")}${symbols.length > 5 ? "..." : ""})`);
|
|
123
|
-
}
|
|
124
|
-
else {
|
|
125
|
-
console.log(` ${rel(file)}`);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
catch (error) {
|
|
132
|
-
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
133
|
-
console.error("Diff failed:", msg);
|
|
134
|
-
process.exitCode = 1;
|
|
135
|
-
}
|
|
136
|
-
finally {
|
|
137
|
-
if (vectorDb) {
|
|
138
|
-
try {
|
|
139
|
-
yield vectorDb.close();
|
|
140
|
-
}
|
|
141
|
-
catch (_q) { }
|
|
142
|
-
}
|
|
143
|
-
yield (0, exit_1.gracefulExit)();
|
|
144
|
-
}
|
|
145
|
-
}));
|
|
6
|
+
.description("[deprecated] Use 'gmax log' instead")
|
|
7
|
+
.argument("[ref]", "(ignored — diff is deprecated)")
|
|
8
|
+
.allowUnknownOption(true)
|
|
9
|
+
.allowExcessArguments(true)
|
|
10
|
+
.action(() => {
|
|
11
|
+
console.error("gmax diff is deprecated; use 'gmax log <path-or-symbol>' instead");
|
|
12
|
+
process.exitCode = 1;
|
|
13
|
+
});
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.log = void 0;
|
|
46
|
+
const fs = __importStar(require("node:fs"));
|
|
47
|
+
const path = __importStar(require("node:path"));
|
|
48
|
+
const commander_1 = require("commander");
|
|
49
|
+
const vector_db_1 = require("../lib/store/vector-db");
|
|
50
|
+
const exit_1 = require("../lib/utils/exit");
|
|
51
|
+
const filter_builder_1 = require("../lib/utils/filter-builder");
|
|
52
|
+
const git_1 = require("../lib/utils/git");
|
|
53
|
+
const project_registry_1 = require("../lib/utils/project-registry");
|
|
54
|
+
const project_root_1 = require("../lib/utils/project-root");
|
|
55
|
+
const useColors = process.stdout.isTTY && !process.env.NO_COLOR;
|
|
56
|
+
const style = {
|
|
57
|
+
bold: (s) => (useColors ? `\x1b[1m${s}\x1b[22m` : s),
|
|
58
|
+
dim: (s) => (useColors ? `\x1b[2m${s}\x1b[22m` : s),
|
|
59
|
+
cyan: (s) => (useColors ? `\x1b[36m${s}\x1b[39m` : s),
|
|
60
|
+
yellow: (s) => (useColors ? `\x1b[33m${s}\x1b[39m` : s),
|
|
61
|
+
};
|
|
62
|
+
function relativize(p, projectRoot) {
|
|
63
|
+
const prefix = projectRoot.endsWith("/") ? projectRoot : `${projectRoot}/`;
|
|
64
|
+
return p.startsWith(prefix) ? p.slice(prefix.length) : p;
|
|
65
|
+
}
|
|
66
|
+
function resolveSymbolPaths(vectorDb, symbol, projectRoot, inOpt, excludeOpt) {
|
|
67
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
68
|
+
const { resolveScope, buildScopeWhere } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/scope-filter")));
|
|
69
|
+
const scope = resolveScope({
|
|
70
|
+
projectRoot,
|
|
71
|
+
in: inOpt,
|
|
72
|
+
exclude: excludeOpt,
|
|
73
|
+
});
|
|
74
|
+
const where = buildScopeWhere(scope, `array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(symbol)}')`);
|
|
75
|
+
const table = yield vectorDb.ensureTable();
|
|
76
|
+
const rows = yield table
|
|
77
|
+
.query()
|
|
78
|
+
.select(["path"])
|
|
79
|
+
.where(where)
|
|
80
|
+
.limit(50)
|
|
81
|
+
.toArray();
|
|
82
|
+
const paths = new Set();
|
|
83
|
+
for (const row of rows) {
|
|
84
|
+
const p = String(row.path || "");
|
|
85
|
+
if (p)
|
|
86
|
+
paths.add(p);
|
|
87
|
+
}
|
|
88
|
+
return [...paths];
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
function printHuman(commits, projectRoot, targetPaths) {
|
|
92
|
+
for (const c of commits) {
|
|
93
|
+
const header = `${style.yellow(c.shortHash)} ${c.author} ${style.dim(c.relDate)} ${style.bold(c.subject)}`;
|
|
94
|
+
console.log(header);
|
|
95
|
+
const stat = `${c.filesChanged} file${c.filesChanged === 1 ? "" : "s"} changed, +${c.insertions} / -${c.deletions}`;
|
|
96
|
+
console.log(` ${style.dim(stat)}`);
|
|
97
|
+
if (targetPaths && targetPaths.length > 1) {
|
|
98
|
+
const targetSet = new Set(targetPaths);
|
|
99
|
+
const touched = c.numstatLines
|
|
100
|
+
.filter((n) => targetSet.has(n.path) || targetSet.has(path.resolve(projectRoot, n.path)))
|
|
101
|
+
.map((n) => relativize(n.path, projectRoot));
|
|
102
|
+
if (touched.length > 0) {
|
|
103
|
+
console.log(` ${style.dim(`via: ${touched.join(", ")}`)}`);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
console.log();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
function printAgent(commits, projectRoot, targetPaths) {
|
|
110
|
+
const targetSet = targetPaths && targetPaths.length > 0 ? new Set(targetPaths) : null;
|
|
111
|
+
for (const c of commits) {
|
|
112
|
+
let touched = "";
|
|
113
|
+
if (targetSet) {
|
|
114
|
+
const touchedPaths = c.numstatLines
|
|
115
|
+
.filter((n) => targetSet.has(n.path) || targetSet.has(path.resolve(projectRoot, n.path)))
|
|
116
|
+
.map((n) => relativize(n.path, projectRoot));
|
|
117
|
+
touched = touchedPaths.join(",");
|
|
118
|
+
}
|
|
119
|
+
const subject = c.subject.replace(/\t/g, " ");
|
|
120
|
+
console.log(`${c.shortHash}\t${c.isoDate}\t${c.author}\t${subject}\t${c.filesChanged}\t${c.insertions}\t${c.deletions}\t${touched}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
exports.log = new commander_1.Command("log")
|
|
124
|
+
.description("Show git commit history for a path or symbol")
|
|
125
|
+
.argument("<path-or-symbol>", "File/dir path or symbol name")
|
|
126
|
+
.option("-l, --limit <n>", "Max commits (default 20)", "20")
|
|
127
|
+
.option("--since <date>", "Filter by date (e.g. '2 weeks ago', '2025-01-01')")
|
|
128
|
+
.option("--from <ref>", "Show commits since git ref (translates to <ref>..HEAD)")
|
|
129
|
+
.option("--author <name>", "Filter by author")
|
|
130
|
+
.option("--root <dir>", "Project root directory")
|
|
131
|
+
.option("--in <subpath>", "Restrict symbol resolution to a sub-path (repeatable; symbol mode only)", (value, prev) => prev ? [...prev, value] : [value])
|
|
132
|
+
.option("--exclude <subpath>", "Exclude a sub-path from symbol resolution (repeatable; symbol mode only)", (value, prev) => prev ? [...prev, value] : [value])
|
|
133
|
+
.option("--no-follow", "Disable rename tracking (default: enabled for single files in path mode)")
|
|
134
|
+
.option("--agent", "Compact TSV output for AI agents", false)
|
|
135
|
+
.action((arg, opts) => __awaiter(void 0, void 0, void 0, function* () {
|
|
136
|
+
var _a;
|
|
137
|
+
const limit = Math.min(Math.max(Number.parseInt(opts.limit || "20", 10), 1), 200);
|
|
138
|
+
const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
|
|
139
|
+
if (root === null)
|
|
140
|
+
return;
|
|
141
|
+
const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
|
|
142
|
+
let vectorDb = null;
|
|
143
|
+
try {
|
|
144
|
+
// 1. Try arg as path (relative to projectRoot, then cwd).
|
|
145
|
+
const candidates = [
|
|
146
|
+
path.resolve(projectRoot, arg),
|
|
147
|
+
path.resolve(process.cwd(), arg),
|
|
148
|
+
];
|
|
149
|
+
let resolvedPath = null;
|
|
150
|
+
for (const c of candidates) {
|
|
151
|
+
if (fs.existsSync(c)) {
|
|
152
|
+
resolvedPath = c;
|
|
153
|
+
break;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
if (resolvedPath) {
|
|
157
|
+
const isDir = fs.statSync(resolvedPath).isDirectory();
|
|
158
|
+
const commits = (0, git_1.getCommitHistory)({
|
|
159
|
+
paths: [resolvedPath],
|
|
160
|
+
limit,
|
|
161
|
+
since: opts.since,
|
|
162
|
+
from: opts.from,
|
|
163
|
+
author: opts.author,
|
|
164
|
+
follow: !isDir && opts.follow !== false,
|
|
165
|
+
cwd: projectRoot,
|
|
166
|
+
});
|
|
167
|
+
if (commits.length === 0) {
|
|
168
|
+
console.log(opts.agent
|
|
169
|
+
? ""
|
|
170
|
+
: `No commits found for ${relativize(resolvedPath, projectRoot)}.`);
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (opts.agent)
|
|
174
|
+
printAgent(commits, projectRoot, null);
|
|
175
|
+
else
|
|
176
|
+
printHuman(commits, projectRoot, null);
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
// 2. Try arg as symbol via index lookup.
|
|
180
|
+
const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
|
|
181
|
+
vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
|
|
182
|
+
const symbolPaths = yield resolveSymbolPaths(vectorDb, arg, projectRoot, opts.in, opts.exclude);
|
|
183
|
+
if (symbolPaths.length === 0) {
|
|
184
|
+
console.error(`gmax log: no file or symbol matched '${arg}'`);
|
|
185
|
+
if (!opts.agent) {
|
|
186
|
+
console.error("");
|
|
187
|
+
console.error("Try `gmax search <term>` to find a symbol, or pass a file path.");
|
|
188
|
+
}
|
|
189
|
+
process.exitCode = 1;
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const commits = (0, git_1.getCommitHistory)({
|
|
193
|
+
paths: symbolPaths,
|
|
194
|
+
limit,
|
|
195
|
+
since: opts.since,
|
|
196
|
+
from: opts.from,
|
|
197
|
+
author: opts.author,
|
|
198
|
+
follow: false,
|
|
199
|
+
cwd: projectRoot,
|
|
200
|
+
});
|
|
201
|
+
if (commits.length === 0) {
|
|
202
|
+
console.log(opts.agent
|
|
203
|
+
? ""
|
|
204
|
+
: `No commits found touching defining files for symbol '${arg}'.`);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
if (opts.agent)
|
|
208
|
+
printAgent(commits, projectRoot, symbolPaths);
|
|
209
|
+
else {
|
|
210
|
+
if (symbolPaths.length > 1) {
|
|
211
|
+
console.log(style.dim(`// '${arg}' defined in ${symbolPaths.length} files; commits merged by hash.`));
|
|
212
|
+
console.log();
|
|
213
|
+
}
|
|
214
|
+
printHuman(commits, projectRoot, symbolPaths);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
219
|
+
console.error("Log failed:", msg);
|
|
220
|
+
process.exitCode = 1;
|
|
221
|
+
}
|
|
222
|
+
finally {
|
|
223
|
+
if (vectorDb) {
|
|
224
|
+
try {
|
|
225
|
+
yield vectorDb.close();
|
|
226
|
+
}
|
|
227
|
+
catch (_b) { }
|
|
228
|
+
}
|
|
229
|
+
yield (0, exit_1.gracefulExit)();
|
|
230
|
+
}
|
|
231
|
+
}));
|
package/dist/commands/recent.js
CHANGED
|
@@ -1,134 +1,12 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
-
if (k2 === undefined) k2 = k;
|
|
4
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
-
}
|
|
8
|
-
Object.defineProperty(o, k2, desc);
|
|
9
|
-
}) : (function(o, m, k, k2) {
|
|
10
|
-
if (k2 === undefined) k2 = k;
|
|
11
|
-
o[k2] = m[k];
|
|
12
|
-
}));
|
|
13
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
-
}) : function(o, v) {
|
|
16
|
-
o["default"] = v;
|
|
17
|
-
});
|
|
18
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
-
var ownKeys = function(o) {
|
|
20
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
-
var ar = [];
|
|
22
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
-
return ar;
|
|
24
|
-
};
|
|
25
|
-
return ownKeys(o);
|
|
26
|
-
};
|
|
27
|
-
return function (mod) {
|
|
28
|
-
if (mod && mod.__esModule) return mod;
|
|
29
|
-
var result = {};
|
|
30
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
-
__setModuleDefault(result, mod);
|
|
32
|
-
return result;
|
|
33
|
-
};
|
|
34
|
-
})();
|
|
35
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
36
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
37
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
38
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
39
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
40
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
41
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
42
|
-
});
|
|
43
|
-
};
|
|
44
|
-
var __asyncValues = (this && this.__asyncValues) || function (o) {
|
|
45
|
-
if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
|
|
46
|
-
var m = o[Symbol.asyncIterator], i;
|
|
47
|
-
return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
|
|
48
|
-
function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
|
|
49
|
-
function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
|
|
50
|
-
};
|
|
51
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
3
|
exports.recent = void 0;
|
|
53
|
-
const path = __importStar(require("node:path"));
|
|
54
4
|
const commander_1 = require("commander");
|
|
55
|
-
const config_1 = require("../config");
|
|
56
|
-
const meta_cache_1 = require("../lib/store/meta-cache");
|
|
57
|
-
const exit_1 = require("../lib/utils/exit");
|
|
58
|
-
const format_helpers_1 = require("../lib/utils/format-helpers");
|
|
59
|
-
const project_registry_1 = require("../lib/utils/project-registry");
|
|
60
|
-
const project_root_1 = require("../lib/utils/project-root");
|
|
61
5
|
exports.recent = new commander_1.Command("recent")
|
|
62
|
-
.description("
|
|
63
|
-
.
|
|
64
|
-
.
|
|
65
|
-
.
|
|
66
|
-
.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const limit = Math.min(Math.max(Number.parseInt(opts.limit || "20", 10), 1), 50);
|
|
70
|
-
try {
|
|
71
|
-
const resolvedRoot = (0, project_registry_1.resolveRootOrExit)(opts.root);
|
|
72
|
-
if (resolvedRoot === null)
|
|
73
|
-
return;
|
|
74
|
-
const root = (_d = (0, project_root_1.findProjectRoot)(resolvedRoot)) !== null && _d !== void 0 ? _d : resolvedRoot;
|
|
75
|
-
const prefix = root.endsWith("/") ? root : `${root}/`;
|
|
76
|
-
const metaCache = new meta_cache_1.MetaCache(config_1.PATHS.lmdbPath);
|
|
77
|
-
try {
|
|
78
|
-
const files = [];
|
|
79
|
-
try {
|
|
80
|
-
for (var _e = true, _f = __asyncValues(metaCache.entries()), _g; _g = yield _f.next(), _a = _g.done, !_a; _e = true) {
|
|
81
|
-
_c = _g.value;
|
|
82
|
-
_e = false;
|
|
83
|
-
const { path: p, entry } = _c;
|
|
84
|
-
if (p.startsWith(prefix)) {
|
|
85
|
-
files.push({ path: p, mtimeMs: entry.mtimeMs });
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
catch (e_1_1) { e_1 = { error: e_1_1 }; }
|
|
90
|
-
finally {
|
|
91
|
-
try {
|
|
92
|
-
if (!_e && !_a && (_b = _f.return)) yield _b.call(_f);
|
|
93
|
-
}
|
|
94
|
-
finally { if (e_1) throw e_1.error; }
|
|
95
|
-
}
|
|
96
|
-
files.sort((a, b) => b.mtimeMs - a.mtimeMs);
|
|
97
|
-
const top = files.slice(0, limit);
|
|
98
|
-
if (top.length === 0) {
|
|
99
|
-
console.log(`No indexed files found for ${root}`);
|
|
100
|
-
console.log("\nTry: `gmax add` to register and index this project, or `gmax status` to see what's indexed.");
|
|
101
|
-
process.exitCode = 1;
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
const now = Date.now();
|
|
105
|
-
if (opts.agent) {
|
|
106
|
-
for (const f of top) {
|
|
107
|
-
const rel = f.path.startsWith(prefix)
|
|
108
|
-
? f.path.slice(prefix.length)
|
|
109
|
-
: f.path;
|
|
110
|
-
console.log(`${rel}\t${(0, format_helpers_1.formatTimeAgo)(now - f.mtimeMs)}`);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
console.log(`Recent changes in ${path.basename(root)} (${top.length} most recent):\n`);
|
|
115
|
-
for (const f of top) {
|
|
116
|
-
const rel = f.path.startsWith(prefix)
|
|
117
|
-
? f.path.slice(prefix.length)
|
|
118
|
-
: f.path;
|
|
119
|
-
const ago = (0, format_helpers_1.formatTimeAgo)(now - f.mtimeMs);
|
|
120
|
-
console.log(` ${ago.padEnd(10)} ${rel}`);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
finally {
|
|
125
|
-
yield metaCache.close();
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
catch (error) {
|
|
129
|
-
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
130
|
-
console.error("Recent changes failed:", msg);
|
|
131
|
-
process.exitCode = 1;
|
|
132
|
-
}
|
|
133
|
-
yield (0, exit_1.gracefulExit)();
|
|
134
|
-
}));
|
|
6
|
+
.description("[deprecated] Use 'gmax log' instead")
|
|
7
|
+
.allowUnknownOption(true)
|
|
8
|
+
.allowExcessArguments(true)
|
|
9
|
+
.action(() => {
|
|
10
|
+
console.error("gmax recent is deprecated; use 'gmax log <path-or-symbol>' instead");
|
|
11
|
+
process.exitCode = 1;
|
|
12
|
+
});
|
package/dist/index.js
CHANGED
|
@@ -52,6 +52,7 @@ const index_1 = require("./commands/index");
|
|
|
52
52
|
const investigate_1 = require("./commands/investigate");
|
|
53
53
|
const list_1 = require("./commands/list");
|
|
54
54
|
const llm_1 = require("./commands/llm");
|
|
55
|
+
const log_1 = require("./commands/log");
|
|
55
56
|
const mcp_1 = require("./commands/mcp");
|
|
56
57
|
const peek_1 = require("./commands/peek");
|
|
57
58
|
const project_1 = require("./commands/project");
|
|
@@ -115,6 +116,7 @@ commander_1.program.addCommand(extract_1.extract);
|
|
|
115
116
|
commander_1.program.addCommand(peek_1.peek);
|
|
116
117
|
commander_1.program.addCommand(project_1.project);
|
|
117
118
|
commander_1.program.addCommand(related_1.related);
|
|
119
|
+
commander_1.program.addCommand(log_1.log);
|
|
118
120
|
commander_1.program.addCommand(recent_1.recent);
|
|
119
121
|
commander_1.program.addCommand(diff_1.diff);
|
|
120
122
|
commander_1.program.addCommand(test_find_1.testFind);
|
package/dist/lib/utils/git.js
CHANGED
|
@@ -37,6 +37,7 @@ exports.isWorktree = isWorktree;
|
|
|
37
37
|
exports.getGitCommonDir = getGitCommonDir;
|
|
38
38
|
exports.getMainRepoRoot = getMainRepoRoot;
|
|
39
39
|
exports.getChangedFiles = getChangedFiles;
|
|
40
|
+
exports.getCommitHistory = getCommitHistory;
|
|
40
41
|
exports.getUntrackedFiles = getUntrackedFiles;
|
|
41
42
|
const node_child_process_1 = require("node:child_process");
|
|
42
43
|
const fs = __importStar(require("node:fs"));
|
|
@@ -115,6 +116,95 @@ function getChangedFiles(ref, cwd) {
|
|
|
115
116
|
return [];
|
|
116
117
|
}
|
|
117
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Get commit history for one or more paths. When paths.length > 1 (symbol
|
|
121
|
+
* fan-out), git natively dedupes commits across paths. --follow only works
|
|
122
|
+
* with a single path; auto-disabled otherwise.
|
|
123
|
+
*/
|
|
124
|
+
function getCommitHistory(opts) {
|
|
125
|
+
var _a;
|
|
126
|
+
if (opts.paths.length === 0)
|
|
127
|
+
return [];
|
|
128
|
+
const args = [
|
|
129
|
+
"log",
|
|
130
|
+
"--pretty=format:%x1e%H%x1f%aN%x1f%aI%x1f%ar%x1f%s",
|
|
131
|
+
"--numstat",
|
|
132
|
+
];
|
|
133
|
+
if (opts.follow && opts.paths.length === 1)
|
|
134
|
+
args.push("--follow");
|
|
135
|
+
if (opts.limit > 0)
|
|
136
|
+
args.push(`-n${opts.limit}`);
|
|
137
|
+
if (opts.since)
|
|
138
|
+
args.push(`--since=${opts.since}`);
|
|
139
|
+
if (opts.author)
|
|
140
|
+
args.push(`--author=${opts.author}`);
|
|
141
|
+
if (opts.from)
|
|
142
|
+
args.push(`${opts.from}..HEAD`);
|
|
143
|
+
args.push("--");
|
|
144
|
+
for (const p of opts.paths)
|
|
145
|
+
args.push(p);
|
|
146
|
+
const execOpts = {
|
|
147
|
+
cwd: (_a = opts.cwd) !== null && _a !== void 0 ? _a : process.cwd(),
|
|
148
|
+
encoding: "utf-8",
|
|
149
|
+
timeout: 10000,
|
|
150
|
+
maxBuffer: 16 * 1024 * 1024,
|
|
151
|
+
};
|
|
152
|
+
let output;
|
|
153
|
+
try {
|
|
154
|
+
output = (0, node_child_process_1.execFileSync)("git", args, execOpts);
|
|
155
|
+
}
|
|
156
|
+
catch (_b) {
|
|
157
|
+
return [];
|
|
158
|
+
}
|
|
159
|
+
const records = output.split("\x1e").filter((r) => r.length > 0);
|
|
160
|
+
const commits = [];
|
|
161
|
+
for (const record of records) {
|
|
162
|
+
const lines = record.split("\n");
|
|
163
|
+
if (lines.length === 0)
|
|
164
|
+
continue;
|
|
165
|
+
const headerFields = lines[0].split("\x1f");
|
|
166
|
+
if (headerFields.length < 5)
|
|
167
|
+
continue;
|
|
168
|
+
const [hash, author, isoDate, relDate, subject] = headerFields;
|
|
169
|
+
const numstatLines = [];
|
|
170
|
+
let insertions = 0;
|
|
171
|
+
let deletions = 0;
|
|
172
|
+
for (let i = 1; i < lines.length; i++) {
|
|
173
|
+
const line = lines[i].trim();
|
|
174
|
+
if (!line)
|
|
175
|
+
continue;
|
|
176
|
+
const parts = line.split("\t");
|
|
177
|
+
if (parts.length < 3)
|
|
178
|
+
continue;
|
|
179
|
+
// Binary diffs report '-' for added/removed; treat as 0.
|
|
180
|
+
const added = parts[0] === "-" ? 0 : Number.parseInt(parts[0], 10);
|
|
181
|
+
const removed = parts[1] === "-" ? 0 : Number.parseInt(parts[1], 10);
|
|
182
|
+
const path = parts.slice(2).join("\t");
|
|
183
|
+
if (Number.isFinite(added))
|
|
184
|
+
insertions += added;
|
|
185
|
+
if (Number.isFinite(removed))
|
|
186
|
+
deletions += removed;
|
|
187
|
+
numstatLines.push({
|
|
188
|
+
added: Number.isFinite(added) ? added : 0,
|
|
189
|
+
removed: Number.isFinite(removed) ? removed : 0,
|
|
190
|
+
path,
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
commits.push({
|
|
194
|
+
hash,
|
|
195
|
+
shortHash: hash.slice(0, 7),
|
|
196
|
+
author,
|
|
197
|
+
isoDate,
|
|
198
|
+
relDate,
|
|
199
|
+
subject,
|
|
200
|
+
filesChanged: numstatLines.length,
|
|
201
|
+
insertions,
|
|
202
|
+
deletions,
|
|
203
|
+
numstatLines,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
return commits;
|
|
207
|
+
}
|
|
118
208
|
/**
|
|
119
209
|
* Get untracked files (not yet added to git).
|
|
120
210
|
* Returns absolute paths.
|
package/package.json
CHANGED
|
@@ -125,9 +125,18 @@ gmax related src/lib/index/syncer.ts # dependencies + dependents
|
|
|
125
125
|
gmax related src/lib/index/syncer.ts --root ~/project
|
|
126
126
|
```
|
|
127
127
|
|
|
128
|
-
###
|
|
128
|
+
### Commit history — `gmax log <path-or-symbol>`
|
|
129
129
|
```
|
|
130
|
-
gmax
|
|
130
|
+
gmax log src/lib/auth.ts # commits touching this file
|
|
131
|
+
gmax log src/lib/ # commits touching this directory
|
|
132
|
+
gmax log handleAuth # commits across all files defining the symbol
|
|
133
|
+
gmax log handleAuth --in src/ # restrict symbol resolution to a sub-path
|
|
134
|
+
gmax log src/lib/auth.ts --limit 5 # last 5 commits
|
|
135
|
+
gmax log src/lib/auth.ts --from main # commits since main (range main..HEAD)
|
|
136
|
+
gmax log src/lib/auth.ts --since "2 weeks ago"
|
|
137
|
+
gmax log src/lib/auth.ts --author Robert
|
|
138
|
+
gmax log src/lib/auth.ts --no-follow # disable rename tracking (default: on for files)
|
|
139
|
+
gmax log src/lib/auth.ts --agent # TSV: hash\tisoDate\tauthor\tsubject\tfilesChanged\tins\tdel\ttouchedFiles
|
|
131
140
|
```
|
|
132
141
|
|
|
133
142
|
### Symbols — `gmax symbols`
|
|
@@ -137,15 +146,6 @@ gmax symbols auth -p src/ --root ~/proj # filter by name, path, project
|
|
|
137
146
|
gmax symbols --agent # compact: symbol\tpath:line\tcount
|
|
138
147
|
```
|
|
139
148
|
|
|
140
|
-
### Diff — `gmax diff [ref]`
|
|
141
|
-
```
|
|
142
|
-
gmax diff # uncommitted changes
|
|
143
|
-
gmax diff HEAD~5 # last 5 commits
|
|
144
|
-
gmax diff main # branch changes vs main
|
|
145
|
-
gmax diff main --query "auth changes" # semantic search within changed files
|
|
146
|
-
gmax diff --agent # compact output
|
|
147
|
-
```
|
|
148
|
-
|
|
149
149
|
### Test — `gmax test <symbol|file>`
|
|
150
150
|
```
|
|
151
151
|
gmax test handleAuth # tests calling handleAuth
|