minovative-mind-cli 2.9.1 → 2.11.0
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 +14 -6
- package/dist/services/agent/commandApproval.js +5 -2
- package/dist/services/agent/slashCommands.js +90 -53
- package/dist/services/agent-tools.d.ts +4 -3
- package/dist/services/agent-tools.js +32 -79
- package/dist/services/agent.d.ts +5 -6
- package/dist/services/agent.js +11 -15
- package/dist/services/ai.d.ts +21 -1
- package/dist/services/ai.js +236 -11
- package/dist/services/chatHistoryService.d.ts +95 -2
- package/dist/services/chatHistoryService.js +236 -9
- package/dist/services/contextAgent.js +196 -81
- package/dist/services/investigationComplexity.d.ts +1 -1
- package/dist/services/investigationComplexity.js +1 -1
- package/dist/services/orchestration/investigationAgent.js +101 -84
- package/dist/services/orchestration/investigationCache.d.ts +80 -5
- package/dist/services/orchestration/investigationCache.js +570 -41
- package/dist/services/orchestration/investigationOrchestrator.js +17 -4
- package/dist/services/orchestration/orchestrator.js +6 -3
- package/dist/services/orchestration/scopedTools.js +5 -0
- package/dist/services/orchestration/subAgent.d.ts +31 -1
- package/dist/services/orchestration/subAgent.js +153 -2
- package/dist/utils/analysisRunner.d.ts +29 -0
- package/dist/utils/analysisRunner.js +200 -5
- package/dist/utils/contextPrompts.d.ts +20 -4
- package/dist/utils/contextPrompts.js +158 -23
- package/dist/utils/historyPrompt.d.ts +92 -1
- package/dist/utils/historyPrompt.js +166 -2
- package/dist/utils/symbolExtractor.d.ts +12 -0
- package/dist/utils/symbolExtractor.js +946 -0
- package/dist/utils/systemPrompts.d.ts +5 -4
- package/dist/utils/systemPrompts.js +46 -14
- package/oclif.manifest.json +1 -1
- package/package.json +2 -2
|
@@ -369,3 +369,949 @@ export function extractSymbols(content, filePath, targetElements) {
|
|
|
369
369
|
}
|
|
370
370
|
return output.join('\n');
|
|
371
371
|
}
|
|
372
|
+
/**
|
|
373
|
+
* Extracts a compact declarations outline for TypeScript/JavaScript source files.
|
|
374
|
+
*
|
|
375
|
+
* @param lines - File lines
|
|
376
|
+
* @returns Declarations outline
|
|
377
|
+
*/
|
|
378
|
+
function extractTsJsOutline(lines) {
|
|
379
|
+
const output = [];
|
|
380
|
+
let docBuffer = [];
|
|
381
|
+
let i = 0;
|
|
382
|
+
const isDocOrDecorator = (line) => {
|
|
383
|
+
const trimmed = line.trim();
|
|
384
|
+
return (trimmed.startsWith('/**') ||
|
|
385
|
+
trimmed.startsWith('*') ||
|
|
386
|
+
trimmed.startsWith('*/') ||
|
|
387
|
+
trimmed.startsWith('//') ||
|
|
388
|
+
trimmed.startsWith('@'));
|
|
389
|
+
};
|
|
390
|
+
while (i < lines.length) {
|
|
391
|
+
const line = lines[i];
|
|
392
|
+
const trimmed = line.trim();
|
|
393
|
+
if (!trimmed) {
|
|
394
|
+
if (docBuffer.length > 0 && docBuffer.every((l) => l.trim().startsWith('//') || l.trim().startsWith('/*'))) {
|
|
395
|
+
docBuffer = [];
|
|
396
|
+
}
|
|
397
|
+
i++;
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
if (isDocOrDecorator(line)) {
|
|
401
|
+
docBuffer.push(line);
|
|
402
|
+
i++;
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
// Check for interface
|
|
406
|
+
if (/^(?:export\s+|declare\s+|default\s+)*interface\s+\w+/.test(trimmed)) {
|
|
407
|
+
if (docBuffer.length > 0) {
|
|
408
|
+
output.push(...docBuffer);
|
|
409
|
+
docBuffer = [];
|
|
410
|
+
}
|
|
411
|
+
let braces = 0;
|
|
412
|
+
let foundOpen = false;
|
|
413
|
+
while (i < lines.length) {
|
|
414
|
+
for (const ch of lines[i]) {
|
|
415
|
+
if (ch === '{') {
|
|
416
|
+
braces++;
|
|
417
|
+
foundOpen = true;
|
|
418
|
+
}
|
|
419
|
+
else if (ch === '}') {
|
|
420
|
+
braces--;
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
output.push(lines[i]);
|
|
424
|
+
i++;
|
|
425
|
+
if (foundOpen && braces <= 0)
|
|
426
|
+
break;
|
|
427
|
+
}
|
|
428
|
+
output.push('');
|
|
429
|
+
continue;
|
|
430
|
+
}
|
|
431
|
+
// Check for type alias
|
|
432
|
+
if (/^(?:export\s+|declare\s+)*type\s+\w+/.test(trimmed)) {
|
|
433
|
+
if (docBuffer.length > 0) {
|
|
434
|
+
output.push(...docBuffer);
|
|
435
|
+
docBuffer = [];
|
|
436
|
+
}
|
|
437
|
+
let braces = 0;
|
|
438
|
+
let parens = 0;
|
|
439
|
+
let brackets = 0;
|
|
440
|
+
while (i < lines.length) {
|
|
441
|
+
for (const ch of lines[i]) {
|
|
442
|
+
if (ch === '{')
|
|
443
|
+
braces++;
|
|
444
|
+
else if (ch === '}')
|
|
445
|
+
braces--;
|
|
446
|
+
else if (ch === '(')
|
|
447
|
+
parens++;
|
|
448
|
+
else if (ch === ')')
|
|
449
|
+
parens--;
|
|
450
|
+
else if (ch === '[')
|
|
451
|
+
brackets++;
|
|
452
|
+
else if (ch === ']')
|
|
453
|
+
brackets--;
|
|
454
|
+
}
|
|
455
|
+
output.push(lines[i]);
|
|
456
|
+
if (braces <= 0 && parens <= 0 && brackets <= 0 && lines[i].includes(';')) {
|
|
457
|
+
i++;
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
i++;
|
|
461
|
+
}
|
|
462
|
+
output.push('');
|
|
463
|
+
continue;
|
|
464
|
+
}
|
|
465
|
+
// Check for enum
|
|
466
|
+
if (/^(?:export\s+|const\s+|declare\s+)*enum\s+\w+/.test(trimmed)) {
|
|
467
|
+
if (docBuffer.length > 0) {
|
|
468
|
+
output.push(...docBuffer);
|
|
469
|
+
docBuffer = [];
|
|
470
|
+
}
|
|
471
|
+
let braces = 0;
|
|
472
|
+
let foundOpen = false;
|
|
473
|
+
while (i < lines.length) {
|
|
474
|
+
for (const ch of lines[i]) {
|
|
475
|
+
if (ch === '{') {
|
|
476
|
+
braces++;
|
|
477
|
+
foundOpen = true;
|
|
478
|
+
}
|
|
479
|
+
else if (ch === '}') {
|
|
480
|
+
braces--;
|
|
481
|
+
}
|
|
482
|
+
}
|
|
483
|
+
output.push(lines[i]);
|
|
484
|
+
i++;
|
|
485
|
+
if (foundOpen && braces <= 0)
|
|
486
|
+
break;
|
|
487
|
+
}
|
|
488
|
+
output.push('');
|
|
489
|
+
continue;
|
|
490
|
+
}
|
|
491
|
+
// Check for class
|
|
492
|
+
if (/^(?:export\s+|default\s+|abstract\s+|declare\s+)*class\s+\w+/.test(trimmed)) {
|
|
493
|
+
if (docBuffer.length > 0) {
|
|
494
|
+
output.push(...docBuffer);
|
|
495
|
+
docBuffer = [];
|
|
496
|
+
}
|
|
497
|
+
const classHeaderLines = [];
|
|
498
|
+
while (i < lines.length) {
|
|
499
|
+
classHeaderLines.push(lines[i]);
|
|
500
|
+
if (lines[i].includes('{')) {
|
|
501
|
+
i++;
|
|
502
|
+
break;
|
|
503
|
+
}
|
|
504
|
+
i++;
|
|
505
|
+
}
|
|
506
|
+
output.push(classHeaderLines.join('\n'));
|
|
507
|
+
let classBraces = 1;
|
|
508
|
+
let memberDoc = [];
|
|
509
|
+
while (i < lines.length && classBraces > 0) {
|
|
510
|
+
const curLine = lines[i];
|
|
511
|
+
const curTrim = curLine.trim();
|
|
512
|
+
if (!curTrim) {
|
|
513
|
+
memberDoc = [];
|
|
514
|
+
i++;
|
|
515
|
+
continue;
|
|
516
|
+
}
|
|
517
|
+
if (isDocOrDecorator(curLine)) {
|
|
518
|
+
memberDoc.push(curLine);
|
|
519
|
+
i++;
|
|
520
|
+
continue;
|
|
521
|
+
}
|
|
522
|
+
if (curTrim === '}' && classBraces === 1) {
|
|
523
|
+
output.push(curLine);
|
|
524
|
+
i++;
|
|
525
|
+
break;
|
|
526
|
+
}
|
|
527
|
+
const isMethodOrCtor = /^(?:public\s+|private\s+|protected\s+|static\s+|async\s+|abstract\s+|override\s+|readonly\s+|get\s+|set\s+)*(?:constructor|[A-Za-z0-9_$]+)\s*(?:<[^>]+>)?\s*\(/.test(curTrim);
|
|
528
|
+
const isField = /^(?:public\s+|private\s+|protected\s+|static\s+|readonly\s+|declare\s+)*(?:[A-Za-z0-9_$#]+)\s*[:=;]/.test(curTrim);
|
|
529
|
+
if (isMethodOrCtor) {
|
|
530
|
+
if (memberDoc.length > 0) {
|
|
531
|
+
output.push(...memberDoc);
|
|
532
|
+
memberDoc = [];
|
|
533
|
+
}
|
|
534
|
+
const sigLines = [];
|
|
535
|
+
let methodHasBody = false;
|
|
536
|
+
let methodBraces = 0;
|
|
537
|
+
while (i < lines.length) {
|
|
538
|
+
const mLine = lines[i];
|
|
539
|
+
sigLines.push(mLine);
|
|
540
|
+
if (mLine.includes('{')) {
|
|
541
|
+
methodHasBody = true;
|
|
542
|
+
methodBraces = 1;
|
|
543
|
+
i++;
|
|
544
|
+
break;
|
|
545
|
+
}
|
|
546
|
+
if (mLine.trim().endsWith(';')) {
|
|
547
|
+
i++;
|
|
548
|
+
break;
|
|
549
|
+
}
|
|
550
|
+
i++;
|
|
551
|
+
}
|
|
552
|
+
if (methodHasBody) {
|
|
553
|
+
const sig = sigLines.join('\n');
|
|
554
|
+
const braceIdx = sig.lastIndexOf('{');
|
|
555
|
+
const cleanSig = sig.substring(0, braceIdx).trimEnd();
|
|
556
|
+
output.push(cleanSig + ' { ... }');
|
|
557
|
+
while (i < lines.length && methodBraces > 0) {
|
|
558
|
+
for (const ch of lines[i]) {
|
|
559
|
+
if (ch === '{')
|
|
560
|
+
methodBraces++;
|
|
561
|
+
else if (ch === '}')
|
|
562
|
+
methodBraces--;
|
|
563
|
+
}
|
|
564
|
+
i++;
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
else {
|
|
568
|
+
output.push(sigLines.join('\n'));
|
|
569
|
+
}
|
|
570
|
+
continue;
|
|
571
|
+
}
|
|
572
|
+
else if (isField) {
|
|
573
|
+
if (memberDoc.length > 0) {
|
|
574
|
+
output.push(...memberDoc);
|
|
575
|
+
memberDoc = [];
|
|
576
|
+
}
|
|
577
|
+
output.push(curLine);
|
|
578
|
+
i++;
|
|
579
|
+
continue;
|
|
580
|
+
}
|
|
581
|
+
else {
|
|
582
|
+
for (const ch of curLine) {
|
|
583
|
+
if (ch === '{')
|
|
584
|
+
classBraces++;
|
|
585
|
+
else if (ch === '}')
|
|
586
|
+
classBraces--;
|
|
587
|
+
}
|
|
588
|
+
if (classBraces === 0) {
|
|
589
|
+
output.push(curLine);
|
|
590
|
+
}
|
|
591
|
+
i++;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
output.push('');
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
// Check for top-level function
|
|
598
|
+
if (/^(?:export\s+|default\s+|async\s+|declare\s+)*function(?:\s*\*|\s+\w+)/.test(trimmed)) {
|
|
599
|
+
if (docBuffer.length > 0) {
|
|
600
|
+
output.push(...docBuffer);
|
|
601
|
+
docBuffer = [];
|
|
602
|
+
}
|
|
603
|
+
const sigLines = [];
|
|
604
|
+
let fnBraces = 0;
|
|
605
|
+
let hasBody = false;
|
|
606
|
+
while (i < lines.length) {
|
|
607
|
+
const fLine = lines[i];
|
|
608
|
+
sigLines.push(fLine);
|
|
609
|
+
if (fLine.includes('{')) {
|
|
610
|
+
hasBody = true;
|
|
611
|
+
fnBraces = 1;
|
|
612
|
+
i++;
|
|
613
|
+
break;
|
|
614
|
+
}
|
|
615
|
+
if (fLine.trim().endsWith(';')) {
|
|
616
|
+
i++;
|
|
617
|
+
break;
|
|
618
|
+
}
|
|
619
|
+
i++;
|
|
620
|
+
}
|
|
621
|
+
if (hasBody) {
|
|
622
|
+
const sig = sigLines.join('\n');
|
|
623
|
+
const braceIdx = sig.lastIndexOf('{');
|
|
624
|
+
const cleanSig = sig.substring(0, braceIdx).trimEnd();
|
|
625
|
+
output.push(cleanSig + ' { ... }');
|
|
626
|
+
while (i < lines.length && fnBraces > 0) {
|
|
627
|
+
for (const ch of lines[i]) {
|
|
628
|
+
if (ch === '{')
|
|
629
|
+
fnBraces++;
|
|
630
|
+
else if (ch === '}')
|
|
631
|
+
fnBraces--;
|
|
632
|
+
}
|
|
633
|
+
i++;
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
else {
|
|
637
|
+
output.push(sigLines.join('\n'));
|
|
638
|
+
}
|
|
639
|
+
output.push('');
|
|
640
|
+
continue;
|
|
641
|
+
}
|
|
642
|
+
// Check for const / let / var function or export
|
|
643
|
+
if (/^(?:export\s+)?(?:const|let|var)\s+\w+/.test(trimmed)) {
|
|
644
|
+
if (docBuffer.length > 0) {
|
|
645
|
+
output.push(...docBuffer);
|
|
646
|
+
docBuffer = [];
|
|
647
|
+
}
|
|
648
|
+
if (trimmed.includes('=>') && trimmed.includes('{')) {
|
|
649
|
+
const sigLines = [];
|
|
650
|
+
let arrowBraces = 0;
|
|
651
|
+
while (i < lines.length) {
|
|
652
|
+
const aLine = lines[i];
|
|
653
|
+
sigLines.push(aLine);
|
|
654
|
+
if (aLine.includes('{')) {
|
|
655
|
+
arrowBraces = 1;
|
|
656
|
+
i++;
|
|
657
|
+
break;
|
|
658
|
+
}
|
|
659
|
+
i++;
|
|
660
|
+
}
|
|
661
|
+
const sig = sigLines.join('\n');
|
|
662
|
+
const braceIdx = sig.lastIndexOf('{');
|
|
663
|
+
output.push(sig.substring(0, braceIdx).trimEnd() + ' { ... };');
|
|
664
|
+
while (i < lines.length && arrowBraces > 0) {
|
|
665
|
+
for (const ch of lines[i]) {
|
|
666
|
+
if (ch === '{')
|
|
667
|
+
arrowBraces++;
|
|
668
|
+
else if (ch === '}')
|
|
669
|
+
arrowBraces--;
|
|
670
|
+
}
|
|
671
|
+
i++;
|
|
672
|
+
}
|
|
673
|
+
output.push('');
|
|
674
|
+
continue;
|
|
675
|
+
}
|
|
676
|
+
else if (trimmed.includes('=>')) {
|
|
677
|
+
output.push(line);
|
|
678
|
+
i++;
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
else {
|
|
682
|
+
output.push(line);
|
|
683
|
+
i++;
|
|
684
|
+
continue;
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
docBuffer = [];
|
|
688
|
+
i++;
|
|
689
|
+
}
|
|
690
|
+
return output.join('\n').trim();
|
|
691
|
+
}
|
|
692
|
+
/**
|
|
693
|
+
* Extracts a compact declarations outline for Python source files.
|
|
694
|
+
*
|
|
695
|
+
* @param lines - File lines
|
|
696
|
+
* @returns Declarations outline
|
|
697
|
+
*/
|
|
698
|
+
function extractPythonOutline(lines) {
|
|
699
|
+
const output = [];
|
|
700
|
+
let docBuffer = [];
|
|
701
|
+
let i = 0;
|
|
702
|
+
const isDocOrDecorator = (line) => {
|
|
703
|
+
const trimmed = line.trim();
|
|
704
|
+
return trimmed.startsWith('#') || trimmed.startsWith('@');
|
|
705
|
+
};
|
|
706
|
+
while (i < lines.length) {
|
|
707
|
+
const line = lines[i];
|
|
708
|
+
const trimmed = line.trim();
|
|
709
|
+
if (!trimmed) {
|
|
710
|
+
if (docBuffer.length > 0 && docBuffer.every((l) => l.trim().startsWith('#'))) {
|
|
711
|
+
docBuffer = [];
|
|
712
|
+
}
|
|
713
|
+
i++;
|
|
714
|
+
continue;
|
|
715
|
+
}
|
|
716
|
+
if (isDocOrDecorator(line)) {
|
|
717
|
+
docBuffer.push(line);
|
|
718
|
+
i++;
|
|
719
|
+
continue;
|
|
720
|
+
}
|
|
721
|
+
// Class definition
|
|
722
|
+
if (/^class\s+\w+/.test(trimmed)) {
|
|
723
|
+
if (docBuffer.length > 0) {
|
|
724
|
+
output.push(...docBuffer);
|
|
725
|
+
docBuffer = [];
|
|
726
|
+
}
|
|
727
|
+
const classBaseIndent = getIndentation(line);
|
|
728
|
+
const classHeaderLines = [];
|
|
729
|
+
while (i < lines.length) {
|
|
730
|
+
classHeaderLines.push(lines[i]);
|
|
731
|
+
if (lines[i].trim().endsWith(':') || lines[i].includes(':')) {
|
|
732
|
+
i++;
|
|
733
|
+
break;
|
|
734
|
+
}
|
|
735
|
+
i++;
|
|
736
|
+
}
|
|
737
|
+
output.push(classHeaderLines.join('\n'));
|
|
738
|
+
let methodDoc = [];
|
|
739
|
+
let inClassDocstring = false;
|
|
740
|
+
let docstringChar = null;
|
|
741
|
+
while (i < lines.length) {
|
|
742
|
+
const cLine = lines[i];
|
|
743
|
+
const cTrim = cLine.trim();
|
|
744
|
+
const cIndent = getIndentation(cLine);
|
|
745
|
+
if (!cTrim) {
|
|
746
|
+
i++;
|
|
747
|
+
continue;
|
|
748
|
+
}
|
|
749
|
+
if (cIndent <= classBaseIndent && !inClassDocstring) {
|
|
750
|
+
break;
|
|
751
|
+
}
|
|
752
|
+
if (inClassDocstring) {
|
|
753
|
+
output.push(cLine);
|
|
754
|
+
if (docstringChar && (cTrim.endsWith(docstringChar) || cTrim.includes(docstringChar))) {
|
|
755
|
+
inClassDocstring = false;
|
|
756
|
+
}
|
|
757
|
+
i++;
|
|
758
|
+
continue;
|
|
759
|
+
}
|
|
760
|
+
if (cTrim.startsWith('"""') || cTrim.startsWith("'''")) {
|
|
761
|
+
const delim = cTrim.startsWith('"""') ? '"""' : "'''";
|
|
762
|
+
output.push(cLine);
|
|
763
|
+
if (cTrim.length > 3 && cTrim.endsWith(delim) && cTrim.indexOf(delim, 3) !== -1) {
|
|
764
|
+
// single line docstring
|
|
765
|
+
}
|
|
766
|
+
else {
|
|
767
|
+
inClassDocstring = true;
|
|
768
|
+
docstringChar = delim;
|
|
769
|
+
}
|
|
770
|
+
i++;
|
|
771
|
+
continue;
|
|
772
|
+
}
|
|
773
|
+
if (isDocOrDecorator(cLine)) {
|
|
774
|
+
methodDoc.push(cLine);
|
|
775
|
+
i++;
|
|
776
|
+
continue;
|
|
777
|
+
}
|
|
778
|
+
if (/^(?:async\s+)?def\s+\w+/.test(cTrim)) {
|
|
779
|
+
if (methodDoc.length > 0) {
|
|
780
|
+
output.push(...methodDoc);
|
|
781
|
+
methodDoc = [];
|
|
782
|
+
}
|
|
783
|
+
const methodIndent = cIndent;
|
|
784
|
+
const methodSigLines = [];
|
|
785
|
+
while (i < lines.length) {
|
|
786
|
+
methodSigLines.push(lines[i]);
|
|
787
|
+
if (lines[i].trim().endsWith(':') || lines[i].includes(':')) {
|
|
788
|
+
i++;
|
|
789
|
+
break;
|
|
790
|
+
}
|
|
791
|
+
i++;
|
|
792
|
+
}
|
|
793
|
+
output.push(methodSigLines.join('\n'));
|
|
794
|
+
const pad = ' '.repeat(methodIndent + 4);
|
|
795
|
+
output.push(`${pad}...`);
|
|
796
|
+
while (i < lines.length) {
|
|
797
|
+
const bLine = lines[i];
|
|
798
|
+
const bTrim = bLine.trim();
|
|
799
|
+
const bIndent = getIndentation(bLine);
|
|
800
|
+
if (!bTrim) {
|
|
801
|
+
i++;
|
|
802
|
+
continue;
|
|
803
|
+
}
|
|
804
|
+
if (bIndent <= methodIndent)
|
|
805
|
+
break;
|
|
806
|
+
i++;
|
|
807
|
+
}
|
|
808
|
+
continue;
|
|
809
|
+
}
|
|
810
|
+
if (cTrim.includes(':') || cTrim.includes('=')) {
|
|
811
|
+
if (methodDoc.length > 0) {
|
|
812
|
+
output.push(...methodDoc);
|
|
813
|
+
methodDoc = [];
|
|
814
|
+
}
|
|
815
|
+
output.push(cLine);
|
|
816
|
+
i++;
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
methodDoc = [];
|
|
820
|
+
i++;
|
|
821
|
+
}
|
|
822
|
+
output.push('');
|
|
823
|
+
continue;
|
|
824
|
+
}
|
|
825
|
+
// Top-level function
|
|
826
|
+
if (/^(?:async\s+)?def\s+\w+/.test(trimmed)) {
|
|
827
|
+
if (docBuffer.length > 0) {
|
|
828
|
+
output.push(...docBuffer);
|
|
829
|
+
docBuffer = [];
|
|
830
|
+
}
|
|
831
|
+
const fnIndent = getIndentation(line);
|
|
832
|
+
const fnSigLines = [];
|
|
833
|
+
while (i < lines.length) {
|
|
834
|
+
fnSigLines.push(lines[i]);
|
|
835
|
+
if (lines[i].trim().endsWith(':') || lines[i].includes(':')) {
|
|
836
|
+
i++;
|
|
837
|
+
break;
|
|
838
|
+
}
|
|
839
|
+
i++;
|
|
840
|
+
}
|
|
841
|
+
output.push(fnSigLines.join('\n'));
|
|
842
|
+
const pad = ' '.repeat(fnIndent + 4);
|
|
843
|
+
output.push(`${pad}...`);
|
|
844
|
+
while (i < lines.length) {
|
|
845
|
+
const bLine = lines[i];
|
|
846
|
+
const bTrim = bLine.trim();
|
|
847
|
+
const bIndent = getIndentation(bLine);
|
|
848
|
+
if (!bTrim) {
|
|
849
|
+
i++;
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
if (bIndent <= fnIndent)
|
|
853
|
+
break;
|
|
854
|
+
i++;
|
|
855
|
+
}
|
|
856
|
+
output.push('');
|
|
857
|
+
continue;
|
|
858
|
+
}
|
|
859
|
+
// Global variable / type alias
|
|
860
|
+
if (trimmed.includes(':') || trimmed.includes('=')) {
|
|
861
|
+
if (docBuffer.length > 0) {
|
|
862
|
+
output.push(...docBuffer);
|
|
863
|
+
docBuffer = [];
|
|
864
|
+
}
|
|
865
|
+
output.push(line);
|
|
866
|
+
i++;
|
|
867
|
+
continue;
|
|
868
|
+
}
|
|
869
|
+
docBuffer = [];
|
|
870
|
+
i++;
|
|
871
|
+
}
|
|
872
|
+
return output.join('\n').trim();
|
|
873
|
+
}
|
|
874
|
+
/**
|
|
875
|
+
* Extracts a compact declarations outline for Go source files.
|
|
876
|
+
*
|
|
877
|
+
* @param lines - File lines
|
|
878
|
+
* @returns Declarations outline
|
|
879
|
+
*/
|
|
880
|
+
function extractGoOutline(lines) {
|
|
881
|
+
const output = [];
|
|
882
|
+
let docBuffer = [];
|
|
883
|
+
let i = 0;
|
|
884
|
+
const isComment = (line) => {
|
|
885
|
+
const trimmed = line.trim();
|
|
886
|
+
return trimmed.startsWith('//') || trimmed.startsWith('/*') || trimmed.startsWith('*');
|
|
887
|
+
};
|
|
888
|
+
while (i < lines.length) {
|
|
889
|
+
const line = lines[i];
|
|
890
|
+
const trimmed = line.trim();
|
|
891
|
+
if (!trimmed) {
|
|
892
|
+
docBuffer = [];
|
|
893
|
+
i++;
|
|
894
|
+
continue;
|
|
895
|
+
}
|
|
896
|
+
if (isComment(line)) {
|
|
897
|
+
docBuffer.push(line);
|
|
898
|
+
i++;
|
|
899
|
+
continue;
|
|
900
|
+
}
|
|
901
|
+
// Package statement
|
|
902
|
+
if (/^package\s+\w+/.test(trimmed)) {
|
|
903
|
+
output.push(line);
|
|
904
|
+
output.push('');
|
|
905
|
+
docBuffer = [];
|
|
906
|
+
i++;
|
|
907
|
+
continue;
|
|
908
|
+
}
|
|
909
|
+
// Type definition (struct, interface, alias)
|
|
910
|
+
if (/^type\s+\w+/.test(trimmed)) {
|
|
911
|
+
if (docBuffer.length > 0) {
|
|
912
|
+
output.push(...docBuffer);
|
|
913
|
+
docBuffer = [];
|
|
914
|
+
}
|
|
915
|
+
let braces = 0;
|
|
916
|
+
let parens = 0;
|
|
917
|
+
let foundBlock = false;
|
|
918
|
+
while (i < lines.length) {
|
|
919
|
+
for (const ch of lines[i]) {
|
|
920
|
+
if (ch === '{') {
|
|
921
|
+
braces++;
|
|
922
|
+
foundBlock = true;
|
|
923
|
+
}
|
|
924
|
+
else if (ch === '}') {
|
|
925
|
+
braces--;
|
|
926
|
+
}
|
|
927
|
+
else if (ch === '(') {
|
|
928
|
+
parens++;
|
|
929
|
+
foundBlock = true;
|
|
930
|
+
}
|
|
931
|
+
else if (ch === ')') {
|
|
932
|
+
parens--;
|
|
933
|
+
}
|
|
934
|
+
}
|
|
935
|
+
output.push(lines[i]);
|
|
936
|
+
i++;
|
|
937
|
+
if (foundBlock && braces <= 0 && parens <= 0)
|
|
938
|
+
break;
|
|
939
|
+
if (!foundBlock && (lines[i - 1].trim().endsWith(';') || !lines[i - 1].includes('{')))
|
|
940
|
+
break;
|
|
941
|
+
}
|
|
942
|
+
output.push('');
|
|
943
|
+
continue;
|
|
944
|
+
}
|
|
945
|
+
// Const / Var blocks
|
|
946
|
+
if (/^(?:const|var)\s*(?:\(|$)/.test(trimmed)) {
|
|
947
|
+
if (docBuffer.length > 0) {
|
|
948
|
+
output.push(...docBuffer);
|
|
949
|
+
docBuffer = [];
|
|
950
|
+
}
|
|
951
|
+
let parens = 0;
|
|
952
|
+
let foundParen = false;
|
|
953
|
+
while (i < lines.length) {
|
|
954
|
+
for (const ch of lines[i]) {
|
|
955
|
+
if (ch === '(') {
|
|
956
|
+
parens++;
|
|
957
|
+
foundParen = true;
|
|
958
|
+
}
|
|
959
|
+
else if (ch === ')') {
|
|
960
|
+
parens--;
|
|
961
|
+
}
|
|
962
|
+
}
|
|
963
|
+
output.push(lines[i]);
|
|
964
|
+
i++;
|
|
965
|
+
if (foundParen && parens <= 0)
|
|
966
|
+
break;
|
|
967
|
+
if (!foundParen)
|
|
968
|
+
break;
|
|
969
|
+
}
|
|
970
|
+
output.push('');
|
|
971
|
+
continue;
|
|
972
|
+
}
|
|
973
|
+
// Single-line const / var
|
|
974
|
+
if (/^(?:const|var)\s+\w+/.test(trimmed)) {
|
|
975
|
+
if (docBuffer.length > 0) {
|
|
976
|
+
output.push(...docBuffer);
|
|
977
|
+
docBuffer = [];
|
|
978
|
+
}
|
|
979
|
+
output.push(line);
|
|
980
|
+
output.push('');
|
|
981
|
+
i++;
|
|
982
|
+
continue;
|
|
983
|
+
}
|
|
984
|
+
// Function or Method
|
|
985
|
+
if (/^func\s+(?:\([^)]+\)\s+)?\w+/.test(trimmed)) {
|
|
986
|
+
if (docBuffer.length > 0) {
|
|
987
|
+
output.push(...docBuffer);
|
|
988
|
+
docBuffer = [];
|
|
989
|
+
}
|
|
990
|
+
const sigLines = [];
|
|
991
|
+
let fnBraces = 0;
|
|
992
|
+
let hasBody = false;
|
|
993
|
+
while (i < lines.length) {
|
|
994
|
+
const fLine = lines[i];
|
|
995
|
+
sigLines.push(fLine);
|
|
996
|
+
if (fLine.includes('{')) {
|
|
997
|
+
hasBody = true;
|
|
998
|
+
fnBraces = 1;
|
|
999
|
+
i++;
|
|
1000
|
+
break;
|
|
1001
|
+
}
|
|
1002
|
+
i++;
|
|
1003
|
+
}
|
|
1004
|
+
if (hasBody) {
|
|
1005
|
+
const sig = sigLines.join('\n');
|
|
1006
|
+
const braceIdx = sig.lastIndexOf('{');
|
|
1007
|
+
const cleanSig = sig.substring(0, braceIdx).trimEnd();
|
|
1008
|
+
output.push(cleanSig + ' { ... }');
|
|
1009
|
+
while (i < lines.length && fnBraces > 0) {
|
|
1010
|
+
for (const ch of lines[i]) {
|
|
1011
|
+
if (ch === '{')
|
|
1012
|
+
fnBraces++;
|
|
1013
|
+
else if (ch === '}')
|
|
1014
|
+
fnBraces--;
|
|
1015
|
+
}
|
|
1016
|
+
i++;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
else {
|
|
1020
|
+
output.push(sigLines.join('\n'));
|
|
1021
|
+
}
|
|
1022
|
+
output.push('');
|
|
1023
|
+
continue;
|
|
1024
|
+
}
|
|
1025
|
+
docBuffer = [];
|
|
1026
|
+
i++;
|
|
1027
|
+
}
|
|
1028
|
+
return output.join('\n').trim();
|
|
1029
|
+
}
|
|
1030
|
+
/**
|
|
1031
|
+
* Extracts a compact declarations outline for Rust source files.
|
|
1032
|
+
*
|
|
1033
|
+
* @param lines - File lines
|
|
1034
|
+
* @returns Declarations outline
|
|
1035
|
+
*/
|
|
1036
|
+
function extractRustOutline(lines) {
|
|
1037
|
+
const output = [];
|
|
1038
|
+
let docBuffer = [];
|
|
1039
|
+
let i = 0;
|
|
1040
|
+
const isDocOrAttr = (line) => {
|
|
1041
|
+
const trimmed = line.trim();
|
|
1042
|
+
return (trimmed.startsWith('///') ||
|
|
1043
|
+
trimmed.startsWith('//!') ||
|
|
1044
|
+
trimmed.startsWith('//') ||
|
|
1045
|
+
trimmed.startsWith('/*') ||
|
|
1046
|
+
trimmed.startsWith('*') ||
|
|
1047
|
+
trimmed.startsWith('#['));
|
|
1048
|
+
};
|
|
1049
|
+
while (i < lines.length) {
|
|
1050
|
+
const line = lines[i];
|
|
1051
|
+
const trimmed = line.trim();
|
|
1052
|
+
if (!trimmed) {
|
|
1053
|
+
if (docBuffer.length > 0 && docBuffer.every((l) => l.trim().startsWith('//'))) {
|
|
1054
|
+
docBuffer = [];
|
|
1055
|
+
}
|
|
1056
|
+
i++;
|
|
1057
|
+
continue;
|
|
1058
|
+
}
|
|
1059
|
+
if (isDocOrAttr(line)) {
|
|
1060
|
+
docBuffer.push(line);
|
|
1061
|
+
i++;
|
|
1062
|
+
continue;
|
|
1063
|
+
}
|
|
1064
|
+
// Struct, Enum, Trait
|
|
1065
|
+
if (/^(?:pub(?:\([^)]+\))?\s+)?(?:struct|enum|trait)\s+\w+/.test(trimmed)) {
|
|
1066
|
+
if (docBuffer.length > 0) {
|
|
1067
|
+
output.push(...docBuffer);
|
|
1068
|
+
docBuffer = [];
|
|
1069
|
+
}
|
|
1070
|
+
if (trimmed.endsWith(';')) {
|
|
1071
|
+
output.push(line);
|
|
1072
|
+
output.push('');
|
|
1073
|
+
i++;
|
|
1074
|
+
continue;
|
|
1075
|
+
}
|
|
1076
|
+
let braces = 0;
|
|
1077
|
+
let foundOpen = false;
|
|
1078
|
+
while (i < lines.length) {
|
|
1079
|
+
for (const ch of lines[i]) {
|
|
1080
|
+
if (ch === '{') {
|
|
1081
|
+
braces++;
|
|
1082
|
+
foundOpen = true;
|
|
1083
|
+
}
|
|
1084
|
+
else if (ch === '}') {
|
|
1085
|
+
braces--;
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
output.push(lines[i]);
|
|
1089
|
+
i++;
|
|
1090
|
+
if (foundOpen && braces <= 0)
|
|
1091
|
+
break;
|
|
1092
|
+
if (!foundOpen && lines[i - 1].trim().endsWith(';'))
|
|
1093
|
+
break;
|
|
1094
|
+
}
|
|
1095
|
+
output.push('');
|
|
1096
|
+
continue;
|
|
1097
|
+
}
|
|
1098
|
+
// Type alias or Const / Static
|
|
1099
|
+
if (/^(?:pub(?:\([^)]+\))?\s+)?(?:type|const|static)\s+\w+/.test(trimmed)) {
|
|
1100
|
+
if (docBuffer.length > 0) {
|
|
1101
|
+
output.push(...docBuffer);
|
|
1102
|
+
docBuffer = [];
|
|
1103
|
+
}
|
|
1104
|
+
let braces = 0;
|
|
1105
|
+
let parens = 0;
|
|
1106
|
+
while (i < lines.length) {
|
|
1107
|
+
for (const ch of lines[i]) {
|
|
1108
|
+
if (ch === '{')
|
|
1109
|
+
braces++;
|
|
1110
|
+
else if (ch === '}')
|
|
1111
|
+
braces--;
|
|
1112
|
+
else if (ch === '(')
|
|
1113
|
+
parens++;
|
|
1114
|
+
else if (ch === ')')
|
|
1115
|
+
parens--;
|
|
1116
|
+
}
|
|
1117
|
+
output.push(lines[i]);
|
|
1118
|
+
if (braces <= 0 && parens <= 0 && lines[i].includes(';')) {
|
|
1119
|
+
i++;
|
|
1120
|
+
break;
|
|
1121
|
+
}
|
|
1122
|
+
i++;
|
|
1123
|
+
}
|
|
1124
|
+
output.push('');
|
|
1125
|
+
continue;
|
|
1126
|
+
}
|
|
1127
|
+
// Impl block
|
|
1128
|
+
if (/^(?:pub(?:\([^)]+\))?\s+)?impl(?:\s+<[^>]+>)?\s+/.test(trimmed)) {
|
|
1129
|
+
if (docBuffer.length > 0) {
|
|
1130
|
+
output.push(...docBuffer);
|
|
1131
|
+
docBuffer = [];
|
|
1132
|
+
}
|
|
1133
|
+
const implHeaderLines = [];
|
|
1134
|
+
while (i < lines.length) {
|
|
1135
|
+
implHeaderLines.push(lines[i]);
|
|
1136
|
+
if (lines[i].includes('{')) {
|
|
1137
|
+
i++;
|
|
1138
|
+
break;
|
|
1139
|
+
}
|
|
1140
|
+
i++;
|
|
1141
|
+
}
|
|
1142
|
+
output.push(implHeaderLines.join('\n'));
|
|
1143
|
+
let implBraces = 1;
|
|
1144
|
+
let memberDoc = [];
|
|
1145
|
+
while (i < lines.length && implBraces > 0) {
|
|
1146
|
+
const curLine = lines[i];
|
|
1147
|
+
const curTrim = curLine.trim();
|
|
1148
|
+
if (!curTrim) {
|
|
1149
|
+
memberDoc = [];
|
|
1150
|
+
i++;
|
|
1151
|
+
continue;
|
|
1152
|
+
}
|
|
1153
|
+
if (isDocOrAttr(curLine)) {
|
|
1154
|
+
memberDoc.push(curLine);
|
|
1155
|
+
i++;
|
|
1156
|
+
continue;
|
|
1157
|
+
}
|
|
1158
|
+
if (curTrim === '}' && implBraces === 1) {
|
|
1159
|
+
output.push(curLine);
|
|
1160
|
+
i++;
|
|
1161
|
+
break;
|
|
1162
|
+
}
|
|
1163
|
+
if (/^(?:pub(?:\([^)]+\))?\s+)?(?:async\s+|const\s+|unsafe\s+|extern\s+)*fn\s+\w+/.test(curTrim)) {
|
|
1164
|
+
if (memberDoc.length > 0) {
|
|
1165
|
+
output.push(...memberDoc);
|
|
1166
|
+
memberDoc = [];
|
|
1167
|
+
}
|
|
1168
|
+
const sigLines = [];
|
|
1169
|
+
let fnBraces = 0;
|
|
1170
|
+
let hasBody = false;
|
|
1171
|
+
while (i < lines.length) {
|
|
1172
|
+
const fLine = lines[i];
|
|
1173
|
+
sigLines.push(fLine);
|
|
1174
|
+
if (fLine.includes('{')) {
|
|
1175
|
+
hasBody = true;
|
|
1176
|
+
fnBraces = 1;
|
|
1177
|
+
i++;
|
|
1178
|
+
break;
|
|
1179
|
+
}
|
|
1180
|
+
if (fLine.trim().endsWith(';')) {
|
|
1181
|
+
i++;
|
|
1182
|
+
break;
|
|
1183
|
+
}
|
|
1184
|
+
i++;
|
|
1185
|
+
}
|
|
1186
|
+
if (hasBody) {
|
|
1187
|
+
const sig = sigLines.join('\n');
|
|
1188
|
+
const braceIdx = sig.lastIndexOf('{');
|
|
1189
|
+
const cleanSig = sig.substring(0, braceIdx).trimEnd();
|
|
1190
|
+
output.push(cleanSig + ' { ... }');
|
|
1191
|
+
while (i < lines.length && fnBraces > 0) {
|
|
1192
|
+
for (const ch of lines[i]) {
|
|
1193
|
+
if (ch === '{')
|
|
1194
|
+
fnBraces++;
|
|
1195
|
+
else if (ch === '}')
|
|
1196
|
+
fnBraces--;
|
|
1197
|
+
}
|
|
1198
|
+
i++;
|
|
1199
|
+
}
|
|
1200
|
+
}
|
|
1201
|
+
else {
|
|
1202
|
+
output.push(sigLines.join('\n'));
|
|
1203
|
+
}
|
|
1204
|
+
continue;
|
|
1205
|
+
}
|
|
1206
|
+
else if (/^(?:pub(?:\([^)]+\))?\s+)?(?:type|const)\s+\w+/.test(curTrim)) {
|
|
1207
|
+
if (memberDoc.length > 0) {
|
|
1208
|
+
output.push(...memberDoc);
|
|
1209
|
+
memberDoc = [];
|
|
1210
|
+
}
|
|
1211
|
+
output.push(curLine);
|
|
1212
|
+
i++;
|
|
1213
|
+
continue;
|
|
1214
|
+
}
|
|
1215
|
+
else {
|
|
1216
|
+
for (const ch of curLine) {
|
|
1217
|
+
if (ch === '{')
|
|
1218
|
+
implBraces++;
|
|
1219
|
+
else if (ch === '}')
|
|
1220
|
+
implBraces--;
|
|
1221
|
+
}
|
|
1222
|
+
if (implBraces === 0) {
|
|
1223
|
+
output.push(curLine);
|
|
1224
|
+
}
|
|
1225
|
+
i++;
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
output.push('');
|
|
1229
|
+
continue;
|
|
1230
|
+
}
|
|
1231
|
+
// Free fn
|
|
1232
|
+
if (/^(?:pub(?:\([^)]+\))?\s+)?(?:async\s+|const\s+|unsafe\s+|extern\s+)*fn\s+\w+/.test(trimmed)) {
|
|
1233
|
+
if (docBuffer.length > 0) {
|
|
1234
|
+
output.push(...docBuffer);
|
|
1235
|
+
docBuffer = [];
|
|
1236
|
+
}
|
|
1237
|
+
const sigLines = [];
|
|
1238
|
+
let fnBraces = 0;
|
|
1239
|
+
let hasBody = false;
|
|
1240
|
+
while (i < lines.length) {
|
|
1241
|
+
const fLine = lines[i];
|
|
1242
|
+
sigLines.push(fLine);
|
|
1243
|
+
if (fLine.includes('{')) {
|
|
1244
|
+
hasBody = true;
|
|
1245
|
+
fnBraces = 1;
|
|
1246
|
+
i++;
|
|
1247
|
+
break;
|
|
1248
|
+
}
|
|
1249
|
+
if (fLine.trim().endsWith(';')) {
|
|
1250
|
+
i++;
|
|
1251
|
+
break;
|
|
1252
|
+
}
|
|
1253
|
+
i++;
|
|
1254
|
+
}
|
|
1255
|
+
if (hasBody) {
|
|
1256
|
+
const sig = sigLines.join('\n');
|
|
1257
|
+
const braceIdx = sig.lastIndexOf('{');
|
|
1258
|
+
const cleanSig = sig.substring(0, braceIdx).trimEnd();
|
|
1259
|
+
output.push(cleanSig + ' { ... }');
|
|
1260
|
+
while (i < lines.length && fnBraces > 0) {
|
|
1261
|
+
for (const ch of lines[i]) {
|
|
1262
|
+
if (ch === '{')
|
|
1263
|
+
fnBraces++;
|
|
1264
|
+
else if (ch === '}')
|
|
1265
|
+
fnBraces--;
|
|
1266
|
+
}
|
|
1267
|
+
i++;
|
|
1268
|
+
}
|
|
1269
|
+
}
|
|
1270
|
+
else {
|
|
1271
|
+
output.push(sigLines.join('\n'));
|
|
1272
|
+
}
|
|
1273
|
+
output.push('');
|
|
1274
|
+
continue;
|
|
1275
|
+
}
|
|
1276
|
+
docBuffer = [];
|
|
1277
|
+
i++;
|
|
1278
|
+
}
|
|
1279
|
+
return output.join('\n').trim();
|
|
1280
|
+
}
|
|
1281
|
+
/**
|
|
1282
|
+
* Extracts a compact declarations outline (type/interface definitions, class skeletons,
|
|
1283
|
+
* and function signatures without full implementation bodies) across TypeScript/JavaScript,
|
|
1284
|
+
* Python, Go, and Rust.
|
|
1285
|
+
*
|
|
1286
|
+
* This dramatically reduces token usage when injecting multi-file context into prompts.
|
|
1287
|
+
*
|
|
1288
|
+
* @param content - Raw source code content
|
|
1289
|
+
* @param filePath - File path used to infer language and syntax rules
|
|
1290
|
+
* @returns Compact declarations outline string
|
|
1291
|
+
*/
|
|
1292
|
+
export function extractDeclarationsOutline(content, filePath) {
|
|
1293
|
+
if (!content || !content.trim())
|
|
1294
|
+
return '';
|
|
1295
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
1296
|
+
const lines = content.split('\n');
|
|
1297
|
+
switch (ext) {
|
|
1298
|
+
case '.ts':
|
|
1299
|
+
case '.tsx':
|
|
1300
|
+
case '.js':
|
|
1301
|
+
case '.jsx':
|
|
1302
|
+
case '.mjs':
|
|
1303
|
+
case '.cjs':
|
|
1304
|
+
case '.mts':
|
|
1305
|
+
case '.cts':
|
|
1306
|
+
return extractTsJsOutline(lines);
|
|
1307
|
+
case '.py':
|
|
1308
|
+
case '.pyi':
|
|
1309
|
+
return extractPythonOutline(lines);
|
|
1310
|
+
case '.go':
|
|
1311
|
+
return extractGoOutline(lines);
|
|
1312
|
+
case '.rs':
|
|
1313
|
+
return extractRustOutline(lines);
|
|
1314
|
+
default:
|
|
1315
|
+
return content.trim();
|
|
1316
|
+
}
|
|
1317
|
+
}
|