@neurcode-ai/cli 0.9.25 → 0.9.26

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.
Files changed (40) hide show
  1. package/dist/api-client.d.ts +29 -0
  2. package/dist/api-client.d.ts.map +1 -1
  3. package/dist/api-client.js +28 -2
  4. package/dist/api-client.js.map +1 -1
  5. package/dist/commands/ask.d.ts +1 -0
  6. package/dist/commands/ask.d.ts.map +1 -1
  7. package/dist/commands/ask.js +321 -22
  8. package/dist/commands/ask.js.map +1 -1
  9. package/dist/commands/brain.d.ts.map +1 -1
  10. package/dist/commands/brain.js +315 -0
  11. package/dist/commands/brain.js.map +1 -1
  12. package/dist/commands/policy.d.ts +3 -0
  13. package/dist/commands/policy.d.ts.map +1 -0
  14. package/dist/commands/policy.js +148 -0
  15. package/dist/commands/policy.js.map +1 -0
  16. package/dist/commands/ship.d.ts +1 -0
  17. package/dist/commands/ship.d.ts.map +1 -1
  18. package/dist/commands/ship.js +93 -0
  19. package/dist/commands/ship.js.map +1 -1
  20. package/dist/commands/simulate.d.ts +10 -0
  21. package/dist/commands/simulate.d.ts.map +1 -0
  22. package/dist/commands/simulate.js +96 -0
  23. package/dist/commands/simulate.js.map +1 -0
  24. package/dist/commands/verify.d.ts.map +1 -1
  25. package/dist/commands/verify.js +85 -51
  26. package/dist/commands/verify.js.map +1 -1
  27. package/dist/index.js +26 -0
  28. package/dist/index.js.map +1 -1
  29. package/dist/utils/ask-cache.d.ts +10 -0
  30. package/dist/utils/ask-cache.d.ts.map +1 -1
  31. package/dist/utils/ask-cache.js.map +1 -1
  32. package/dist/utils/breakage-simulator.d.ts +53 -0
  33. package/dist/utils/breakage-simulator.d.ts.map +1 -0
  34. package/dist/utils/breakage-simulator.js +323 -0
  35. package/dist/utils/breakage-simulator.js.map +1 -0
  36. package/dist/utils/policy-packs.d.ts +31 -0
  37. package/dist/utils/policy-packs.d.ts.map +1 -0
  38. package/dist/utils/policy-packs.js +277 -0
  39. package/dist/utils/policy-packs.js.map +1 -0
  40. package/package.json +1 -1
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.askCommand = askCommand;
4
+ const child_process_1 = require("child_process");
4
5
  const fs_1 = require("fs");
5
6
  const path_1 = require("path");
6
7
  const config_1 = require("../config");
@@ -487,7 +488,140 @@ function buildOutOfScopeAnswerPayload(question, normalizedQuestion, reasons) {
487
488
  },
488
489
  };
489
490
  }
490
- function tryBuildDeterministicAnswer(_cwd, question, normalizedQuestion) {
491
+ function parseOwnershipLookbackDays(normalizedQuestion) {
492
+ if (/\bquarter\b/.test(normalizedQuestion))
493
+ return 120;
494
+ if (/\bhalf[-\s]?year\b/.test(normalizedQuestion))
495
+ return 180;
496
+ if (/\byear\b/.test(normalizedQuestion))
497
+ return 365;
498
+ if (/\bmonth\b/.test(normalizedQuestion))
499
+ return 30;
500
+ if (/\bweek\b/.test(normalizedQuestion))
501
+ return 14;
502
+ const explicitDays = normalizedQuestion.match(/\b(\d{1,4})\s*days?\b/);
503
+ if (explicitDays) {
504
+ const parsed = Number(explicitDays[1]);
505
+ if (Number.isFinite(parsed))
506
+ return Math.max(1, Math.min(parsed, 3650));
507
+ }
508
+ return 90;
509
+ }
510
+ function buildOwnershipDeterministicAnswer(cwd, question, normalizedQuestion) {
511
+ const asksOwnership = /\b(who|owner|owners|authored|touched|touch)\b/.test(normalizedQuestion);
512
+ if (!asksOwnership)
513
+ return null;
514
+ const ignoreTerms = new Set([
515
+ 'who', 'owner', 'owners', 'authored', 'touched', 'touch', 'last', 'quarter', 'recent', 'recently',
516
+ 'file', 'files', 'module', 'modules', 'repo', 'repository', 'codebase', 'this', 'that', 'these', 'those',
517
+ 'for', 'from', 'with', 'about', 'during', 'before', 'after', 'show', 'list', 'what', 'which', 'where',
518
+ ]);
519
+ const focusTerms = (0, plan_cache_1.normalizeIntent)(question)
520
+ .split(/\s+/)
521
+ .map((term) => term.trim())
522
+ .filter((term) => term.length >= 4 && !ignoreTerms.has(term));
523
+ const sinceDays = parseOwnershipLookbackDays(normalizedQuestion);
524
+ const result = (0, child_process_1.spawnSync)('git', ['log', `--since=${sinceDays}.days`, '--name-only', '--pretty=format:__AUTHOR__%an'], {
525
+ cwd,
526
+ encoding: 'utf-8',
527
+ maxBuffer: 1024 * 1024 * 60,
528
+ stdio: ['ignore', 'pipe', 'pipe'],
529
+ });
530
+ if ((result.status ?? 1) !== 0 || !result.stdout) {
531
+ return null;
532
+ }
533
+ const authorTouches = new Map();
534
+ const fileTouches = new Map();
535
+ let currentAuthor = '';
536
+ for (const rawLine of result.stdout.split(/\r?\n/)) {
537
+ const line = rawLine.trim();
538
+ if (!line)
539
+ continue;
540
+ if (line.startsWith('__AUTHOR__')) {
541
+ currentAuthor = line.replace('__AUTHOR__', '').trim() || 'Unknown';
542
+ continue;
543
+ }
544
+ if (!currentAuthor)
545
+ continue;
546
+ const normalizedPath = line.replace(/\\/g, '/').replace(/^\.\//, '');
547
+ if (!normalizedPath || normalizedPath.startsWith('.git/') || normalizedPath.startsWith('node_modules/')) {
548
+ continue;
549
+ }
550
+ if (focusTerms.length > 0 && !focusTerms.some((term) => (0, plan_cache_1.normalizeIntent)(normalizedPath).includes(term))) {
551
+ continue;
552
+ }
553
+ authorTouches.set(currentAuthor, (authorTouches.get(currentAuthor) || 0) + 1);
554
+ const perFile = fileTouches.get(normalizedPath) || new Map();
555
+ perFile.set(currentAuthor, (perFile.get(currentAuthor) || 0) + 1);
556
+ fileTouches.set(normalizedPath, perFile);
557
+ }
558
+ if (authorTouches.size === 0) {
559
+ return null;
560
+ }
561
+ const topContributors = [...authorTouches.entries()]
562
+ .sort((a, b) => b[1] - a[1])
563
+ .slice(0, 5)
564
+ .map(([author, touches]) => ({ author, touches }));
565
+ const topFiles = [...fileTouches.entries()]
566
+ .sort((a, b) => {
567
+ const aCount = [...a[1].values()].reduce((sum, n) => sum + n, 0);
568
+ const bCount = [...b[1].values()].reduce((sum, n) => sum + n, 0);
569
+ return bCount - aCount;
570
+ })
571
+ .slice(0, 6);
572
+ const targetLabel = focusTerms.length > 0 ? `files matching "${focusTerms.slice(0, 4).join(', ')}"` : 'this repository';
573
+ const answer = [
574
+ `Top contributors for ${targetLabel} in the last ${sinceDays} day(s):`,
575
+ ...topContributors.map((entry) => ` • ${entry.author} (${entry.touches} touches)`),
576
+ ].join('\n');
577
+ const citations = topFiles.map(([path, owners]) => {
578
+ const summary = [...owners.entries()]
579
+ .sort((a, b) => b[1] - a[1])
580
+ .slice(0, 3)
581
+ .map(([author, touches]) => `${author}(${touches})`)
582
+ .join(', ');
583
+ return {
584
+ path,
585
+ line: 1,
586
+ term: focusTerms[0] || 'authorship',
587
+ snippet: `Recent git touches (last ${sinceDays}d): ${summary}`,
588
+ };
589
+ });
590
+ const sourceFiles = new Set(citations.map((citation) => citation.path)).size;
591
+ const score = Math.min(0.98, 0.65 + Math.min(sourceFiles, 6) * 0.05);
592
+ return {
593
+ reason: 'ownership_git_history',
594
+ payload: {
595
+ question,
596
+ questionNormalized: normalizedQuestion,
597
+ mode: 'search',
598
+ answer,
599
+ findings: [
600
+ `Derived from git history over the last ${sinceDays} day(s).`,
601
+ `Focus: ${targetLabel}.`,
602
+ ],
603
+ confidence: topContributors.length >= 2 ? 'high' : 'medium',
604
+ truth: {
605
+ status: 'grounded',
606
+ score,
607
+ reasons: ['Answer is grounded in local git history for this repository.'],
608
+ sourceCitations: citations.length,
609
+ sourceFiles,
610
+ minCitationsRequired: 1,
611
+ minFilesRequired: 1,
612
+ },
613
+ citations,
614
+ generatedAt: new Date().toISOString(),
615
+ stats: {
616
+ scannedFiles: 0,
617
+ matchedFiles: sourceFiles,
618
+ matchedLines: citations.length,
619
+ brainCandidates: 0,
620
+ },
621
+ },
622
+ };
623
+ }
624
+ function tryBuildDeterministicAnswer(cwd, question, normalizedQuestion) {
491
625
  const scope = assessQuestionScope(normalizedQuestion);
492
626
  if (scope.isOutOfScope) {
493
627
  return {
@@ -495,6 +629,10 @@ function tryBuildDeterministicAnswer(_cwd, question, normalizedQuestion) {
495
629
  reason: 'out_of_scope',
496
630
  };
497
631
  }
632
+ const ownership = buildOwnershipDeterministicAnswer(cwd, question, normalizedQuestion);
633
+ if (ownership) {
634
+ return ownership;
635
+ }
498
636
  return null;
499
637
  }
500
638
  function normalizeSnippet(line) {
@@ -585,21 +723,9 @@ function extractCommandsFromCliIndex(cwd) {
585
723
  }
586
724
  return [...commandSet].slice(0, 40);
587
725
  }
588
- function extractFeatureBulletsFromReadme(cwd, limit) {
589
- const readmePath = (0, path_1.join)(cwd, 'README.md');
590
- if (!(0, fs_1.existsSync)(readmePath))
591
- return [];
592
- let content = '';
593
- try {
594
- content = (0, fs_1.readFileSync)(readmePath, 'utf-8');
595
- }
596
- catch {
597
- return [];
598
- }
599
- const lines = content.split(/\r?\n/);
600
- let start = lines.findIndex((line) => /^##\s+.*features/i.test(line));
601
- if (start < 0)
602
- start = lines.findIndex((line) => /^###\s+.*features/i.test(line));
726
+ function extractFeatureBulletsFromMarkdown(markdown, limit, headingPattern) {
727
+ const lines = markdown.split(/\r?\n/);
728
+ let start = lines.findIndex((line) => headingPattern.test(line));
603
729
  if (start < 0)
604
730
  return [];
605
731
  const out = [];
@@ -622,8 +748,10 @@ function extractFeatureBulletsFromReadme(cwd, limit) {
622
748
  if (!simple?.[1])
623
749
  continue;
624
750
  const text = formatInsightSnippet(simple[1]);
751
+ if (!text)
752
+ continue;
625
753
  const key = text.toLowerCase();
626
- if (text && !seen.has(key)) {
754
+ if (!seen.has(key)) {
627
755
  seen.add(key);
628
756
  out.push(text);
629
757
  }
@@ -633,6 +761,63 @@ function extractFeatureBulletsFromReadme(cwd, limit) {
633
761
  }
634
762
  return out;
635
763
  }
764
+ function extractFeatureBulletsFromReadme(cwd, limit) {
765
+ const readmePath = (0, path_1.join)(cwd, 'README.md');
766
+ if (!(0, fs_1.existsSync)(readmePath))
767
+ return [];
768
+ try {
769
+ const content = (0, fs_1.readFileSync)(readmePath, 'utf-8');
770
+ return extractFeatureBulletsFromMarkdown(content, limit, /^#{2,3}\s+.*(features|capabilities|what it does|highlights|offerings|product surface|platform surface|overview)/i);
771
+ }
772
+ catch {
773
+ return [];
774
+ }
775
+ }
776
+ function extractFeatureBulletsFromDocs(cwd, limit) {
777
+ const docsDir = (0, path_1.join)(cwd, 'docs');
778
+ if (!(0, fs_1.existsSync)(docsDir))
779
+ return [];
780
+ const out = [];
781
+ const seen = new Set();
782
+ let files = [];
783
+ try {
784
+ files = (0, fs_1.readdirSync)(docsDir).filter((name) => name.toLowerCase().endsWith('.md')).slice(0, 40);
785
+ }
786
+ catch {
787
+ return [];
788
+ }
789
+ for (const fileName of files) {
790
+ if (out.length >= limit)
791
+ break;
792
+ const fullPath = (0, path_1.join)(docsDir, fileName);
793
+ let content = '';
794
+ try {
795
+ content = (0, fs_1.readFileSync)(fullPath, 'utf-8');
796
+ }
797
+ catch {
798
+ continue;
799
+ }
800
+ const extracted = extractFeatureBulletsFromMarkdown(content, limit, /^#{2,3}\s+.*(features|capabilities|what it does|highlights|offerings|product surface|platform surface|overview)/i);
801
+ for (const bullet of extracted) {
802
+ const key = bullet.toLowerCase();
803
+ if (seen.has(key))
804
+ continue;
805
+ seen.add(key);
806
+ out.push(bullet);
807
+ if (out.length >= limit)
808
+ break;
809
+ }
810
+ }
811
+ return out;
812
+ }
813
+ function extractFeatureBulletsForRepo(cwd, limit) {
814
+ const readmeBullets = extractFeatureBulletsFromReadme(cwd, limit);
815
+ if (readmeBullets.length >= limit) {
816
+ return readmeBullets.slice(0, limit);
817
+ }
818
+ const docsBullets = extractFeatureBulletsFromDocs(cwd, limit);
819
+ return [...new Set([...readmeBullets, ...docsBullets])].slice(0, limit);
820
+ }
636
821
  function extractNeurcodeCommandsFromCitations(citations) {
637
822
  const commandSet = new Set();
638
823
  for (const citation of citations) {
@@ -1189,6 +1374,57 @@ function buildAnswer(mode, question, terms, citations, stats, termCounts, perFil
1189
1374
  const bullets = (context.featureBullets || []).slice(0, 6).map((line) => ` • ${line}`);
1190
1375
  answer = ['Here are the main platform features I could verify from the repo:', ...bullets].join('\n');
1191
1376
  }
1377
+ else if (asksFeatures) {
1378
+ const inferredFeatures = [];
1379
+ const signalText = citations.map((citation) => `${citation.path} ${citation.snippet}`.toLowerCase());
1380
+ const hasSignal = (patterns) => {
1381
+ return signalText.some((line) => patterns.some((pattern) => pattern.test(line)));
1382
+ };
1383
+ const preferredCommands = [
1384
+ 'neurcode ask',
1385
+ 'neurcode plan',
1386
+ 'neurcode apply',
1387
+ 'neurcode verify',
1388
+ 'neurcode ship',
1389
+ 'neurcode watch',
1390
+ 'neurcode session',
1391
+ 'neurcode revert',
1392
+ 'neurcode check',
1393
+ 'neurcode login',
1394
+ 'neurcode config',
1395
+ 'neurcode init',
1396
+ ];
1397
+ const primaryCommands = [
1398
+ ...preferredCommands.filter((command) => commandMatches.includes(command)),
1399
+ ...commandMatches.filter((command) => !preferredCommands.includes(command)),
1400
+ ]
1401
+ .filter((command) => !command.startsWith('neurcode session ') && !command.startsWith('neurcode revert '))
1402
+ .slice(0, 12);
1403
+ if (primaryCommands.length > 0) {
1404
+ inferredFeatures.push(`CLI workflows: ${primaryCommands.join(', ')}`);
1405
+ }
1406
+ if (hasSignal([/\bverify\b/, /\bpolicy\b/, /\bscope[_ -]?guard\b/, /\bgovernance\b/])) {
1407
+ inferredFeatures.push('Governance and policy verification gates.');
1408
+ }
1409
+ if (hasSignal([/\bship\b/, /\bmerge confidence\b/, /\bauto-remediation\b/])) {
1410
+ inferredFeatures.push('Ship workflow with automated remediation and merge confidence scoring.');
1411
+ }
1412
+ if (hasSignal([/\bask\b/, /\bcitation\b/, /\bgrounded\b/, /\bbrain\b/])) {
1413
+ inferredFeatures.push('Repository-grounded Q&A with cached context retrieval.');
1414
+ }
1415
+ if (hasSignal([/\bsession\b/, /\brevert\b/, /\bhistory\b/, /\btime machine\b/])) {
1416
+ inferredFeatures.push('Session/history tracking with revert support.');
1417
+ }
1418
+ if (hasSignal([/\bx-org-id\b/, /\bmulti[- ]tenant\b/, /\borganization[_ -]?id\b/])) {
1419
+ inferredFeatures.push('Multi-tenant organization-scoped isolation and identity context.');
1420
+ }
1421
+ if (inferredFeatures.length > 0) {
1422
+ answer = ['Based on repository evidence, these platform capabilities are available:', ...inferredFeatures.map((line) => ` • ${line}`)].join('\n');
1423
+ }
1424
+ else {
1425
+ answer = 'I found code evidence, but not enough explicit feature documentation to give a precise feature list yet.';
1426
+ }
1427
+ }
1192
1428
  else if (asksSchema && citations.length > 0) {
1193
1429
  const lineCache = context.lineCache || new Map();
1194
1430
  const schemaFieldSummaries = buildSchemaFieldSummaries(citations, lineCache, terms);
@@ -1387,6 +1623,22 @@ function buildAnswer(mode, question, terms, citations, stats, termCounts, perFil
1387
1623
  answer,
1388
1624
  findings,
1389
1625
  confidence,
1626
+ proof: {
1627
+ topFiles: [...citations.reduce((acc, citation) => {
1628
+ acc.set(citation.path, (acc.get(citation.path) || 0) + 1);
1629
+ return acc;
1630
+ }, new Map()).entries()]
1631
+ .sort((a, b) => b[1] - a[1])
1632
+ .slice(0, 5)
1633
+ .map(([path]) => path),
1634
+ evidenceCount: citations.length,
1635
+ coverage: {
1636
+ sourceCitations: truth.sourceCitations,
1637
+ sourceFiles: truth.sourceFiles,
1638
+ matchedFiles: stats.matchedFiles,
1639
+ matchedLines: stats.matchedLines,
1640
+ },
1641
+ },
1390
1642
  truth: {
1391
1643
  status: truth.status,
1392
1644
  score: Number(truth.score.toFixed(2)),
@@ -1426,14 +1678,49 @@ function emitAskResult(result, options) {
1426
1678
  else {
1427
1679
  console.log(chalk.yellow(result.answer));
1428
1680
  }
1429
- if (!options.verbose) {
1430
- if (result.truth.status === 'insufficient' && result.truth.reasons.length > 0) {
1681
+ const showProof = options.proof === true;
1682
+ if (!options.verbose && !showProof) {
1683
+ console.log(chalk.dim(`\nConfidence: ${result.confidence.toUpperCase()}`));
1684
+ if (result.truth.status === 'insufficient' && result.citations.length === 0 && result.truth.reasons.length > 0) {
1431
1685
  console.log(chalk.yellow('\nWhy confidence is limited:'));
1432
- for (const reason of result.truth.reasons.slice(0, 3)) {
1686
+ for (const reason of result.truth.reasons.slice(0, 2)) {
1433
1687
  console.log(chalk.yellow(` • ${reason}`));
1434
1688
  }
1689
+ console.log(chalk.dim('\nTip: add `--proof` for quick evidence digest or `--verbose` for full detail.'));
1435
1690
  }
1436
- console.log(chalk.dim('\nTip: add `--verbose` to show citations and full grounding details.'));
1691
+ return;
1692
+ }
1693
+ if (showProof && !options.verbose) {
1694
+ const proof = result.proof;
1695
+ if (proof) {
1696
+ console.log(chalk.bold.white('\nProof:'));
1697
+ const topFiles = proof.topFiles.slice(0, 4);
1698
+ if (topFiles.length > 0) {
1699
+ console.log(chalk.cyan(` • Top files: ${topFiles.join(', ')}`));
1700
+ }
1701
+ console.log(chalk.cyan(` • Evidence: citations=${proof.coverage.sourceCitations}, source_files=${proof.coverage.sourceFiles}, matched_lines=${proof.coverage.matchedLines}`));
1702
+ }
1703
+ const tenancyQuestion = /\b(tenant|tenancy|single|multi|organization|org)\b/.test(result.questionNormalized);
1704
+ let citations = result.citations.slice(0, Math.min(options.maxCitations, 12));
1705
+ if (tenancyQuestion) {
1706
+ const focused = citations.filter((citation) => /\b(multi[- ]tenant|x-org-id|organization[_ -]?id|org[_ -]?id)\b/i.test(citation.snippet));
1707
+ if (focused.length >= 3) {
1708
+ citations = focused;
1709
+ }
1710
+ if (/multi-tenant/i.test(result.answer)) {
1711
+ citations = citations.filter((citation) => !/\bsingle-user platform\b/i.test(citation.snippet));
1712
+ }
1713
+ }
1714
+ citations = citations.slice(0, Math.min(options.maxCitations, 6));
1715
+ if (citations.length > 0) {
1716
+ console.log(chalk.bold.white('\nKey Evidence:'));
1717
+ citations.forEach((citation, idx) => {
1718
+ console.log(chalk.dim(` ${idx + 1}. ${citation.path}:${citation.line} ${citation.snippet}`));
1719
+ });
1720
+ }
1721
+ const truthLabel = result.truth.status === 'grounded' ? chalk.green('GROUNDED') : chalk.yellow('INSUFFICIENT');
1722
+ console.log(chalk.dim(`\nTruth Mode: ${truthLabel} (score=${result.truth.score.toFixed(2)}, source_citations=${result.truth.sourceCitations}, source_files=${result.truth.sourceFiles})`));
1723
+ console.log(chalk.dim(`Confidence: ${result.confidence.toUpperCase()} | scanned=${result.stats.scannedFiles} matched=${result.stats.matchedFiles}`));
1437
1724
  return;
1438
1725
  }
1439
1726
  if (result.findings.length > 0) {
@@ -1483,6 +1770,7 @@ async function askCommand(question, options = {}) {
1483
1770
  maxCitations,
1484
1771
  fromPlan: options.fromPlan,
1485
1772
  verbose: options.verbose,
1773
+ proof: options.proof,
1486
1774
  });
1487
1775
  if (orgId && projectId) {
1488
1776
  (0, brain_context_1.recordBrainProgressEvent)(cwd, scope, {
@@ -1544,6 +1832,7 @@ async function askCommand(question, options = {}) {
1544
1832
  cacheLabel: `Using cached answer (created: ${new Date(exact.createdAt).toLocaleString()})`,
1545
1833
  fromPlan: options.fromPlan,
1546
1834
  verbose: options.verbose,
1835
+ proof: options.proof,
1547
1836
  });
1548
1837
  (0, brain_context_1.recordBrainProgressEvent)(cwd, scope, {
1549
1838
  type: 'ask',
@@ -1577,6 +1866,7 @@ async function askCommand(question, options = {}) {
1577
1866
  cacheLabel: `${reasonText}, similarity ${near.similarity.toFixed(2)}, created: ${new Date(near.entry.createdAt).toLocaleString()}`,
1578
1867
  fromPlan: options.fromPlan,
1579
1868
  verbose: options.verbose,
1869
+ proof: options.proof,
1580
1870
  });
1581
1871
  (0, brain_context_1.recordBrainProgressEvent)(cwd, scope, {
1582
1872
  type: 'ask',
@@ -1593,7 +1883,7 @@ async function askCommand(question, options = {}) {
1593
1883
  : { entries: [], totalIndexedFiles: 0 };
1594
1884
  const cliPackageName = readCliPackageName(cwd);
1595
1885
  const knownCliCommands = extractCommandsFromCliIndex(cwd);
1596
- const featureBullets = extractFeatureBulletsFromReadme(cwd, 10);
1886
+ const featureBullets = extractFeatureBulletsForRepo(cwd, 10);
1597
1887
  if (!options.json && brainResults.entries.length > 0) {
1598
1888
  const top = brainResults.entries.filter((entry) => entry.score > 0).length;
1599
1889
  console.log(chalk.dim(`🧠 Brain retrieval: ${top} relevant file summaries from ${brainResults.totalIndexedFiles} indexed files`));
@@ -1644,6 +1934,14 @@ async function askCommand(question, options = {}) {
1644
1934
  else if (pathHints.length > 0) {
1645
1935
  score -= 0.08;
1646
1936
  }
1937
+ if (/\b(feature|features|capability|capabilities|offers?|platform)\b/.test(normalizedQuestion)) {
1938
+ if (filePath === 'README.md' || normalizedPath.startsWith('docs/')) {
1939
+ score += 0.55;
1940
+ }
1941
+ if (normalizedPath.startsWith('web/') || normalizedPath.startsWith('packages/dashboard/')) {
1942
+ score -= 0.25;
1943
+ }
1944
+ }
1647
1945
  if (score > 0) {
1648
1946
  candidateSet.add(filePath);
1649
1947
  pathPriority.set(filePath, score);
@@ -1987,6 +2285,7 @@ async function askCommand(question, options = {}) {
1987
2285
  maxCitations,
1988
2286
  fromPlan: options.fromPlan,
1989
2287
  verbose: options.verbose,
2288
+ proof: options.proof,
1990
2289
  });
1991
2290
  if (orgId && projectId) {
1992
2291
  (0, brain_context_1.recordBrainProgressEvent)(cwd, scope, {