@neurcode-ai/cli 0.9.24 → 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.
- package/README.md +48 -0
- package/dist/api-client.d.ts +29 -0
- package/dist/api-client.d.ts.map +1 -1
- package/dist/api-client.js +28 -2
- package/dist/api-client.js.map +1 -1
- package/dist/commands/ask.d.ts +1 -0
- package/dist/commands/ask.d.ts.map +1 -1
- package/dist/commands/ask.js +321 -22
- package/dist/commands/ask.js.map +1 -1
- package/dist/commands/brain.d.ts.map +1 -1
- package/dist/commands/brain.js +315 -0
- package/dist/commands/brain.js.map +1 -1
- package/dist/commands/policy.d.ts +3 -0
- package/dist/commands/policy.d.ts.map +1 -0
- package/dist/commands/policy.js +148 -0
- package/dist/commands/policy.js.map +1 -0
- package/dist/commands/ship.d.ts +1 -0
- package/dist/commands/ship.d.ts.map +1 -1
- package/dist/commands/ship.js +93 -0
- package/dist/commands/ship.js.map +1 -1
- package/dist/commands/simulate.d.ts +10 -0
- package/dist/commands/simulate.d.ts.map +1 -0
- package/dist/commands/simulate.js +96 -0
- package/dist/commands/simulate.js.map +1 -0
- package/dist/commands/verify.d.ts.map +1 -1
- package/dist/commands/verify.js +85 -51
- package/dist/commands/verify.js.map +1 -1
- package/dist/index.js +26 -0
- package/dist/index.js.map +1 -1
- package/dist/utils/ask-cache.d.ts +10 -0
- package/dist/utils/ask-cache.d.ts.map +1 -1
- package/dist/utils/ask-cache.js.map +1 -1
- package/dist/utils/breakage-simulator.d.ts +53 -0
- package/dist/utils/breakage-simulator.d.ts.map +1 -0
- package/dist/utils/breakage-simulator.js +323 -0
- package/dist/utils/breakage-simulator.js.map +1 -0
- package/dist/utils/policy-packs.d.ts +31 -0
- package/dist/utils/policy-packs.d.ts.map +1 -0
- package/dist/utils/policy-packs.js +277 -0
- package/dist/utils/policy-packs.js.map +1 -0
- package/package.json +1 -1
package/dist/commands/ask.js
CHANGED
|
@@ -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
|
|
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
|
|
589
|
-
const
|
|
590
|
-
|
|
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 (
|
|
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
|
-
|
|
1430
|
-
|
|
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,
|
|
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
|
-
|
|
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 =
|
|
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, {
|