grepmax 0.16.0 → 0.16.2

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.
@@ -44,13 +44,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
45
  exports.context = void 0;
46
46
  const fs = __importStar(require("node:fs"));
47
- const path = __importStar(require("node:path"));
48
47
  const commander_1 = require("commander");
49
48
  const searcher_1 = require("../lib/search/searcher");
50
49
  const skeleton_1 = require("../lib/skeleton");
51
50
  const vector_db_1 = require("../lib/store/vector-db");
52
51
  const filter_builder_1 = require("../lib/utils/filter-builder");
53
52
  const exit_1 = require("../lib/utils/exit");
53
+ const project_registry_1 = require("../lib/utils/project-registry");
54
54
  const project_root_1 = require("../lib/utils/project-root");
55
55
  const arrow_1 = require("../lib/utils/arrow");
56
56
  function estimateTokens(text) {
@@ -69,7 +69,9 @@ exports.context = new commander_1.Command("context")
69
69
  const maxResults = Number.parseInt(opts.maxResults || "10", 10) || 10;
70
70
  let vectorDb = null;
71
71
  try {
72
- const root = opts.root ? path.resolve(opts.root) : process.cwd();
72
+ const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
73
+ if (root === null)
74
+ return;
73
75
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
74
76
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
75
77
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
@@ -1,37 +1,4 @@
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
2
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
36
3
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
37
4
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -43,13 +10,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
43
10
  };
44
11
  Object.defineProperty(exports, "__esModule", { value: true });
45
12
  exports.diff = void 0;
46
- const path = __importStar(require("node:path"));
47
13
  const commander_1 = require("commander");
48
14
  const searcher_1 = require("../lib/search/searcher");
49
15
  const vector_db_1 = require("../lib/store/vector-db");
50
16
  const filter_builder_1 = require("../lib/utils/filter-builder");
51
17
  const exit_1 = require("../lib/utils/exit");
52
18
  const git_1 = require("../lib/utils/git");
19
+ const project_registry_1 = require("../lib/utils/project-registry");
53
20
  const project_root_1 = require("../lib/utils/project-root");
54
21
  const arrow_1 = require("../lib/utils/arrow");
55
22
  exports.diff = new commander_1.Command("diff")
@@ -65,7 +32,9 @@ exports.diff = new commander_1.Command("diff")
65
32
  const limit = Math.min(Math.max(Number.parseInt(opts.maxCount || "10", 10), 1), 50);
66
33
  let vectorDb = null;
67
34
  try {
68
- const root = opts.root ? path.resolve(opts.root) : process.cwd();
35
+ const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
36
+ if (root === null)
37
+ return;
69
38
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
70
39
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
71
40
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
@@ -44,12 +44,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
45
  exports.extract = void 0;
46
46
  const fs = __importStar(require("node:fs"));
47
- const path = __importStar(require("node:path"));
48
47
  const commander_1 = require("commander");
49
48
  const vector_db_1 = require("../lib/store/vector-db");
50
49
  const exit_1 = require("../lib/utils/exit");
51
50
  const filter_builder_1 = require("../lib/utils/filter-builder");
52
51
  const import_extractor_1 = require("../lib/utils/import-extractor");
52
+ const language_1 = require("../lib/utils/language");
53
+ const project_registry_1 = require("../lib/utils/project-registry");
53
54
  const project_root_1 = require("../lib/utils/project-root");
54
55
  const useColors = process.stdout.isTTY && !process.env.NO_COLOR;
55
56
  const style = {
@@ -63,10 +64,9 @@ const ROLE_PRIORITY = {
63
64
  DEFINITION: 2,
64
65
  IMPLEMENTATION: 1,
65
66
  };
66
- function findSymbolChunks(symbol, db, projectRoot) {
67
+ function findSymbolChunks(db, whereClause) {
67
68
  return __awaiter(this, void 0, void 0, function* () {
68
69
  const table = yield db.ensureTable();
69
- const prefix = projectRoot.endsWith("/") ? projectRoot : `${projectRoot}/`;
70
70
  const rows = yield table
71
71
  .query()
72
72
  .select([
@@ -77,7 +77,7 @@ function findSymbolChunks(symbol, db, projectRoot) {
77
77
  "is_exported",
78
78
  "defined_symbols",
79
79
  ])
80
- .where(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(symbol)}') AND path LIKE '${(0, filter_builder_1.escapeSqlString)(prefix)}%'`)
80
+ .where(whereClause)
81
81
  .limit(10)
82
82
  .toArray();
83
83
  return rows.map((row) => ({
@@ -106,17 +106,28 @@ exports.extract = new commander_1.Command("extract")
106
106
  .description("Extract full function/class body by symbol name")
107
107
  .argument("<symbol>", "The symbol to extract")
108
108
  .option("--root <dir>", "Project root directory")
109
+ .option("--in <subpath>", "Restrict to a sub-path of the project (repeatable)", (value, prev) => (prev ? [...prev, value] : [value]))
110
+ .option("--exclude <subpath>", "Exclude a sub-path of the project (repeatable)", (value, prev) => (prev ? [...prev, value] : [value]))
109
111
  .option("--agent", "Compact output for AI agents", false)
110
112
  .option("--imports", "Prepend file imports", false)
111
113
  .action((symbol, opts) => __awaiter(void 0, void 0, void 0, function* () {
112
114
  var _a;
113
115
  let vectorDb = null;
114
- const root = opts.root ? path.resolve(opts.root) : process.cwd();
116
+ const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
117
+ if (root === null)
118
+ return;
115
119
  try {
116
120
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
117
121
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
118
122
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
119
- const chunks = yield findSymbolChunks(symbol, vectorDb, projectRoot);
123
+ const { resolveScope, buildScopeWhere } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/scope-filter")));
124
+ const scope = resolveScope({
125
+ projectRoot,
126
+ in: opts.in,
127
+ exclude: opts.exclude,
128
+ });
129
+ const where = buildScopeWhere(scope, `array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(symbol)}')`);
130
+ const chunks = yield findSymbolChunks(vectorDb, where);
120
131
  if (chunks.length === 0) {
121
132
  const lines = [
122
133
  `Symbol not found: ${opts.agent ? symbol : style.bold(symbol)}`,
@@ -128,6 +139,25 @@ exports.extract = new commander_1.Command("extract")
128
139
  process.exitCode = 1;
129
140
  return;
130
141
  }
142
+ // Cross-language disambiguation: when the symbol is defined in 2+
143
+ // languages, refuse to silently pick one. Listing all matches with a
144
+ // recovery hint avoids the dogfooded failure mode where peek picked
145
+ // Swift but listed TS callers.
146
+ const byLang = (0, language_1.groupByLanguage)(chunks);
147
+ if (byLang.size >= 2) {
148
+ const rel = (p) => p.startsWith(projectRoot) ? p.slice(projectRoot.length + 1) : p;
149
+ const lines = [
150
+ `Symbol '${symbol}' is defined in multiple languages:`,
151
+ ];
152
+ for (const [lang, group] of byLang) {
153
+ const c = group[0];
154
+ lines.push(` ${lang.padEnd(6)} ${rel(c.path)}:${c.startLine + 1}`);
155
+ }
156
+ lines.push(`Disambiguate with --root or pin to a path: gmax extract ${symbol} --root <project-root>`);
157
+ console.log(lines.join("\n"));
158
+ process.exitCode = 1;
159
+ return;
160
+ }
131
161
  const best = pickBestMatch(chunks, symbol);
132
162
  const content = fs.readFileSync(best.path, "utf-8");
133
163
  const allLines = content.split("\n");
@@ -48,19 +48,24 @@ const commander_1 = require("commander");
48
48
  const impact_1 = require("../lib/graph/impact");
49
49
  const vector_db_1 = require("../lib/store/vector-db");
50
50
  const exit_1 = require("../lib/utils/exit");
51
+ const project_registry_1 = require("../lib/utils/project-registry");
51
52
  const project_root_1 = require("../lib/utils/project-root");
52
53
  exports.impact = new commander_1.Command("impact")
53
54
  .description("Analyze change impact: dependents and affected tests")
54
55
  .argument("<target>", "Symbol name or file path")
55
56
  .option("-d, --depth <n>", "Caller traversal depth (default 1, max 3)", "1")
56
57
  .option("--root <dir>", "Project root directory")
58
+ .option("--in <subpath>", "Restrict to a sub-path of the project (repeatable)", (value, prev) => (prev ? [...prev, value] : [value]))
59
+ .option("--exclude <subpath>", "Exclude a sub-path of the project (repeatable)", (value, prev) => (prev ? [...prev, value] : [value]))
57
60
  .option("--agent", "Compact output for AI agents", false)
58
61
  .action((target, opts) => __awaiter(void 0, void 0, void 0, function* () {
59
62
  var _a;
60
63
  const depth = Math.min(Math.max(Number.parseInt(opts.depth || "1", 10), 1), 3);
61
64
  let vectorDb = null;
62
65
  try {
63
- const root = opts.root ? path.resolve(opts.root) : process.cwd();
66
+ const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
67
+ if (root === null)
68
+ return;
64
69
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
65
70
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
66
71
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
@@ -77,10 +82,23 @@ exports.impact = new commander_1.Command("impact")
77
82
  ? (target.startsWith("/") ? target : path.resolve(projectRoot, target))
78
83
  : undefined;
79
84
  const excludePaths = targetPath ? new Set([targetPath]) : undefined;
85
+ const { resolveScope } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/scope-filter")));
86
+ const scope = resolveScope({
87
+ projectRoot,
88
+ in: opts.in,
89
+ exclude: opts.exclude,
90
+ });
91
+ // Treat --in as an exclude-everything-else when set: any prefix that
92
+ // isn't the --in scope becomes effectively excluded. Today findDependents
93
+ // always queries within projectRoot; passing scope.pathPrefix when --in
94
+ // is set narrows it. Reuse the existing projectRoot semantic when no --in.
95
+ const queryRoot = opts.in && opts.in.length > 0
96
+ ? scope.pathPrefix.replace(/\/$/, "")
97
+ : projectRoot;
80
98
  // Run dependents and tests in parallel
81
99
  const [dependents, tests] = yield Promise.all([
82
- (0, impact_1.findDependents)(symbols, vectorDb, projectRoot, excludePaths),
83
- (0, impact_1.findTests)(symbols, vectorDb, projectRoot, depth),
100
+ (0, impact_1.findDependents)(symbols, vectorDb, queryRoot, excludePaths, undefined, scope.excludePrefixes),
101
+ (0, impact_1.findTests)(symbols, vectorDb, queryRoot, depth, scope.excludePrefixes),
84
102
  ]);
85
103
  // Separate test files from non-test dependents
86
104
  const nonTestDeps = dependents.filter((d) => !(0, impact_1.isTestPath)(d.file));
@@ -43,9 +43,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
43
43
  };
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
45
  exports.investigateCmd = void 0;
46
- const path = __importStar(require("node:path"));
47
46
  const commander_1 = require("commander");
48
47
  const exit_1 = require("../lib/utils/exit");
48
+ const project_registry_1 = require("../lib/utils/project-registry");
49
49
  const project_root_1 = require("../lib/utils/project-root");
50
50
  exports.investigateCmd = new commander_1.Command("investigate")
51
51
  .description("Ask a question about the codebase using local LLM + gmax tools")
@@ -62,7 +62,9 @@ Examples:
62
62
  .action((question, opts) => __awaiter(void 0, void 0, void 0, function* () {
63
63
  var _a;
64
64
  try {
65
- const root = opts.root ? path.resolve(opts.root) : process.cwd();
65
+ const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
66
+ if (root === null)
67
+ return;
66
68
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
67
69
  const maxRounds = Math.min(Math.max(Number.parseInt(opts.rounds || "10", 10), 1), 20);
68
70
  // Ensure LLM server is running
@@ -44,12 +44,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
44
44
  Object.defineProperty(exports, "__esModule", { value: true });
45
45
  exports.peek = void 0;
46
46
  const fs = __importStar(require("node:fs"));
47
- const path = __importStar(require("node:path"));
48
47
  const commander_1 = require("commander");
49
48
  const graph_builder_1 = require("../lib/graph/graph-builder");
50
49
  const vector_db_1 = require("../lib/store/vector-db");
51
50
  const exit_1 = require("../lib/utils/exit");
52
51
  const filter_builder_1 = require("../lib/utils/filter-builder");
52
+ const language_1 = require("../lib/utils/language");
53
+ const project_registry_1 = require("../lib/utils/project-registry");
53
54
  const project_root_1 = require("../lib/utils/project-root");
54
55
  const useColors = process.stdout.isTTY && !process.env.NO_COLOR;
55
56
  const style = {
@@ -94,17 +95,60 @@ exports.peek = new commander_1.Command("peek")
94
95
  .argument("<symbol>", "The symbol to peek at")
95
96
  .option("-d, --depth <n>", "Caller traversal depth (default 1, max 3)", "1")
96
97
  .option("--root <dir>", "Project root directory")
98
+ .option("--in <subpath>", "Restrict to a sub-path of the project (repeatable)", (value, prev) => (prev ? [...prev, value] : [value]))
99
+ .option("--exclude <subpath>", "Exclude a sub-path of the project (repeatable)", (value, prev) => (prev ? [...prev, value] : [value]))
97
100
  .option("--agent", "Compact output for AI agents", false)
98
101
  .action((symbol, opts) => __awaiter(void 0, void 0, void 0, function* () {
99
102
  var _a;
100
103
  let vectorDb = null;
101
- const root = opts.root ? path.resolve(opts.root) : process.cwd();
104
+ const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
105
+ if (root === null)
106
+ return;
102
107
  const depth = Math.min(Math.max(Number.parseInt(opts.depth || "1", 10), 1), 3);
103
108
  try {
104
109
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
105
110
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
106
111
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
107
- const graphBuilder = new graph_builder_1.GraphBuilder(vectorDb, projectRoot);
112
+ const { resolveScope, buildScopeWhere } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/scope-filter")));
113
+ const scope = resolveScope({
114
+ projectRoot,
115
+ in: opts.in,
116
+ exclude: opts.exclude,
117
+ });
118
+ const scopeWhere = (cond) => buildScopeWhere(scope, cond);
119
+ // Cross-language disambiguation: when the symbol is defined in 2+
120
+ // languages, refuse to silently pick one. The graph builder otherwise
121
+ // picks one chunk arbitrarily and lists callers from a different
122
+ // language — verified failure mode.
123
+ {
124
+ const tableForCheck = yield vectorDb.ensureTable();
125
+ const allDefs = yield tableForCheck
126
+ .query()
127
+ .select(["path", "start_line"])
128
+ .where(scopeWhere(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(symbol)}')`))
129
+ .limit(20)
130
+ .toArray();
131
+ const chunks = allDefs.map((row) => ({
132
+ path: String(row.path || ""),
133
+ startLine: Number(row.start_line || 0),
134
+ }));
135
+ const byLang = (0, language_1.groupByLanguage)(chunks);
136
+ if (byLang.size >= 2) {
137
+ const rel = (p) => p.startsWith(projectRoot) ? p.slice(projectRoot.length + 1) : p;
138
+ const lines = [
139
+ `Symbol '${symbol}' is defined in multiple languages:`,
140
+ ];
141
+ for (const [lang, group] of byLang) {
142
+ const c = group[0];
143
+ lines.push(` ${lang.padEnd(6)} ${rel(c.path)}:${c.startLine + 1}`);
144
+ }
145
+ lines.push(`Disambiguate with --root or pin to a path: gmax peek ${symbol} --root <project-root>`);
146
+ console.log(lines.join("\n"));
147
+ process.exitCode = 1;
148
+ return;
149
+ }
150
+ }
151
+ const graphBuilder = new graph_builder_1.GraphBuilder(vectorDb, scope.pathPrefix, scope.excludePrefixes);
108
152
  const graph = yield graphBuilder.buildGraph(symbol);
109
153
  if (!graph.center) {
110
154
  const lines = [
@@ -123,13 +167,10 @@ exports.peek = new commander_1.Command("peek")
123
167
  : p;
124
168
  // Get chunk metadata for is_exported and end_line
125
169
  const table = yield vectorDb.ensureTable();
126
- const prefix = projectRoot.endsWith("/")
127
- ? projectRoot
128
- : `${projectRoot}/`;
129
170
  const metaRows = yield table
130
171
  .query()
131
172
  .select(["is_exported", "start_line", "end_line"])
132
- .where(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(symbol)}') AND path LIKE '${(0, filter_builder_1.escapeSqlString)(prefix)}%'`)
173
+ .where(scopeWhere(`array_contains(defined_symbols, '${(0, filter_builder_1.escapeSqlString)(symbol)}')`))
133
174
  .limit(1)
134
175
  .toArray();
135
176
  const exported = metaRows.length > 0 && Boolean(metaRows[0].is_exported);
@@ -56,12 +56,13 @@ exports.project = new commander_1.Command("project")
56
56
  .option("--root <dir>", "Project root (defaults to current directory)")
57
57
  .option("--agent", "Compact output for AI agents", false)
58
58
  .action((opts) => __awaiter(void 0, void 0, void 0, function* () {
59
- var _a, _b, _c, _d;
59
+ var _a, _b, _c;
60
60
  let vectorDb = null;
61
61
  try {
62
- const root = opts.root
63
- ? (_a = (0, project_root_1.findProjectRoot)(path.resolve(opts.root))) !== null && _a !== void 0 ? _a : path.resolve(opts.root)
64
- : (_b = (0, project_root_1.findProjectRoot)(process.cwd())) !== null && _b !== void 0 ? _b : process.cwd();
62
+ const resolvedRoot = (0, project_registry_1.resolveRootOrExit)(opts.root);
63
+ if (resolvedRoot === null)
64
+ return;
65
+ const root = (_a = (0, project_root_1.findProjectRoot)(resolvedRoot)) !== null && _a !== void 0 ? _a : resolvedRoot;
65
66
  const prefix = root.endsWith("/") ? root : `${root}/`;
66
67
  const projectName = path.basename(root);
67
68
  const paths = (0, project_root_1.ensureProjectPaths)(root);
@@ -136,7 +137,7 @@ exports.project = new commander_1.Command("project")
136
137
  console.log(`root\t${root}`);
137
138
  console.log(`chunks\t${rows.length}`);
138
139
  console.log(`files\t${files.size}`);
139
- console.log(`last_indexed\t${(_c = proj === null || proj === void 0 ? void 0 : proj.lastIndexed) !== null && _c !== void 0 ? _c : "unknown"}`);
140
+ console.log(`last_indexed\t${(_b = proj === null || proj === void 0 ? void 0 : proj.lastIndexed) !== null && _b !== void 0 ? _b : "unknown"}`);
140
141
  console.log(`languages\t${extEntries.map(([ext]) => ext).join(",")}`);
141
142
  console.log(`top_dirs\t${Array.from(dirCounts.entries()).sort((a, b) => b[1].chunks - a[1].chunks).slice(0, 8).map(([d]) => d).join(",")}`);
142
143
  if (topSymbols.length > 0) {
@@ -148,7 +149,7 @@ exports.project = new commander_1.Command("project")
148
149
  }
149
150
  else {
150
151
  console.log(`Project: ${projectName} (${root})`);
151
- console.log(`Last indexed: ${(_d = proj === null || proj === void 0 ? void 0 : proj.lastIndexed) !== null && _d !== void 0 ? _d : "unknown"} • ${rows.length} chunks • ${files.size} files\n`);
152
+ console.log(`Last indexed: ${(_c = proj === null || proj === void 0 ? void 0 : proj.lastIndexed) !== null && _c !== void 0 ? _c : "unknown"} • ${rows.length} chunks • ${files.size} files\n`);
152
153
  console.log(`Languages: ${extEntries.map(([ext, count]) => `${ext} (${Math.round((count / rows.length) * 100)}%)`).join(", ")}\n`);
153
154
  console.log("Directory structure:");
154
155
  for (const [dir, data] of Array.from(dirCounts.entries())
@@ -182,7 +183,7 @@ exports.project = new commander_1.Command("project")
182
183
  try {
183
184
  yield vectorDb.close();
184
185
  }
185
- catch (_e) { }
186
+ catch (_d) { }
186
187
  }
187
188
  yield (0, exit_1.gracefulExit)();
188
189
  }
@@ -56,6 +56,7 @@ const config_1 = require("../config");
56
56
  const meta_cache_1 = require("../lib/store/meta-cache");
57
57
  const exit_1 = require("../lib/utils/exit");
58
58
  const format_helpers_1 = require("../lib/utils/format-helpers");
59
+ const project_registry_1 = require("../lib/utils/project-registry");
59
60
  const project_root_1 = require("../lib/utils/project-root");
60
61
  exports.recent = new commander_1.Command("recent")
61
62
  .description("Show recently modified indexed files")
@@ -64,20 +65,21 @@ exports.recent = new commander_1.Command("recent")
64
65
  .option("--agent", "Compact output for AI agents", false)
65
66
  .action((opts) => __awaiter(void 0, void 0, void 0, function* () {
66
67
  var _a, e_1, _b, _c;
67
- var _d, _e;
68
+ var _d;
68
69
  const limit = Math.min(Math.max(Number.parseInt(opts.limit || "20", 10), 1), 50);
69
70
  try {
70
- const root = opts.root
71
- ? (_d = (0, project_root_1.findProjectRoot)(path.resolve(opts.root))) !== null && _d !== void 0 ? _d : path.resolve(opts.root)
72
- : (_e = (0, project_root_1.findProjectRoot)(process.cwd())) !== null && _e !== void 0 ? _e : process.cwd();
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;
73
75
  const prefix = root.endsWith("/") ? root : `${root}/`;
74
76
  const metaCache = new meta_cache_1.MetaCache(config_1.PATHS.lmdbPath);
75
77
  try {
76
78
  const files = [];
77
79
  try {
78
- for (var _f = true, _g = __asyncValues(metaCache.entries()), _h; _h = yield _g.next(), _a = _h.done, !_a; _f = true) {
79
- _c = _h.value;
80
- _f = false;
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;
81
83
  const { path: p, entry } = _c;
82
84
  if (p.startsWith(prefix)) {
83
85
  files.push({ path: p, mtimeMs: entry.mtimeMs });
@@ -87,7 +89,7 @@ exports.recent = new commander_1.Command("recent")
87
89
  catch (e_1_1) { e_1 = { error: e_1_1 }; }
88
90
  finally {
89
91
  try {
90
- if (!_f && !_a && (_b = _g.return)) yield _b.call(_g);
92
+ if (!_e && !_a && (_b = _f.return)) yield _b.call(_f);
91
93
  }
92
94
  finally { if (e_1) throw e_1.error; }
93
95
  }
@@ -48,6 +48,7 @@ const commander_1 = require("commander");
48
48
  const vector_db_1 = require("../lib/store/vector-db");
49
49
  const filter_builder_1 = require("../lib/utils/filter-builder");
50
50
  const exit_1 = require("../lib/utils/exit");
51
+ const project_registry_1 = require("../lib/utils/project-registry");
51
52
  const project_root_1 = require("../lib/utils/project-root");
52
53
  const arrow_1 = require("../lib/utils/arrow");
53
54
  exports.related = new commander_1.Command("related")
@@ -55,19 +56,29 @@ exports.related = new commander_1.Command("related")
55
56
  .argument("<file>", "File path relative to project root")
56
57
  .option("-l, --limit <n>", "Max results per direction (default 10)", "10")
57
58
  .option("--root <dir>", "Project root directory")
59
+ .option("--in <subpath>", "Restrict to a sub-path of the project (repeatable)", (value, prev) => (prev ? [...prev, value] : [value]))
60
+ .option("--exclude <subpath>", "Exclude a sub-path of the project (repeatable)", (value, prev) => (prev ? [...prev, value] : [value]))
58
61
  .option("--agent", "Compact output for AI agents", false)
59
62
  .action((file, opts) => __awaiter(void 0, void 0, void 0, function* () {
60
63
  var _a;
61
64
  const limit = Math.min(Math.max(Number.parseInt(opts.limit || "10", 10), 1), 25);
62
65
  let vectorDb = null;
63
66
  try {
64
- const root = opts.root ? path.resolve(opts.root) : process.cwd();
67
+ const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
68
+ if (root === null)
69
+ return;
65
70
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
66
71
  const paths = (0, project_root_1.ensureProjectPaths)(projectRoot);
67
72
  vectorDb = new vector_db_1.VectorDB(paths.lancedbDir);
68
73
  const absPath = path.resolve(projectRoot, file);
69
74
  const table = yield vectorDb.ensureTable();
70
- const pathScope = `path LIKE '${(0, filter_builder_1.escapeSqlString)(projectRoot)}/%'`;
75
+ const { resolveScope, buildScopeWhere } = yield Promise.resolve().then(() => __importStar(require("../lib/utils/scope-filter")));
76
+ const scope = resolveScope({
77
+ projectRoot,
78
+ in: opts.in,
79
+ exclude: opts.exclude,
80
+ });
81
+ const pathScope = buildScopeWhere(scope);
71
82
  const fileChunks = yield table
72
83
  .query()
73
84
  .select(["defined_symbols", "referenced_symbols"])
@@ -68,12 +68,13 @@ function confirm(message) {
68
68
  }
69
69
  exports.remove = new commander_1.Command("remove")
70
70
  .description("Remove a project from the gmax index")
71
- .argument("[dir]", "Directory to remove (defaults to current directory)")
71
+ .argument("[dir-or-name]", "Directory or registered project name (defaults to current directory)")
72
72
  .option("-f, --force", "Skip confirmation prompt", false)
73
73
  .addHelpText("after", `
74
74
  Examples:
75
75
  gmax remove Remove the current project
76
- gmax remove ~/projects/app Remove a specific project
76
+ gmax remove ~/projects/app Remove a specific project by path
77
+ gmax remove my-app Remove a registered project by name
77
78
  gmax remove --force Skip confirmation
78
79
  `)
79
80
  .action((dir, opts) => __awaiter(void 0, void 0, void 0, function* () {
@@ -81,7 +82,23 @@ Examples:
81
82
  let vectorDb = null;
82
83
  let metaCache = null;
83
84
  try {
84
- const targetDir = dir ? path.resolve(dir) : process.cwd();
85
+ // Resolve name registered root when arg has no path separator and
86
+ // isn't a dir. Avoids the footgun where a typo'd name silently removed
87
+ // the cwd project.
88
+ let targetDir;
89
+ if (dir) {
90
+ try {
91
+ targetDir = (0, project_registry_1.resolveProjectRoot)(dir);
92
+ }
93
+ catch (err) {
94
+ console.error(err instanceof Error ? err.message : String(err));
95
+ process.exitCode = 1;
96
+ return;
97
+ }
98
+ }
99
+ else {
100
+ targetDir = process.cwd();
101
+ }
85
102
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(targetDir)) !== null && _a !== void 0 ? _a : targetDir;
86
103
  const projectName = path.basename(projectRoot);
87
104
  const project = (0, project_registry_1.getProject)(projectRoot);
@@ -48,6 +48,7 @@ const fs = __importStar(require("node:fs"));
48
48
  const path = __importStar(require("node:path"));
49
49
  const commander_1 = require("commander");
50
50
  const exit_1 = require("../lib/utils/exit");
51
+ const project_registry_1 = require("../lib/utils/project-registry");
51
52
  const project_root_1 = require("../lib/utils/project-root");
52
53
  exports.review = new commander_1.Command("review")
53
54
  .description("Review code changes using local LLM + codebase context")
@@ -69,7 +70,9 @@ Subcommands:
69
70
  .action((opts) => __awaiter(void 0, void 0, void 0, function* () {
70
71
  var _a;
71
72
  try {
72
- const root = opts.root ? path.resolve(opts.root) : process.cwd();
73
+ const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
74
+ if (root === null)
75
+ return;
73
76
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
74
77
  const commitRef = opts.commit;
75
78
  if (opts.background) {
@@ -135,7 +138,9 @@ exports.review
135
138
  .action((opts) => __awaiter(void 0, void 0, void 0, function* () {
136
139
  var _a;
137
140
  try {
138
- const root = opts.root ? path.resolve(opts.root) : process.cwd();
141
+ const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
142
+ if (root === null)
143
+ return;
139
144
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
140
145
  const { readReport, formatReportText } = yield Promise.resolve().then(() => __importStar(require("../lib/llm/report")));
141
146
  const report = readReport(projectRoot);
@@ -166,7 +171,9 @@ exports.review
166
171
  .action((opts) => __awaiter(void 0, void 0, void 0, function* () {
167
172
  var _a;
168
173
  try {
169
- const root = opts.root ? path.resolve(opts.root) : process.cwd();
174
+ const root = (0, project_registry_1.resolveRootOrExit)(opts.root);
175
+ if (root === null)
176
+ return;
170
177
  const projectRoot = (_a = (0, project_root_1.findProjectRoot)(root)) !== null && _a !== void 0 ? _a : root;
171
178
  const { clearReport } = yield Promise.resolve().then(() => __importStar(require("../lib/llm/report")));
172
179
  clearReport(projectRoot);