@neurcode-ai/cli 0.9.21 → 0.9.23

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.
@@ -39,14 +39,21 @@ const STOP_WORDS = new Set([
39
39
  'from', 'your', 'about', 'there', 'their', 'them', 'have', 'does', 'is', 'are', 'was',
40
40
  'were', 'any', 'all', 'read', 'tell', 'me', 'its', 'it', 'instead', 'than', 'then',
41
41
  'workflow', 'codebase', 'repo', 'repository', 'used', 'use', 'mentioned', 'mention', 'whether',
42
+ 'list', 'show', 'like', 'can', 'type', 'types', 'using', 'etc', 'cmd', 'cmds', 'command', 'commands',
43
+ 'system', 'platform', 'latest', 'package', 'packages',
42
44
  ]);
43
45
  const LOW_SIGNAL_TERMS = new Set([
44
46
  'used', 'use', 'using', 'mentioned', 'mention', 'where', 'tell', 'read', 'check', 'find', 'search',
45
- 'workflow', 'repo', 'repository', 'codebase', 'anywhere',
47
+ 'workflow', 'repo', 'repository', 'codebase', 'anywhere', 'can', 'type', 'types', 'list', 'show', 'like',
48
+ 'neurcode', 'cli', 'file', 'files', 'path', 'filepath', 'header', 'added', 'add', 'request', 'requests',
46
49
  ]);
50
+ const GENERIC_OUTPUT_TERMS = new Set(['file', 'files', 'path', 'filepath']);
47
51
  function scanFiles(dir, maxFiles = MAX_SCAN_FILES) {
48
52
  const files = [];
49
- const ignoreDirs = new Set(['node_modules', '.git', '.next', 'dist', 'build', '.turbo', '.cache', 'coverage']);
53
+ const ignoreDirs = new Set([
54
+ 'node_modules', '.git', '.next', 'dist', 'build', '.turbo', '.cache', 'coverage',
55
+ '.neurcode', '.vscode', '.pnpm-store', '.npm', '.yarn',
56
+ ]);
50
57
  const ignoreExts = new Set(['map', 'log', 'lock', 'png', 'jpg', 'jpeg', 'gif', 'ico', 'svg', 'woff', 'woff2', 'ttf', 'eot', 'pdf']);
51
58
  const walk = (current) => {
52
59
  if (files.length >= maxFiles)
@@ -95,7 +102,9 @@ function scanFiles(dir, maxFiles = MAX_SCAN_FILES) {
95
102
  }
96
103
  function tokenizeQuestion(question) {
97
104
  return (0, plan_cache_1.normalizeIntent)(question)
105
+ .replace(/[^a-z0-9_\-\s]/g, ' ')
98
106
  .split(/\s+/)
107
+ .map((token) => token.trim())
99
108
  .filter((token) => token.length >= 3 && !STOP_WORDS.has(token));
100
109
  }
101
110
  function escapeRegExp(input) {
@@ -105,6 +114,7 @@ function normalizeTerm(raw) {
105
114
  return raw
106
115
  .toLowerCase()
107
116
  .replace(/['"`]/g, '')
117
+ .replace(/[^a-z0-9_\-\s]/g, ' ')
108
118
  .replace(/\s+/g, ' ')
109
119
  .trim();
110
120
  }
@@ -178,6 +188,34 @@ function buildTermMatchers(term, weight) {
178
188
  push(normalized, `\\b${escapeRegExp(tokens.join('-'))}\\b`);
179
189
  return out;
180
190
  }
191
+ function expandSearchTerms(terms) {
192
+ const expanded = new Set();
193
+ for (const rawTerm of terms) {
194
+ const term = normalizeTerm(rawTerm);
195
+ if (!term)
196
+ continue;
197
+ expanded.add(term);
198
+ if (term.includes(' ')) {
199
+ continue;
200
+ }
201
+ if (term.endsWith('ies') && term.length > 4) {
202
+ expanded.add(`${term.slice(0, -3)}y`);
203
+ }
204
+ if (term.endsWith('s') && term.length > 3) {
205
+ expanded.add(term.slice(0, -1));
206
+ }
207
+ else if (term.length > 3) {
208
+ expanded.add(`${term}s`);
209
+ }
210
+ if (term.endsWith('ing') && term.length > 5) {
211
+ expanded.add(term.slice(0, -3));
212
+ }
213
+ if (term.endsWith('ed') && term.length > 4) {
214
+ expanded.add(term.slice(0, -2));
215
+ }
216
+ }
217
+ return [...expanded];
218
+ }
181
219
  function buildMatchers(question) {
182
220
  const comparisonTerms = extractComparisonTerms(question);
183
221
  if (comparisonTerms.length >= 2) {
@@ -192,10 +230,12 @@ function buildMatchers(question) {
192
230
  };
193
231
  }
194
232
  const quoted = extractQuotedTerms(question);
233
+ const identityTerms = extractIdentityTerms(question);
195
234
  const keywords = tokenizeQuestion(question).slice(0, 8);
196
235
  const quotedSet = new Set(quoted.map((term) => normalizeTerm(term)));
197
- const terms = [...new Set([...quoted, ...keywords].map(normalizeTerm).filter(Boolean))]
198
- .filter((term) => quotedSet.has(term) || !LOW_SIGNAL_TERMS.has(term));
236
+ const baseTerms = [...new Set([...quoted, ...identityTerms, ...keywords].map(normalizeTerm).filter(Boolean))];
237
+ const filteredTerms = baseTerms.filter((term) => quotedSet.has(term) || !LOW_SIGNAL_TERMS.has(term));
238
+ const terms = expandSearchTerms(filteredTerms.length > 0 ? filteredTerms : baseTerms).filter(Boolean);
199
239
  const matchers = terms.flatMap((term) => buildTermMatchers(term, quoted.includes(term) ? 0.9 : 0.55));
200
240
  return {
201
241
  mode: 'search',
@@ -216,8 +256,29 @@ function derivePathHints(question) {
216
256
  }
217
257
  if (/\bdashboard|landing|docs|frontend|ui|pricing\b/.test(normalized)) {
218
258
  hints.push('web/dashboard/');
259
+ hints.push('docs/');
260
+ hints.push('README.md');
261
+ }
262
+ if (/\bfeature|capabilit|offer|platform\b/.test(normalized)) {
263
+ hints.push('docs/');
264
+ hints.push('README.md');
265
+ }
266
+ if (/\binstall|setup|upgrade|update\b/.test(normalized)) {
267
+ hints.push('README.md');
268
+ hints.push('docs/');
269
+ hints.push('packages/cli/');
270
+ }
271
+ if (/\btenant|tenancy|single|multi|organization|org\b/.test(normalized)) {
272
+ hints.push('services/api/src/lib/');
273
+ hints.push('services/api/src/routes/');
274
+ hints.push('packages/cli/src/');
275
+ }
276
+ if (/\brequest|requests|header|inject|injected\b/.test(normalized)) {
277
+ hints.push('packages/cli/src/api-client.ts');
278
+ hints.push('services/api/src/middleware/');
219
279
  }
220
280
  if (/(github action|\bci\b)/.test(normalized)) {
281
+ hints.push('.github/workflows/');
221
282
  hints.push('packages/action/');
222
283
  hints.push('actions/');
223
284
  }
@@ -230,533 +291,240 @@ function normalizeSnippet(line) {
230
291
  .trim()
231
292
  .slice(0, 220);
232
293
  }
233
- function lineNumberFromOffset(content, offset) {
234
- if (offset <= 0)
235
- return 1;
236
- return content.slice(0, offset).split(/\r?\n/).length;
237
- }
238
- function stripMarkdown(value) {
239
- return value
240
- .replace(/[*_`~]/g, '')
241
- .replace(/\[(.*?)\]\(.*?\)/g, '$1')
242
- .replace(/\s+/g, ' ')
243
- .trim();
244
- }
245
- function isCliCommandListQuestion(normalizedQuestion) {
246
- const mentionsCli = /\bcli\b|\bneurcode\b|\bcommand\b|\bcmds?\b/.test(normalizedQuestion);
247
- if (!mentionsCli)
248
- return false;
249
- return (/\blist\b/.test(normalizedQuestion) ||
250
- /\bshow\b/.test(normalizedQuestion) ||
251
- /\bavailable\b/.test(normalizedQuestion) ||
252
- /\bwhat can i\b/.test(normalizedQuestion) ||
253
- /\bwhich\b/.test(normalizedQuestion) ||
254
- /\blike\b/.test(normalizedQuestion));
255
- }
256
- function isInstallCliQuestion(normalizedQuestion) {
257
- const mentionsInstall = /\binstall\b|\bupgrade\b|\blatest\b|\bupdate\b/.test(normalizedQuestion);
258
- const mentionsCli = /\bcli\b|\bneurcode\b/.test(normalizedQuestion);
259
- const asksForCommand = /\bcmd\b|\bcommand\b|\bhow\b|\bwhat\b/.test(normalizedQuestion);
260
- return mentionsInstall && mentionsCli && asksForCommand;
261
- }
262
- function isTenancyQuestion(normalizedQuestion) {
263
- if (/\bmulti[- ]?tenant\b|\bsingle[- ]?tenant\b|\btenancy\b/.test(normalizedQuestion))
264
- return true;
265
- return /\borganization\b/.test(normalizedQuestion) && /\bsingle\b|\bmulti\b/.test(normalizedQuestion);
266
- }
267
- function isFeatureOverviewQuestion(normalizedQuestion) {
268
- return (/\bfeature\b|\bfeatures\b|\bcapability\b|\bcapabilities\b/.test(normalizedQuestion) ||
269
- /\bwhat all\b|\bwhat does\b|\bwhat can\b|\boffers?\b|\bplatform have\b/.test(normalizedQuestion));
294
+ function addAnchorCandidates(fileTree, candidateSet, pathPriority, normalizedQuestion) {
295
+ const asSet = new Set(fileTree);
296
+ const pinned = [
297
+ 'README.md',
298
+ 'packages/cli/package.json',
299
+ 'packages/cli/src/index.ts',
300
+ 'packages/cli/src/api-client.ts',
301
+ 'services/api/src/lib/org-context.ts',
302
+ 'services/api/src/lib/user-org.ts',
303
+ 'docs/cli-commands.md',
304
+ 'docs/enterprise-setup.md',
305
+ ];
306
+ for (const path of pinned) {
307
+ if (!asSet.has(path))
308
+ continue;
309
+ candidateSet.add(path);
310
+ pathPriority.set(path, Math.max(pathPriority.get(path) || 0, 0.22));
311
+ }
312
+ const docsLikelyUseful = /\b(feature|capabilit|offer|platform|install|setup|tenant|tenancy|architecture)\b/.test(normalizedQuestion);
313
+ if (!docsLikelyUseful)
314
+ return;
315
+ let addedDocs = 0;
316
+ for (const path of fileTree) {
317
+ if (addedDocs >= 24)
318
+ break;
319
+ if (path === 'README.md' || (path.startsWith('docs/') && path.endsWith('.md'))) {
320
+ candidateSet.add(path);
321
+ pathPriority.set(path, Math.max(pathPriority.get(path) || 0, 0.18));
322
+ addedDocs++;
323
+ }
324
+ }
270
325
  }
271
- function extractCliCommandCatalog(cwd) {
272
- const indexPath = 'packages/cli/src/index.ts';
273
- const fullPath = (0, path_1.join)(cwd, indexPath);
274
- if (!(0, fs_1.existsSync)(fullPath))
326
+ function readCliPackageName(cwd) {
327
+ const pkgPath = (0, path_1.join)(cwd, 'packages/cli/package.json');
328
+ if (!(0, fs_1.existsSync)(pkgPath))
275
329
  return null;
276
- let content = '';
277
330
  try {
278
- content = (0, fs_1.readFileSync)(fullPath, 'utf-8');
331
+ const parsed = JSON.parse((0, fs_1.readFileSync)(pkgPath, 'utf-8'));
332
+ if (typeof parsed.name !== 'string' || !parsed.name.trim())
333
+ return null;
334
+ return parsed.name.trim();
279
335
  }
280
336
  catch {
281
337
  return null;
282
338
  }
283
- if (!content.trim())
284
- return null;
285
- const lines = content.split(/\r?\n/);
286
- const topLevel = [];
287
- const subcommands = [];
288
- const citations = [];
339
+ }
340
+ function extractCommandsFromCliIndex(cwd) {
341
+ const indexPath = (0, path_1.join)(cwd, 'packages/cli/src/index.ts');
342
+ if (!(0, fs_1.existsSync)(indexPath))
343
+ return [];
344
+ let content = '';
345
+ try {
346
+ content = (0, fs_1.readFileSync)(indexPath, 'utf-8');
347
+ }
348
+ catch {
349
+ return [];
350
+ }
351
+ const commandSet = new Set();
289
352
  const topByVar = new Map();
290
- const seenTop = new Set();
291
- const seenSub = new Set();
292
- const topRegex = /(?:const\s+([A-Za-z_$][\w$]*)\s*=\s*)?program\s*\r?\n\s*\.command\('([^']+)'\)/g;
293
- for (const match of content.matchAll(topRegex)) {
353
+ for (const match of content.matchAll(/(?:const\s+([A-Za-z_$][\w$]*)\s*=\s*)?program\s*\r?\n\s*\.command\('([^']+)'\)/g)) {
294
354
  const varName = match[1];
295
- const rawCommand = (match[2] || '').trim();
296
- if (!rawCommand)
297
- continue;
298
- const commandName = rawCommand.split(/\s+/)[0];
299
- if (!commandName)
355
+ const top = (match[2] || '').trim().split(/\s+/)[0];
356
+ if (!top)
300
357
  continue;
358
+ commandSet.add(`neurcode ${top}`);
301
359
  if (varName) {
302
- topByVar.set(varName, commandName);
303
- }
304
- if (!seenTop.has(commandName)) {
305
- seenTop.add(commandName);
306
- topLevel.push(commandName);
307
- const baseOffset = match.index ?? 0;
308
- const commandInMatchOffset = match[0].indexOf(".command('");
309
- const offset = commandInMatchOffset >= 0 ? baseOffset + commandInMatchOffset : baseOffset;
310
- const line = lineNumberFromOffset(content, offset);
311
- citations.push({
312
- path: indexPath,
313
- line,
314
- snippet: normalizeSnippet(lines[line - 1] || `.command('${rawCommand}')`),
315
- term: 'command',
316
- });
360
+ topByVar.set(varName, top);
317
361
  }
318
362
  }
319
- const subRegex = /([A-Za-z_$][\w$]*)\s*\r?\n\s*\.command\('([^']+)'\)/g;
320
- for (const match of content.matchAll(subRegex)) {
363
+ for (const match of content.matchAll(/([A-Za-z_$][\w$]*)\s*\r?\n\s*\.command\('([^']+)'\)/g)) {
321
364
  const parentVar = match[1];
322
365
  const parent = topByVar.get(parentVar);
323
366
  if (!parent)
324
367
  continue;
325
- const rawSubcommand = (match[2] || '').trim();
326
- if (!rawSubcommand)
327
- continue;
328
- const subcommandName = rawSubcommand.split(/\s+/)[0];
329
- if (!subcommandName)
368
+ const sub = (match[2] || '').trim().split(/\s+/)[0];
369
+ if (!sub)
330
370
  continue;
331
- const combined = `${parent} ${subcommandName}`;
332
- if (seenSub.has(combined))
333
- continue;
334
- seenSub.add(combined);
335
- subcommands.push(combined);
336
- const baseOffset = match.index ?? 0;
337
- const commandInMatchOffset = match[0].indexOf(".command('");
338
- const offset = commandInMatchOffset >= 0 ? baseOffset + commandInMatchOffset : baseOffset;
339
- const line = lineNumberFromOffset(content, offset);
340
- citations.push({
341
- path: indexPath,
342
- line,
343
- snippet: normalizeSnippet(lines[line - 1] || `${parentVar}.command('${rawSubcommand}')`),
344
- term: 'subcommand',
345
- });
346
- }
347
- if (topLevel.length === 0)
348
- return null;
349
- return {
350
- topLevelCommands: topLevel,
351
- subcommands,
352
- citations,
353
- };
354
- }
355
- function extractInstallCommandInfo(cwd) {
356
- const pkgPath = 'packages/cli/package.json';
357
- const fullPath = (0, path_1.join)(cwd, pkgPath);
358
- if (!(0, fs_1.existsSync)(fullPath))
359
- return null;
360
- let raw = '';
361
- try {
362
- raw = (0, fs_1.readFileSync)(fullPath, 'utf-8');
363
- }
364
- catch {
365
- return null;
366
- }
367
- let packageName = '';
368
- try {
369
- const parsed = JSON.parse(raw);
370
- packageName = typeof parsed.name === 'string' ? parsed.name.trim() : '';
371
- }
372
- catch {
373
- return null;
374
- }
375
- if (!packageName)
376
- return null;
377
- const lines = raw.split(/\r?\n/);
378
- const nameLine = lines.findIndex((line) => /"name"\s*:/.test(line));
379
- const versionLine = lines.findIndex((line) => /"version"\s*:/.test(line));
380
- const citations = [];
381
- if (nameLine >= 0) {
382
- citations.push({
383
- path: pkgPath,
384
- line: nameLine + 1,
385
- snippet: normalizeSnippet(lines[nameLine]),
386
- term: 'package name',
387
- });
371
+ commandSet.add(`neurcode ${parent} ${sub}`);
388
372
  }
389
- if (versionLine >= 0) {
390
- citations.push({
391
- path: pkgPath,
392
- line: versionLine + 1,
393
- snippet: normalizeSnippet(lines[versionLine]),
394
- term: 'version',
395
- });
396
- }
397
- return {
398
- packageName,
399
- citations,
400
- };
373
+ return [...commandSet].slice(0, 40);
401
374
  }
402
- function buildInstallCommandAnswer(question, normalizedQuestion, installInfo, brainCandidates) {
403
- const pkg = installInfo.packageName;
404
- return {
405
- question,
406
- questionNormalized: normalizedQuestion,
407
- mode: 'search',
408
- answer: [
409
- 'Use this command to install the latest Neurcode CLI globally:',
410
- `\`npm install -g ${pkg}@latest\``,
411
- '',
412
- `Optional (pnpm): \`pnpm add -g ${pkg}@latest\``,
413
- 'Then verify with: `neurcode --version`',
414
- ].join('\n'),
415
- findings: [
416
- `CLI package detected: ${pkg}`,
417
- 'Source of truth: packages/cli/package.json',
418
- ],
419
- confidence: 'high',
420
- truth: {
421
- status: 'grounded',
422
- score: 0.99,
423
- reasons: [],
424
- sourceCitations: installInfo.citations.length,
425
- sourceFiles: 1,
426
- minCitationsRequired: 1,
427
- minFilesRequired: 1,
428
- },
429
- citations: installInfo.citations,
430
- generatedAt: new Date().toISOString(),
431
- stats: {
432
- scannedFiles: 1,
433
- matchedFiles: 1,
434
- matchedLines: installInfo.citations.length,
435
- brainCandidates,
436
- },
437
- };
438
- }
439
- function buildTenancyAnswer(cwd, question, normalizedQuestion, brainCandidates) {
440
- const candidatePaths = [
441
- 'README.md',
442
- 'packages/cli/src/api-client.ts',
443
- 'packages/cli/src/utils/state.ts',
444
- 'services/api/src/lib/org-context.ts',
445
- 'services/api/src/routes/logs.ts',
446
- 'services/api/src/routes/metrics.ts',
447
- 'services/api/src/db/index.ts',
448
- 'services/api/src/services/architect-service.ts',
449
- 'services/api/src/lib/user-org.ts',
450
- ];
451
- const multiPatterns = [
452
- { regex: /\bmulti[- ]tenant\b/i, weight: 3, term: 'multi-tenant' },
453
- { regex: /\bx-org-id\b/i, weight: 2, term: 'x-org-id' },
454
- { regex: /\borganization[_ -]?id\b/i, weight: 1, term: 'organization_id' },
455
- { regex: /\bscoped by organization/i, weight: 3, term: 'org-scoped' },
456
- { regex: /\bisolated workspaces?\b/i, weight: 2, term: 'isolated workspaces' },
457
- ];
458
- const singlePatterns = [
459
- { regex: /\bsingle[- ]tenant\b/i, weight: 3, term: 'single-tenant' },
460
- { regex: /\bsingle-user platform\b/i, weight: 2, term: 'single-user platform' },
461
- { regex: /\bno organization management\b/i, weight: 2, term: 'no organization management' },
462
- ];
463
- const weighted = [];
464
- let scannedFiles = 0;
465
- let multiScore = 0;
466
- let singleScore = 0;
467
- for (const relPath of candidatePaths) {
468
- const fullPath = (0, path_1.join)(cwd, relPath);
469
- if (!(0, fs_1.existsSync)(fullPath))
470
- continue;
471
- let content = '';
472
- try {
473
- const st = (0, fs_1.statSync)(fullPath);
474
- if (st.size > MAX_FILE_BYTES)
475
- continue;
476
- content = (0, fs_1.readFileSync)(fullPath, 'utf-8');
477
- }
478
- catch {
479
- continue;
480
- }
481
- scannedFiles++;
482
- const lines = content.split(/\r?\n/);
483
- for (let idx = 0; idx < lines.length; idx++) {
484
- const line = lines[idx];
485
- if (!line || line.trim().length === 0)
486
- continue;
487
- for (const pattern of multiPatterns) {
488
- if (!pattern.regex.test(line))
489
- continue;
490
- multiScore += pattern.weight;
491
- weighted.push({
492
- path: relPath,
493
- line: idx + 1,
494
- snippet: normalizeSnippet(line),
495
- term: pattern.term,
496
- weight: pattern.weight,
497
- side: 'multi',
498
- });
499
- }
500
- for (const pattern of singlePatterns) {
501
- if (!pattern.regex.test(line))
502
- continue;
503
- singleScore += pattern.weight;
504
- weighted.push({
505
- path: relPath,
506
- line: idx + 1,
507
- snippet: normalizeSnippet(line),
508
- term: pattern.term,
509
- weight: pattern.weight,
510
- side: 'single',
511
- });
512
- }
513
- }
514
- }
515
- if (weighted.length === 0)
516
- return null;
517
- const deduped = new Map();
518
- for (const citation of weighted) {
519
- const key = `${citation.path}:${citation.line}:${citation.term || ''}`;
520
- const existing = deduped.get(key);
521
- if (!existing || citation.weight > existing.weight) {
522
- deduped.set(key, citation);
523
- }
524
- }
525
- const citations = [...deduped.values()].sort((a, b) => b.weight - a.weight);
526
- const matchedFiles = new Set(citations.map((citation) => citation.path)).size;
527
- const totalSignals = multiScore + singleScore;
528
- const delta = Math.abs(multiScore - singleScore);
529
- let classification;
530
- if (multiScore >= singleScore + 3)
531
- classification = 'multi';
532
- else if (singleScore >= multiScore + 3)
533
- classification = 'single';
534
- else
535
- classification = 'mixed';
536
- const reasons = [];
537
- if (classification === 'mixed') {
538
- reasons.push('Mixed indicators found: both multi-tenant and single-tenant signals are present.');
539
- }
540
- if (matchedFiles < 2) {
541
- reasons.push('Evidence is concentrated in too few files for a strong architecture verdict.');
542
- }
543
- const scoreBase = totalSignals > 0 ? delta / totalSignals : 0;
544
- const score = Math.max(0.45, Math.min(0.98, scoreBase * 0.6 + Math.min(citations.length, 12) / 20 + (matchedFiles >= 2 ? 0.15 : 0.05)));
545
- const truthStatus = reasons.length > 0 && classification === 'mixed' ? 'insufficient' : 'grounded';
546
- const truth = {
547
- status: truthStatus,
548
- score,
549
- reasons,
550
- sourceCitations: citations.length,
551
- sourceFiles: matchedFiles,
552
- minCitationsRequired: 2,
553
- minFilesRequired: 1,
554
- };
555
- let answer;
556
- if (classification === 'multi') {
557
- answer = 'This codebase is multi-tenant. Core API/CLI flows are organization-scoped (org IDs and `x-org-id` context are enforced).';
558
- }
559
- else if (classification === 'single') {
560
- answer = 'This codebase appears single-tenant based on current source signals.';
561
- }
562
- else if (multiScore >= singleScore) {
563
- answer = 'The repo has mixed signals, but it currently leans multi-tenant. Most runtime paths are organization-scoped, with some legacy single-user wording.';
564
- }
565
- else {
566
- answer = 'The repo has mixed tenancy signals and currently leans single-tenant in wording, though some organization-scoped logic exists.';
567
- }
568
- return {
569
- question,
570
- questionNormalized: normalizedQuestion,
571
- mode: 'comparison',
572
- answer,
573
- findings: [
574
- `Tenancy signals: multi=${multiScore}, single=${singleScore}`,
575
- `Evidence files: ${matchedFiles}`,
576
- 'Key sources include API org-context/auth routes and CLI org-scoping headers.',
577
- ],
578
- confidence: calibrateConfidence(truth),
579
- truth: {
580
- status: truth.status,
581
- score: Number(truth.score.toFixed(2)),
582
- reasons: truth.reasons,
583
- sourceCitations: truth.sourceCitations,
584
- sourceFiles: truth.sourceFiles,
585
- minCitationsRequired: truth.minCitationsRequired,
586
- minFilesRequired: truth.minFilesRequired,
587
- },
588
- citations: citations.slice(0, 24).map(({ path, line, snippet, term }) => ({ path, line, snippet, term })),
589
- generatedAt: new Date().toISOString(),
590
- stats: {
591
- scannedFiles,
592
- matchedFiles,
593
- matchedLines: citations.length,
594
- brainCandidates,
595
- },
596
- };
597
- }
598
- function extractFeatureEntriesFromReadme(cwd) {
599
- const readmePath = 'README.md';
600
- const fullPath = (0, path_1.join)(cwd, readmePath);
601
- if (!(0, fs_1.existsSync)(fullPath))
375
+ function extractFeatureBulletsFromReadme(cwd, limit) {
376
+ const readmePath = (0, path_1.join)(cwd, 'README.md');
377
+ if (!(0, fs_1.existsSync)(readmePath))
602
378
  return [];
603
379
  let content = '';
604
380
  try {
605
- content = (0, fs_1.readFileSync)(fullPath, 'utf-8');
381
+ content = (0, fs_1.readFileSync)(readmePath, 'utf-8');
606
382
  }
607
383
  catch {
608
384
  return [];
609
385
  }
610
386
  const lines = content.split(/\r?\n/);
611
387
  let start = lines.findIndex((line) => /^##\s+.*features/i.test(line));
612
- if (start < 0) {
613
- start = lines.findIndex((line) => /^###\s+the solution/i.test(line));
614
- }
388
+ if (start < 0)
389
+ start = lines.findIndex((line) => /^###\s+.*features/i.test(line));
615
390
  if (start < 0)
616
391
  return [];
617
- let end = lines.length;
392
+ const out = [];
393
+ const seen = new Set();
618
394
  for (let i = start + 1; i < lines.length; i++) {
619
- if (/^##\s+/.test(lines[i])) {
620
- end = i;
395
+ const line = lines[i];
396
+ if (/^##\s+/.test(line))
621
397
  break;
398
+ const rich = line.match(/^- \*\*(.+?)\*\*\s*-\s*(.+)$/);
399
+ if (rich?.[1]) {
400
+ const text = `${formatInsightSnippet(rich[1])} - ${formatInsightSnippet(rich[2])}`.trim();
401
+ const key = text.toLowerCase();
402
+ if (key && !seen.has(key)) {
403
+ seen.add(key);
404
+ out.push(text);
405
+ }
622
406
  }
407
+ else {
408
+ const simple = line.match(/^- (.+)$/);
409
+ if (!simple?.[1])
410
+ continue;
411
+ const text = formatInsightSnippet(simple[1]);
412
+ const key = text.toLowerCase();
413
+ if (text && !seen.has(key)) {
414
+ seen.add(key);
415
+ out.push(text);
416
+ }
417
+ }
418
+ if (out.length >= limit)
419
+ break;
623
420
  }
624
- const entries = [];
625
- let currentCategory = 'Core Features';
626
- for (let i = start + 1; i < end; i++) {
627
- const line = lines[i];
628
- if (!line)
629
- continue;
630
- const headingMatch = line.match(/^###\s+(.+)$/);
631
- if (headingMatch?.[1]) {
632
- currentCategory = stripMarkdown(headingMatch[1]);
633
- continue;
421
+ return out;
422
+ }
423
+ function extractNeurcodeCommandsFromCitations(citations) {
424
+ const commandSet = new Set();
425
+ for (const citation of citations) {
426
+ const snippet = citation.snippet || '';
427
+ for (const match of snippet.matchAll(/\.command\('([^']+)'\)/g)) {
428
+ const command = (match[1] || '').trim().split(/\s+/)[0];
429
+ if (!command)
430
+ continue;
431
+ commandSet.add(`neurcode ${command}`);
634
432
  }
635
- const richBullet = line.match(/^- \*\*(.+?)\*\*\s*-\s*(.+)$/);
636
- if (richBullet?.[1]) {
637
- entries.push({
638
- category: currentCategory,
639
- title: stripMarkdown(richBullet[1]),
640
- description: stripMarkdown(richBullet[2]),
641
- line: i + 1,
642
- snippet: normalizeSnippet(line),
643
- });
644
- continue;
433
+ for (const match of snippet.matchAll(/`neurcode\s+([^`]+)`/g)) {
434
+ const command = (match[1] || '').trim();
435
+ if (!command)
436
+ continue;
437
+ commandSet.add(`neurcode ${command}`);
645
438
  }
646
- const simpleBullet = line.match(/^- (.+)$/);
647
- if (simpleBullet?.[1]) {
648
- const text = stripMarkdown(simpleBullet[1]);
649
- if (!text)
439
+ for (const plainMatch of snippet.matchAll(/\bneurcode\s+([a-z][a-z0-9-]*(?:\s+[a-z][a-z0-9-]*)?)\b/g)) {
440
+ if (!plainMatch?.[1])
650
441
  continue;
651
- entries.push({
652
- category: currentCategory,
653
- title: text,
654
- line: i + 1,
655
- snippet: normalizeSnippet(line),
656
- });
442
+ commandSet.add(`neurcode ${plainMatch[1].trim()}`);
657
443
  }
658
444
  }
659
- return entries;
445
+ return [...commandSet].slice(0, 20);
660
446
  }
661
- function buildFeatureOverviewAnswer(question, normalizedQuestion, entries, brainCandidates) {
662
- const grouped = new Map();
663
- for (const entry of entries) {
664
- const bucket = grouped.get(entry.category) || [];
665
- const item = entry.description ? `${entry.title} - ${entry.description}` : entry.title;
666
- if (!bucket.includes(item))
667
- bucket.push(item);
668
- grouped.set(entry.category, bucket);
669
- }
670
- const categoryLines = [];
671
- for (const [category, items] of grouped.entries()) {
672
- categoryLines.push(`• ${category}: ${items.slice(0, 3).join('; ')}`);
673
- if (categoryLines.length >= 6)
447
+ function extractInstallCommandsFromCitations(citations) {
448
+ const installSet = new Set();
449
+ for (const citation of citations) {
450
+ const snippet = citation.snippet || '';
451
+ const matches = snippet.match(/\b(?:npm|pnpm|yarn)\s+(?:install|add)\s+-g\s+[@a-z0-9_.\-/]+(?:@latest)?\b/gi) || [];
452
+ for (const match of matches) {
453
+ installSet.add(match.trim());
454
+ }
455
+ }
456
+ return [...installSet].slice(0, 6);
457
+ }
458
+ function formatInsightSnippet(snippet) {
459
+ return snippet
460
+ .replace(/^[-*]\s+/, '')
461
+ .replace(/^\/\/\s?/, '')
462
+ .replace(/^\/\*\s?/, '')
463
+ .replace(/\*\/$/, '')
464
+ .replace(/^\*\s+/, '')
465
+ .replace(/\s+/g, ' ')
466
+ .trim()
467
+ .slice(0, 180);
468
+ }
469
+ function extractInsightLines(citations, limit) {
470
+ const out = [];
471
+ const seen = new Set();
472
+ for (const citation of citations) {
473
+ const line = formatInsightSnippet(citation.snippet || '');
474
+ if (!line || line.length < 10)
475
+ continue;
476
+ if (/^#+\s/.test(line))
477
+ continue;
478
+ if (/^name:\s+/i.test(line))
479
+ continue;
480
+ const tooNoisy = /[{}[\];=><]/.test(line) && !/\b(multi-tenant|single-tenant|organization|install|command|feature)\b/i.test(line);
481
+ if (tooNoisy)
482
+ continue;
483
+ const key = line.toLowerCase();
484
+ if (seen.has(key))
485
+ continue;
486
+ seen.add(key);
487
+ out.push(line);
488
+ if (out.length >= limit)
674
489
  break;
675
490
  }
676
- const citations = entries.slice(0, 24).map((entry) => ({
677
- path: 'README.md',
678
- line: entry.line,
679
- snippet: entry.snippet,
680
- term: 'feature',
681
- }));
682
- return {
683
- question,
684
- questionNormalized: normalizedQuestion,
685
- mode: 'search',
686
- answer: [
687
- 'Neurcode platform features in this repo include:',
688
- ...categoryLines,
689
- '',
690
- 'If you want, I can also list this as “feature + exact CLI command” pairs.',
691
- ].join('\n'),
692
- findings: [
693
- `Feature entries found: ${entries.length}`,
694
- `Feature categories found: ${grouped.size}`,
695
- 'Source of truth: README.md',
696
- ],
697
- confidence: 'high',
698
- truth: {
699
- status: 'grounded',
700
- score: 0.93,
701
- reasons: [],
702
- sourceCitations: citations.length,
703
- sourceFiles: 1,
704
- minCitationsRequired: 2,
705
- minFilesRequired: 1,
706
- },
707
- citations,
708
- generatedAt: new Date().toISOString(),
709
- stats: {
710
- scannedFiles: 1,
711
- matchedFiles: 1,
712
- matchedLines: citations.length,
713
- brainCandidates,
714
- },
715
- };
491
+ return out;
716
492
  }
717
- function buildCliCommandAnswer(question, normalizedQuestion, catalog, brainCandidates) {
718
- const commandBullets = catalog.topLevelCommands.map((command) => ` • \`neurcode ${command}\``);
719
- const nestedBullets = catalog.subcommands.slice(0, 8).map((command) => ` • \`neurcode ${command}\``);
720
- const parts = ['Yes. These are the main CLI commands available in this repo:', ...commandBullets];
721
- if (nestedBullets.length > 0) {
722
- parts.push('\nCommon subcommands:', ...nestedBullets);
723
- }
724
- parts.push('\nTip: run `neurcode <command> --help` for options and examples.');
725
- return {
726
- question,
727
- questionNormalized: normalizedQuestion,
728
- mode: 'search',
729
- answer: parts.join('\n'),
730
- findings: [
731
- `Top-level commands found: ${catalog.topLevelCommands.length}`,
732
- catalog.subcommands.length > 0 ? `Nested subcommands found: ${catalog.subcommands.length}` : 'Nested subcommands found: 0',
733
- 'Source of truth: packages/cli/src/index.ts',
734
- ],
735
- confidence: 'high',
736
- truth: {
737
- status: 'grounded',
738
- score: 0.99,
739
- reasons: [],
740
- sourceCitations: catalog.citations.length,
741
- sourceFiles: 1,
742
- minCitationsRequired: 1,
743
- minFilesRequired: 1,
744
- },
745
- citations: catalog.citations,
746
- generatedAt: new Date().toISOString(),
747
- stats: {
748
- scannedFiles: 1,
749
- matchedFiles: 1,
750
- matchedLines: catalog.citations.length,
751
- brainCandidates,
752
- },
753
- };
493
+ function isCommandCatalogIntent(normalizedQuestion) {
494
+ const mentionsCommandSurface = /\b(cli|cmd|cmds|command|commands|subcommand|subcommands)\b/.test(normalizedQuestion);
495
+ if (!mentionsCommandSurface)
496
+ return false;
497
+ const listIntent = /\blist\b/.test(normalizedQuestion) ||
498
+ /\bshow\b/.test(normalizedQuestion) ||
499
+ /\bavailable\b/.test(normalizedQuestion) ||
500
+ /\ball commands?\b/.test(normalizedQuestion) ||
501
+ /\bwhich commands?\b/.test(normalizedQuestion) ||
502
+ /\bwhat commands?\b/.test(normalizedQuestion) ||
503
+ /\bwhat can i (type|run)\b/.test(normalizedQuestion) ||
504
+ /\bcan i type\b/.test(normalizedQuestion) ||
505
+ /\bcmds\b/.test(normalizedQuestion);
506
+ if (!listIntent)
507
+ return false;
508
+ const specificIntent = /\bwhere\b/.test(normalizedQuestion) ||
509
+ /\bhow\b/.test(normalizedQuestion) ||
510
+ /\bwhy\b/.test(normalizedQuestion) ||
511
+ /\bwhen\b/.test(normalizedQuestion) ||
512
+ /\bwhich file\b/.test(normalizedQuestion) ||
513
+ /\bin which file\b/.test(normalizedQuestion) ||
514
+ /\bfilepath\b/.test(normalizedQuestion) ||
515
+ /\bfile path\b/.test(normalizedQuestion) ||
516
+ /\binject(?:ed)?\b/.test(normalizedQuestion) ||
517
+ /\bhandle(?:s|d)?\b/.test(normalizedQuestion) ||
518
+ /\bused?\b/.test(normalizedQuestion) ||
519
+ /\bflow\b/.test(normalizedQuestion);
520
+ return !specificIntent;
754
521
  }
755
522
  function isPrimarySourcePath(filePath) {
756
523
  const normalized = filePath.trim().replace(/\\/g, '/').toLowerCase();
757
524
  if (!normalized)
758
525
  return false;
759
526
  if (normalized.startsWith('.neurcode/') ||
527
+ normalized.startsWith('.github/') ||
760
528
  normalized.startsWith('.git/') ||
761
529
  normalized.startsWith('node_modules/') ||
762
530
  normalized.startsWith('dist/') ||
@@ -796,12 +564,13 @@ function evaluateTruthAssessment(mode, normalizedQuestion, terms, sourceCitation
796
564
  const broadQuestion = isBroadQuestion(normalizedQuestion);
797
565
  const minCitationsRequired = mode === 'comparison' ? 2 : 2;
798
566
  let minFilesRequired = mode === 'comparison' ? 2 : 1;
567
+ const sourceCitationCount = sourceCitations.length;
568
+ const sourceFileCount = sourcePerFileCounts.size;
569
+ const hasStrongSingleFileEvidence = sourceFileCount === 1 && sourceCitationCount >= 6;
799
570
  if (broadQuestion) {
800
- minFilesRequired = Math.max(minFilesRequired, 2);
571
+ minFilesRequired = hasStrongSingleFileEvidence ? minFilesRequired : Math.max(minFilesRequired, 2);
801
572
  }
802
573
  const reasons = [];
803
- const sourceCitationCount = sourceCitations.length;
804
- const sourceFileCount = sourcePerFileCounts.size;
805
574
  if (sourceCitationCount === 0) {
806
575
  reasons.push('No direct source-file evidence was found for the query terms.');
807
576
  }
@@ -831,7 +600,7 @@ function evaluateTruthAssessment(mode, normalizedQuestion, terms, sourceCitation
831
600
  const highest = Math.max(...sourcePerFileCounts.values());
832
601
  dominantShare = highest / sourceCitationCount;
833
602
  }
834
- if (broadQuestion && dominantShare > 0.85 && sourceFileCount < 3) {
603
+ if (broadQuestion && dominantShare > 0.85 && sourceFileCount < 3 && !hasStrongSingleFileEvidence) {
835
604
  reasons.push('Evidence is highly concentrated in one file; broader coverage is required for this query.');
836
605
  }
837
606
  const citationScore = Math.min(1, sourceCitationCount / Math.max(minCitationsRequired, 4));
@@ -867,17 +636,28 @@ function evaluateTruthAssessment(mode, normalizedQuestion, terms, sourceCitation
867
636
  minFilesRequired,
868
637
  };
869
638
  }
870
- function buildAnswer(mode, question, terms, citations, stats, termCounts, perFileCounts, truth) {
639
+ function buildAnswer(mode, question, terms, citations, stats, termCounts, perFileCounts, truth, context = {}) {
871
640
  const confidence = calibrateConfidence(truth);
872
641
  const normalizedQuestion = (0, plan_cache_1.normalizeIntent)(question);
873
642
  const findings = [];
874
643
  let answer = '';
875
- if (truth.status === 'insufficient') {
876
- answer = 'Insufficient grounded evidence to answer confidently from current source coverage.';
877
- findings.push(...truth.reasons);
878
- findings.push('Refine the query with specific file paths, modules, or identifiers for stronger grounding.');
879
- }
880
- else if (mode === 'comparison' && terms.length >= 2) {
644
+ const asksCommandCatalog = isCommandCatalogIntent(normalizedQuestion);
645
+ const asksInstall = /\b(install|upgrade|update|latest)\b/.test(normalizedQuestion);
646
+ const asksFeatures = /\b(feature|features|capability|capabilities|offers?)\b/.test(normalizedQuestion);
647
+ const asksTenancy = /\b(tenant|tenancy|single|multi|organization)\b/.test(normalizedQuestion);
648
+ const asksLocation = /\b(where|which file|in which file|filepath|file path|location)\b/.test(normalizedQuestion);
649
+ const asksHow = /\bhow\b/.test(normalizedQuestion);
650
+ const asksOrgRequestInjection = /\b(inject|injected|header|request|requests)\b/.test(normalizedQuestion) &&
651
+ /\b(org|organization)\b/.test(normalizedQuestion);
652
+ const commandMatches = [
653
+ ...extractNeurcodeCommandsFromCitations(citations),
654
+ ...(context.knownCommands || []),
655
+ ].filter((value, index, arr) => arr.indexOf(value) === index);
656
+ const installMatches = extractInstallCommandsFromCitations(citations);
657
+ const insightLines = extractInsightLines(citations, 5);
658
+ const multiSignals = citations.filter((citation) => /\bmulti[- ]tenant|x-org-id|organization[_ -]?id|org-scoped\b/i.test(citation.snippet)).length;
659
+ const singleSignals = citations.filter((citation) => /\bsingle[- ]tenant|single-user\b/i.test(citation.snippet)).length;
660
+ if (mode === 'comparison' && terms.length >= 2) {
881
661
  const left = terms[0];
882
662
  const right = terms[1];
883
663
  const leftCount = termCounts.get(left) || 0;
@@ -897,14 +677,94 @@ function buildAnswer(mode, question, terms, citations, stats, termCounts, perFil
897
677
  findings.push(`Compared terms: "${left}" vs "${right}"`);
898
678
  findings.push(`Matches by term: ${left}=${leftCount}, ${right}=${rightCount}`);
899
679
  }
900
- else {
901
- if (citations.length > 0) {
902
- answer = `Found ${citations.length} relevant evidence line(s) across ${stats.matchedFiles} file(s) for your question.`;
680
+ else if (citations.length === 0) {
681
+ answer = 'I could not find direct grounded evidence for that in the files I scanned.';
682
+ findings.push('Try adding a module/file hint, for example: "in packages/cli" or "in services/api auth middleware".');
683
+ }
684
+ else if (asksInstall) {
685
+ const manifestInstall = context.cliPackageName ? `npm install -g ${context.cliPackageName}@latest` : null;
686
+ const primaryInstall = manifestInstall || installMatches[0];
687
+ if (!primaryInstall) {
688
+ answer = 'I found CLI references, but not an explicit install command in the matched evidence.';
689
+ }
690
+ else {
691
+ answer = [
692
+ 'Here is the install command I found for the CLI:',
693
+ `\`${primaryInstall}\``,
694
+ installMatches.length > 1 ? `Also seen: ${installMatches.slice(1, 3).map((cmd) => `\`${cmd}\``).join(', ')}` : '',
695
+ ].filter(Boolean).join('\n');
696
+ }
697
+ }
698
+ else if (asksFeatures && (context.featureBullets || []).length > 0) {
699
+ const bullets = (context.featureBullets || []).slice(0, 6).map((line) => ` • ${line}`);
700
+ answer = ['Here are the main platform features I could verify from the repo:', ...bullets].join('\n');
701
+ }
702
+ else if (asksCommandCatalog && commandMatches.length > 0) {
703
+ const normalizedCommands = commandMatches
704
+ .filter((command) => /^neurcode\s+[a-z]/.test(command))
705
+ .slice(0, 22);
706
+ const commandBullets = normalizedCommands.map((command) => ` • \`${command}\``);
707
+ answer = ['Here are the CLI commands I could verify from the repo:', ...commandBullets].join('\n');
708
+ }
709
+ else if (asksLocation && citations.length > 0) {
710
+ const focusedCitations = citations.filter((citation) => {
711
+ const term = (citation.term || '').toLowerCase();
712
+ return term.length === 0 || !GENERIC_OUTPUT_TERMS.has(term);
713
+ });
714
+ const locationPool = focusedCitations.length > 0 ? [...focusedCitations] : [...citations];
715
+ if (asksOrgRequestInjection) {
716
+ const score = (citation) => {
717
+ const snippet = citation.snippet.toLowerCase();
718
+ let value = 0;
719
+ if (snippet.includes('x-org-id'))
720
+ value += 4;
721
+ if (snippet.includes('auto-inject') || snippet.includes('inject'))
722
+ value += 2;
723
+ if (snippet.includes('headers[') || snippet.includes('header'))
724
+ value += 2;
725
+ if (snippet.includes('request'))
726
+ value += 1;
727
+ if (citation.path.includes('api-client.ts'))
728
+ value += 2;
729
+ return value;
730
+ };
731
+ locationPool.sort((a, b) => score(b) - score(a));
732
+ }
733
+ const locations = locationPool
734
+ .slice(0, 5)
735
+ .map((citation) => ` • ${citation.path}:${citation.line} — ${formatInsightSnippet(citation.snippet)}`);
736
+ answer = ['I found the relevant references here:', ...locations].join('\n');
737
+ }
738
+ else if (asksHow && insightLines.length > 0) {
739
+ const bullets = insightLines.map((line) => ` • ${line}`);
740
+ answer = ['From the matched code, this is how it works:', ...bullets].join('\n');
741
+ }
742
+ else if (asksTenancy && (multiSignals > 0 || singleSignals > 0)) {
743
+ if (multiSignals >= singleSignals + 2) {
744
+ answer = 'This codebase is multi-tenant, with organization-scoped flows (for example org IDs / `x-org-id` context).';
745
+ }
746
+ else if (singleSignals >= multiSignals + 2) {
747
+ answer = 'This codebase currently looks single-tenant from the scanned evidence.';
903
748
  }
904
749
  else {
905
- answer = 'No direct evidence lines were found for this question in the scanned files.';
750
+ answer = 'I see mixed tenancy signals, but it leans multi-tenant in current runtime paths.';
906
751
  }
907
752
  }
753
+ else if (insightLines.length > 0) {
754
+ const bullets = insightLines.map((line) => ` • ${line}`);
755
+ answer = truth.status === 'insufficient'
756
+ ? ['I found partial evidence, but not enough for a fully definitive answer yet.', 'What I can confirm so far:', ...bullets].join('\n')
757
+ : ['From this repo, here is what I found:', ...bullets].join('\n');
758
+ }
759
+ else {
760
+ answer = `I found grounded evidence in ${stats.matchedFiles} file(s), but it is mostly low-level implementation detail.`;
761
+ }
762
+ if (asksCommandCatalog && commandMatches.length === 0 && citations.length > 0) {
763
+ findings.push('Command-style question detected, but no command declarations were found in the matched lines.');
764
+ }
765
+ if (asksInstall && installMatches.length === 0 && !context.cliPackageName) {
766
+ findings.push('Install question detected, but no package name could be resolved from packages/cli/package.json.');
767
+ }
908
768
  const topFiles = [...perFileCounts.entries()]
909
769
  .sort((a, b) => b[1] - a[1])
910
770
  .slice(0, 3)
@@ -913,6 +773,10 @@ function buildAnswer(mode, question, terms, citations, stats, termCounts, perFil
913
773
  findings.push(`Most relevant files: ${topFiles.join(', ')}`);
914
774
  }
915
775
  findings.push(`Scanned ${stats.scannedFiles} file(s); matched ${stats.matchedFiles} file(s).`);
776
+ if (truth.status === 'insufficient') {
777
+ findings.push(...truth.reasons);
778
+ findings.push('Add scope hints (module/file/path) to improve precision and grounding coverage.');
779
+ }
916
780
  return {
917
781
  question,
918
782
  questionNormalized: normalizedQuestion,
@@ -1105,86 +969,13 @@ async function askCommand(question, options = {}) {
1105
969
  const brainResults = orgId && projectId
1106
970
  ? (0, brain_context_1.searchBrainContextEntries)(cwd, scope, normalizedQuestion, { limit: 48 })
1107
971
  : { entries: [], totalIndexedFiles: 0 };
972
+ const cliPackageName = readCliPackageName(cwd);
973
+ const knownCliCommands = extractCommandsFromCliIndex(cwd);
974
+ const featureBullets = extractFeatureBulletsFromReadme(cwd, 10);
1108
975
  if (!options.json && brainResults.entries.length > 0) {
1109
976
  const top = brainResults.entries.filter((entry) => entry.score > 0).length;
1110
977
  console.log(chalk.dim(`🧠 Brain retrieval: ${top} relevant file summaries from ${brainResults.totalIndexedFiles} indexed files`));
1111
978
  }
1112
- const finalizeSpecialAnswer = (answer, note) => {
1113
- emitAskResult(answer, {
1114
- json: options.json,
1115
- maxCitations,
1116
- fromPlan: options.fromPlan,
1117
- verbose: options.verbose,
1118
- });
1119
- if (orgId && projectId) {
1120
- (0, brain_context_1.recordBrainProgressEvent)(cwd, scope, {
1121
- type: 'ask',
1122
- note,
1123
- });
1124
- }
1125
- if (shouldUseCache && orgId && projectId) {
1126
- const questionHash = (0, ask_cache_1.computeAskQuestionHash)({
1127
- question: normalizedQuestion,
1128
- contextHash: staticContext.hash,
1129
- });
1130
- const key = (0, ask_cache_1.computeAskCacheKey)({
1131
- schemaVersion: 3,
1132
- orgId,
1133
- projectId,
1134
- repo: repoFingerprint,
1135
- questionHash,
1136
- policyVersionHash,
1137
- neurcodeVersion,
1138
- });
1139
- (0, ask_cache_1.writeCachedAsk)(cwd, {
1140
- key,
1141
- input: {
1142
- schemaVersion: 3,
1143
- orgId,
1144
- projectId,
1145
- repo: repoFingerprint,
1146
- questionHash,
1147
- policyVersionHash,
1148
- neurcodeVersion,
1149
- question: normalizedQuestion,
1150
- contextHash: staticContext.hash,
1151
- },
1152
- output: answer,
1153
- evidencePaths: answer.citations.map((citation) => citation.path),
1154
- });
1155
- }
1156
- };
1157
- if (isCliCommandListQuestion(normalizedQuestion)) {
1158
- const catalog = extractCliCommandCatalog(cwd);
1159
- if (catalog) {
1160
- const answer = buildCliCommandAnswer(question, normalizedQuestion, catalog, brainResults.entries.length);
1161
- finalizeSpecialAnswer(answer, `mode=cli_catalog;truth=${answer.truth.status};score=${answer.truth.score.toFixed(2)};commands=${catalog.topLevelCommands.length}`);
1162
- return;
1163
- }
1164
- }
1165
- if (isInstallCliQuestion(normalizedQuestion)) {
1166
- const installInfo = extractInstallCommandInfo(cwd);
1167
- if (installInfo) {
1168
- const answer = buildInstallCommandAnswer(question, normalizedQuestion, installInfo, brainResults.entries.length);
1169
- finalizeSpecialAnswer(answer, `mode=install_cli;truth=${answer.truth.status};score=${answer.truth.score.toFixed(2)};package=${installInfo.packageName}`);
1170
- return;
1171
- }
1172
- }
1173
- if (isTenancyQuestion(normalizedQuestion)) {
1174
- const answer = buildTenancyAnswer(cwd, question, normalizedQuestion, brainResults.entries.length);
1175
- if (answer) {
1176
- finalizeSpecialAnswer(answer, `mode=tenancy;truth=${answer.truth.status};score=${answer.truth.score.toFixed(2)};matched_files=${answer.stats.matchedFiles}`);
1177
- return;
1178
- }
1179
- }
1180
- if (isFeatureOverviewQuestion(normalizedQuestion)) {
1181
- const entries = extractFeatureEntriesFromReadme(cwd);
1182
- if (entries.length > 0) {
1183
- const answer = buildFeatureOverviewAnswer(question, normalizedQuestion, entries, brainResults.entries.length);
1184
- finalizeSpecialAnswer(answer, `mode=feature_overview;truth=${answer.truth.status};score=${answer.truth.score.toFixed(2)};entries=${entries.length}`);
1185
- return;
1186
- }
1187
- }
1188
979
  const { mode, terms, matchers } = buildMatchers(question);
1189
980
  const pathHints = derivePathHints(question);
1190
981
  const mentionsAskCommand = /\bask\b/.test(normalizedQuestion);
@@ -1198,6 +989,7 @@ async function askCommand(question, options = {}) {
1198
989
  candidateSet.add(entry.path);
1199
990
  pathPriority.set(entry.path, (pathPriority.get(entry.path) || 0) + entry.score);
1200
991
  }
992
+ addAnchorCandidates(fileTree, candidateSet, pathPriority, normalizedQuestion);
1201
993
  const tokenHints = tokenizeQuestion(question);
1202
994
  if (candidateSet.size < 80) {
1203
995
  for (const filePath of fileTree) {
@@ -1207,6 +999,12 @@ async function askCommand(question, options = {}) {
1207
999
  if (normalizedPath.includes(token))
1208
1000
  score += 0.2;
1209
1001
  }
1002
+ if (normalizedPath.startsWith('.github/')) {
1003
+ score -= 0.2;
1004
+ }
1005
+ if (normalizedPath.startsWith('scripts/')) {
1006
+ score -= 0.1;
1007
+ }
1210
1008
  if (!mentionsAskCommand && (filePath.endsWith('/commands/ask.ts') || filePath.endsWith('/utils/ask-cache.ts'))) {
1211
1009
  score -= 0.45;
1212
1010
  }
@@ -1222,8 +1020,8 @@ async function askCommand(question, options = {}) {
1222
1020
  }
1223
1021
  }
1224
1022
  }
1225
- if (candidateSet.size < 20) {
1226
- for (const filePath of fileTree.slice(0, 160)) {
1023
+ if (candidateSet.size < 40) {
1024
+ for (const filePath of fileTree.slice(0, Math.min(fileTree.length, MAX_SCAN_FILES))) {
1227
1025
  candidateSet.add(filePath);
1228
1026
  }
1229
1027
  }
@@ -1301,6 +1099,8 @@ async function askCommand(question, options = {}) {
1301
1099
  }
1302
1100
  }
1303
1101
  const selectedForOutput = [];
1102
+ const wantsOrgRequestInjection = /\b(inject|injected|header|request|requests)\b/.test(normalizedQuestion) &&
1103
+ /\b(org|organization)\b/.test(normalizedQuestion);
1304
1104
  if (mode === 'comparison' && terms.length >= 2) {
1305
1105
  for (const term of terms.slice(0, 2)) {
1306
1106
  const firstForTerm = sourceEvidence.find((citation) => citation.term === term);
@@ -1309,6 +1109,38 @@ async function askCommand(question, options = {}) {
1309
1109
  }
1310
1110
  }
1311
1111
  }
1112
+ else if (mode === 'search' && terms.length > 0) {
1113
+ const preferredTerms = terms
1114
+ .filter((term) => !LOW_SIGNAL_TERMS.has(term) && term.length >= 4)
1115
+ .slice(0, 8);
1116
+ for (const term of preferredTerms) {
1117
+ const firstForTerm = sourceEvidence.find((citation) => {
1118
+ if (citation.term !== term)
1119
+ return false;
1120
+ const normalized = (citation.term || '').toLowerCase();
1121
+ return !GENERIC_OUTPUT_TERMS.has(normalized);
1122
+ });
1123
+ if (!firstForTerm)
1124
+ continue;
1125
+ if (selectedForOutput.some((existing) => existing.path === firstForTerm.path && existing.line === firstForTerm.line && existing.term === firstForTerm.term)) {
1126
+ continue;
1127
+ }
1128
+ selectedForOutput.push(firstForTerm);
1129
+ if (selectedForOutput.length >= maxCitations)
1130
+ break;
1131
+ }
1132
+ }
1133
+ if (wantsOrgRequestInjection && selectedForOutput.length < maxCitations) {
1134
+ const targeted = sourceEvidence.filter((citation) => /x-org-id|org[_ -]?id|headers?\[|auto-?inject|request/i.test(citation.snippet));
1135
+ for (const citation of targeted) {
1136
+ if (selectedForOutput.length >= maxCitations)
1137
+ break;
1138
+ if (selectedForOutput.some((existing) => existing.path === citation.path && existing.line === citation.line && existing.term === citation.term)) {
1139
+ continue;
1140
+ }
1141
+ selectedForOutput.push(citation);
1142
+ }
1143
+ }
1312
1144
  for (const citation of sourceEvidence) {
1313
1145
  if (selectedForOutput.length >= maxCitations)
1314
1146
  break;
@@ -1330,7 +1162,11 @@ async function askCommand(question, options = {}) {
1330
1162
  brainCandidates: brainResults.entries.length,
1331
1163
  };
1332
1164
  const truth = evaluateTruthAssessment(mode, normalizedQuestion, terms, sourceEvidence, sourcePerFileCounts, sourceTermCounts);
1333
- const answer = buildAnswer(mode, question, terms, finalCitations, stats, sourceTermCounts, sourcePerFileCounts, truth);
1165
+ const answer = buildAnswer(mode, question, terms, finalCitations, stats, sourceTermCounts, sourcePerFileCounts, truth, {
1166
+ cliPackageName,
1167
+ knownCommands: knownCliCommands,
1168
+ featureBullets,
1169
+ });
1334
1170
  emitAskResult(answer, {
1335
1171
  json: options.json,
1336
1172
  maxCitations,