agen-vektor 0.3.12 → 0.3.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.
@@ -36,8 +36,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.createSearchTools = createSearchTools;
37
37
  /**
38
38
  * Search tools — search files by name or content.
39
- * Implementation uses ripgrep (rg) when available, else a pure-Node
39
+ * Implementation uses ripgrep when available, else a pure-Node
40
40
  * recursive walk with string matching (slow but dependency-free).
41
+ *
42
+ * Ripgrep resolution order:
43
+ * 1. @vscode/ripgrep bundled binary (optionalDependency — ships the rg
44
+ * binary in the npm tarball via per-platform optionalDependencies, so
45
+ * `npm i -g agen-vektor` auto-installs it with NO install scripts,
46
+ * safe under npm 12's allowScripts=off default; skipped silently on
47
+ * exotic platforms, where the fallback below takes over)
48
+ * 2. `rg` found on PATH
49
+ * 3. pure-Node fallback (always works)
41
50
  */
42
51
  const fs = __importStar(require("node:fs"));
43
52
  const path = __importStar(require("node:path"));
@@ -45,17 +54,40 @@ const node_child_process_1 = require("node:child_process");
45
54
  const node_util_1 = require("node:util");
46
55
  const execFileP = (0, node_util_1.promisify)(node_child_process_1.execFile);
47
56
  const IGNORE = new Set(['.git', 'node_modules', 'vendor', 'dist', 'build', '.cache', '.env', '.next', 'target', 'coverage']);
48
- function hasRg() {
57
+ /** Bundled ripgrep from @vscode/ripgrep, when the optional dep installed. */
58
+ function bundledRgPath() {
49
59
  try {
50
- execFileSyncCheck('rg', ['--version']);
51
- return true;
60
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
61
+ const { rgPath } = require('@vscode/ripgrep');
62
+ if (rgPath && fs.existsSync(rgPath))
63
+ return rgPath;
52
64
  }
53
65
  catch {
54
- return false;
66
+ /* optional dep not installed (npm 12 script-skip / exotic platform) */
55
67
  }
68
+ return null;
69
+ }
70
+ /** Probe a candidate rg binary (bundled path or PATH lookup). */
71
+ function probeRg(cmd) {
72
+ const res = (0, node_child_process_1.spawnSync)(cmd, ['--version'], { stdio: 'ignore' });
73
+ return !res.error;
74
+ }
75
+ function hasRg() {
76
+ // NOTE: spawnSync does NOT throw when the binary is missing — it returns
77
+ // { error: ENOENT }. A try/catch here never fired, so the old check
78
+ // wrongly returned true on machines without ripgrep and every search
79
+ // died with "spawn rg ENOENT" instead of using the pure-Node fallback.
80
+ const bundled = bundledRgPath();
81
+ if (bundled && probeRg(bundled))
82
+ return true;
83
+ return probeRg('rg');
56
84
  }
57
- function execFileSyncCheck(cmd, args) {
58
- (0, node_child_process_1.spawnSync)(cmd, args, { stdio: 'ignore' });
85
+ /** Which rg binary to actually spawn (bundled first, then PATH). */
86
+ function rgCommand() {
87
+ const bundled = bundledRgPath();
88
+ if (bundled && probeRg(bundled))
89
+ return bundled;
90
+ return 'rg';
59
91
  }
60
92
  function walkFiles(dir, out, max) {
61
93
  let entries;
@@ -114,9 +146,16 @@ function createSearchTools() {
114
146
  }
115
147
  async function runRg(base, query, filename, max) {
116
148
  try {
117
- const args = ['--no-ignore', '-m', '1', '--max-count', '3'];
149
+ // NOTE: --max-count must appear exactly once. Passing both "-m 1" and
150
+ // "--max-count 3" makes ripgrep exit with code 2 ("The argument
151
+ // '--max-count' was provided more than once") — a bug that silently
152
+ // broke every search when rg was installed.
153
+ const args = ['--no-ignore', '--line-number', '--max-count', '3'];
154
+ // "**/<dir>/**" so exclusion works even when the search base is an
155
+ // absolute path outside the rg process cwd (plain "!dir/**" only
156
+ // matches relative to cwd — bit us with tmpdir-based tests).
118
157
  for (const ig of IGNORE)
119
- args.push('-g', `!${ig}/**`);
158
+ args.push('-g', `!**/${ig}/**`);
120
159
  if (filename) {
121
160
  args.push('-g', filename.includes('*') ? filename : `*${filename}*`);
122
161
  }
@@ -124,9 +163,12 @@ async function runRg(base, query, filename, max) {
124
163
  args.push('--', query, base);
125
164
  }
126
165
  else {
127
- args.push('-l', '--', '', base);
166
+ // Filename-only search: --files lists matching paths. (The previous
167
+ // empty-pattern hack "rg -l -- '' <dir>" matched nothing, so
168
+ // filename search never returned results.)
169
+ args.push('--files', base);
128
170
  }
129
- const { stdout } = await execFileP('rg', args, { maxBuffer: 10 * 1024 * 1024 });
171
+ const { stdout } = await execFileP(rgCommand(), args, { maxBuffer: 10 * 1024 * 1024 });
130
172
  const lines = stdout.split('\n').filter(Boolean).slice(0, max);
131
173
  if (lines.length === 0) {
132
174
  return { output: 'no matches found', summary: 'search: no matches' };
@@ -167,7 +209,15 @@ function fallbackSearch(base, query, filename, max, cwd) {
167
209
  try {
168
210
  const content = fs.readFileSync(f, 'utf8');
169
211
  if (content.includes(query)) {
170
- results.push(rel);
212
+ // Include line numbers like ripgrep does — the tool description
213
+ // promises "line numbers for text search".
214
+ const lines = content.split('\n');
215
+ const hits = [];
216
+ for (let i = 0; i < lines.length && hits.length < 3; i++) {
217
+ if (lines[i].includes(query))
218
+ hits.push(`${rel}:${i + 1}`);
219
+ }
220
+ results.push(hits.length > 0 ? hits.join('\n') : rel);
171
221
  }
172
222
  }
173
223
  catch {