@vfarcic/dot-ai 0.49.0 → 0.51.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.
Files changed (43) hide show
  1. package/README.md +49 -2
  2. package/dist/core/base-vector-service.d.ts +4 -4
  3. package/dist/core/base-vector-service.d.ts.map +1 -1
  4. package/dist/core/base-vector-service.js +31 -39
  5. package/dist/core/capabilities.d.ts +71 -0
  6. package/dist/core/capabilities.d.ts.map +1 -0
  7. package/dist/core/capabilities.js +208 -0
  8. package/dist/core/capability-vector-service.d.ts +80 -0
  9. package/dist/core/capability-vector-service.d.ts.map +1 -0
  10. package/dist/core/capability-vector-service.js +142 -0
  11. package/dist/core/claude.d.ts +14 -1
  12. package/dist/core/claude.d.ts.map +1 -1
  13. package/dist/core/claude.js +109 -13
  14. package/dist/core/discovery.d.ts +0 -7
  15. package/dist/core/discovery.d.ts.map +1 -1
  16. package/dist/core/discovery.js +0 -18
  17. package/dist/core/embedding-service.d.ts +3 -3
  18. package/dist/core/embedding-service.d.ts.map +1 -1
  19. package/dist/core/embedding-service.js +6 -7
  20. package/dist/core/index.d.ts +1 -0
  21. package/dist/core/index.d.ts.map +1 -1
  22. package/dist/core/index.js +5 -4
  23. package/dist/core/schema.d.ts +11 -30
  24. package/dist/core/schema.d.ts.map +1 -1
  25. package/dist/core/schema.js +107 -126
  26. package/dist/core/vector-db-service.d.ts +6 -0
  27. package/dist/core/vector-db-service.d.ts.map +1 -1
  28. package/dist/core/vector-db-service.js +40 -10
  29. package/dist/tools/organizational-data.d.ts +5 -3
  30. package/dist/tools/organizational-data.d.ts.map +1 -1
  31. package/dist/tools/organizational-data.js +1714 -52
  32. package/dist/tools/recommend.d.ts.map +1 -1
  33. package/dist/tools/recommend.js +3 -7
  34. package/dist/tools/version.d.ts +9 -0
  35. package/dist/tools/version.d.ts.map +1 -1
  36. package/dist/tools/version.js +115 -5
  37. package/package.json +1 -1
  38. package/prompts/capability-inference.md +122 -0
  39. package/prompts/doc-testing-test-section.md +40 -2
  40. package/prompts/resource-selection.md +10 -3
  41. package/shared-prompts/prd-update-decisions.md +9 -0
  42. package/shared-prompts/prd-update-progress.md +33 -0
  43. package/prompts/concept-extraction.md +0 -95
@@ -49,29 +49,33 @@ const error_handling_1 = require("../core/error-handling");
49
49
  // Import only what we need - other imports removed as they're no longer used with Vector DB
50
50
  const pattern_creation_session_1 = require("../core/pattern-creation-session");
51
51
  const index_1 = require("../core/index");
52
+ const capabilities_1 = require("../core/capabilities");
52
53
  const session_utils_1 = require("../core/session-utils");
53
54
  const fs = __importStar(require("fs"));
54
55
  const path = __importStar(require("path"));
55
56
  // Tool metadata for MCP registration
56
57
  exports.ORGANIZATIONAL_DATA_TOOL_NAME = 'manageOrgData';
57
- exports.ORGANIZATIONAL_DATA_TOOL_DESCRIPTION = 'Unified tool for managing cluster data: organizational patterns (available now), resource capabilities (PRD #48), and resource dependencies (PRD #49). For patterns: supports step-by-step creation workflow. For capabilities/dependencies: returns implementation status. Use dataType parameter to specify what to manage: "pattern" for organizational patterns, "capabilities" for resource capabilities, "dependencies" for resource dependencies.';
58
- // Extensible schema - supports patterns, capabilities, and dependencies
58
+ exports.ORGANIZATIONAL_DATA_TOOL_DESCRIPTION = 'Unified tool for managing cluster data: organizational patterns and resource capabilities. For patterns: supports step-by-step creation workflow. For capabilities: supports scan, list, get, delete, deleteAll, and progress operations for cluster resource capability discovery and management. Use dataType parameter to specify what to manage: "pattern" for organizational patterns, "capabilities" for resource capabilities.';
59
+ // Extensible schema - supports patterns and capabilities
59
60
  exports.ORGANIZATIONAL_DATA_TOOL_INPUT_SCHEMA = {
60
- dataType: zod_1.z.enum(['pattern', 'capabilities', 'dependencies']).describe('Type of cluster data to manage: pattern (organizational patterns), capabilities (resource capabilities), dependencies (resource dependencies)'),
61
- operation: zod_1.z.enum(['create', 'list', 'get', 'delete', 'scan', 'analyze']).describe('Operation to perform on the cluster data'),
61
+ dataType: zod_1.z.enum(['pattern', 'capabilities']).describe('Type of cluster data to manage: "pattern" for organizational patterns, "capabilities" for resource capabilities'),
62
+ operation: zod_1.z.enum(['create', 'list', 'get', 'delete', 'deleteAll', 'scan', 'analyze', 'progress', 'search']).describe('Operation to perform on the cluster data'),
62
63
  // Workflow fields for step-by-step pattern creation
63
- sessionId: zod_1.z.string().optional().describe('Pattern creation session ID (for continuing multi-step workflow)'),
64
+ sessionId: zod_1.z.string().optional().describe('Session ID (required for continuing workflow steps, optional for progress - uses latest session if omitted)'),
65
+ step: zod_1.z.string().optional().describe('Current workflow step (required when sessionId is provided)'),
64
66
  response: zod_1.z.string().optional().describe('User response to previous workflow step question'),
65
- // Generic fields for get/delete operations
66
- id: zod_1.z.string().optional().describe('Data item ID (required for get/delete operations)'),
67
+ // Generic fields for get/delete/search operations
68
+ id: zod_1.z.string().optional().describe('Data item ID (required for get/delete operations) or search query (required for search operations)'),
67
69
  // Generic fields for list operations
68
70
  limit: zod_1.z.number().optional().describe('Maximum number of items to return (default: 10)'),
69
- // Resource-specific fields (for capabilities and dependencies)
71
+ // Resource-specific fields (for capabilities operations)
70
72
  resource: zod_1.z.object({
71
73
  kind: zod_1.z.string(),
72
74
  group: zod_1.z.string(),
73
75
  apiVersion: zod_1.z.string()
74
- }).optional().describe('Kubernetes resource reference (for capabilities/dependencies operations)')
76
+ }).optional().describe('Kubernetes resource reference (for capabilities operations)'),
77
+ // Resource list for specific resource scanning
78
+ resourceList: zod_1.z.string().optional().describe('Comma-separated list of resources to scan (format: Kind.group or Kind for core resources)')
75
79
  };
76
80
  /**
77
81
  * Get Vector DB-based pattern service with optional embedding support
@@ -426,59 +430,1720 @@ async function handlePatternOperation(operation, args, logger, requestId) {
426
430
  }
427
431
  }
428
432
  /**
429
- * Handle capabilities operations (placeholder for PRD #48)
433
+ * Create standardized capability scan completion response
430
434
  */
431
- async function handleCapabilitiesOperation(operation, args, logger, requestId) {
432
- logger.info('Capabilities operation requested', { requestId, operation });
435
+ function createCapabilityScanCompletionResponse(sessionId, totalProcessed, successful, failed, processingTime, mode, stopped = false) {
436
+ let message;
437
+ if (stopped) {
438
+ message = `⏹️ Capability scan stopped by user after processing ${successful} of ${totalProcessed} resources.`;
439
+ }
440
+ else if (failed > 0) {
441
+ message = `✅ Capability scan completed with ${failed} errors. ${successful}/${totalProcessed} resources processed successfully.`;
442
+ }
443
+ else {
444
+ message = `✅ Capability scan completed successfully! Processed ${totalProcessed} resources.`;
445
+ }
433
446
  return {
434
- success: false,
435
- operation,
447
+ success: true,
448
+ operation: 'scan',
436
449
  dataType: 'capabilities',
437
- error: {
438
- message: 'Resource capabilities management not yet implemented',
439
- details: 'This feature is planned for PRD #48 - Resource Capabilities Discovery & Integration',
440
- status: 'coming-soon',
441
- implementationPlan: {
442
- prd: 'PRD #48',
443
- description: 'Kubernetes resource capability discovery and semantic matching',
444
- expectedFeatures: [
445
- 'Cluster resource scanning',
446
- 'Capability inference from schemas',
447
- 'Semantic resource matching',
448
- 'Vector DB storage and search'
449
- ]
450
- }
450
+ mode: mode,
451
+ step: 'complete',
452
+ sessionId: sessionId,
453
+ summary: {
454
+ totalScanned: totalProcessed,
455
+ successful: successful,
456
+ failed: failed,
457
+ processingTime: processingTime,
458
+ ...(stopped && { stopped: true })
451
459
  },
452
- message: 'Capabilities management will be available after PRD #48 implementation'
460
+ message: message,
461
+ availableOptions: {
462
+ viewResults: "Use 'list' operation to browse all discovered capabilities",
463
+ getDetails: "Use 'get' operation with capability ID to view specific capability details",
464
+ checkStatus: successful > 0 ? "Capabilities are now available for AI-powered recommendations" : "No capabilities were stored"
465
+ },
466
+ userNote: "The above options are available for you to choose from - the system will not execute them automatically."
467
+ };
468
+ }
469
+ /**
470
+ * Handle capabilities operations - PRD #48 Implementation
471
+ */
472
+ async function handleCapabilitiesOperation(operation, args, logger, requestId) {
473
+ logger.info('Capabilities operation requested', { requestId, operation });
474
+ switch (operation) {
475
+ case 'scan':
476
+ return await handleCapabilityScan(args, logger, requestId);
477
+ case 'progress':
478
+ return await handleCapabilityProgress(args, logger, requestId);
479
+ case 'list':
480
+ case 'get':
481
+ case 'search':
482
+ case 'delete':
483
+ case 'deleteAll': {
484
+ // Create and initialize capability service for list/get/delete operations
485
+ const capabilityService = new index_1.CapabilityVectorService();
486
+ try {
487
+ const vectorDBHealthy = await capabilityService.healthCheck();
488
+ if (!vectorDBHealthy) {
489
+ return {
490
+ success: false,
491
+ operation,
492
+ dataType: 'capabilities',
493
+ error: {
494
+ message: 'Vector DB (Qdrant) connection required',
495
+ details: 'Capability operations require a working Qdrant connection.',
496
+ setup: {
497
+ docker: 'docker run -p 6333:6333 qdrant/qdrant',
498
+ config: 'export QDRANT_URL=http://localhost:6333'
499
+ }
500
+ }
501
+ };
502
+ }
503
+ await capabilityService.initialize();
504
+ if (operation === 'list') {
505
+ return await handleCapabilityList(args, logger, requestId, capabilityService);
506
+ }
507
+ else if (operation === 'get') {
508
+ return await handleCapabilityGet(args, logger, requestId, capabilityService);
509
+ }
510
+ else if (operation === 'search') {
511
+ return await handleCapabilitySearch(args, logger, requestId, capabilityService);
512
+ }
513
+ else if (operation === 'delete') {
514
+ return await handleCapabilityDelete(args, logger, requestId, capabilityService);
515
+ }
516
+ else if (operation === 'deleteAll') {
517
+ return await handleCapabilityDeleteAll(args, logger, requestId, capabilityService);
518
+ }
519
+ else {
520
+ // This should never happen since we already check the operation in the switch case
521
+ throw new Error(`Unexpected operation: ${operation}`);
522
+ }
523
+ }
524
+ catch (error) {
525
+ logger.error(`Capability ${operation} operation failed`, error, { requestId });
526
+ return {
527
+ success: false,
528
+ operation,
529
+ dataType: 'capabilities',
530
+ error: {
531
+ message: `Capability ${operation} failed`,
532
+ details: error instanceof Error ? error.message : String(error)
533
+ }
534
+ };
535
+ }
536
+ }
537
+ default:
538
+ return {
539
+ success: false,
540
+ operation,
541
+ dataType: 'capabilities',
542
+ error: {
543
+ message: `Unsupported capabilities operation: ${operation}`,
544
+ supportedOperations: ['scan', 'list', 'get', 'delete', 'deleteAll']
545
+ }
546
+ };
547
+ }
548
+ }
549
+ /**
550
+ * Convert numeric response to option value
551
+ */
552
+ function parseNumericResponse(response, validOptions) {
553
+ // If response is a number, map it to the corresponding option
554
+ const num = parseInt(response, 10);
555
+ if (!isNaN(num) && num >= 1 && num <= validOptions.length) {
556
+ return validOptions[num - 1]; // Convert 1-based to 0-based index
557
+ }
558
+ // Otherwise return the original response (for backward compatibility)
559
+ return response;
560
+ }
561
+ /**
562
+ * Get session file path following established pattern
563
+ */
564
+ function getCapabilitySessionPath(sessionId, args) {
565
+ const sessionDir = (0, session_utils_1.getAndValidateSessionDirectory)(args, false);
566
+ const sessionSubDir = path.join(sessionDir, 'capability-sessions');
567
+ // Ensure capability-sessions subdirectory exists
568
+ if (!fs.existsSync(sessionSubDir)) {
569
+ fs.mkdirSync(sessionSubDir, { recursive: true });
570
+ }
571
+ return path.join(sessionSubDir, `${sessionId}.json`);
572
+ }
573
+ /**
574
+ * Load session from file system following established pattern
575
+ */
576
+ function loadCapabilitySession(sessionId, args) {
577
+ try {
578
+ const sessionPath = getCapabilitySessionPath(sessionId, args);
579
+ if (!fs.existsSync(sessionPath)) {
580
+ return null;
581
+ }
582
+ const sessionData = fs.readFileSync(sessionPath, 'utf8');
583
+ const session = JSON.parse(sessionData);
584
+ // Update last activity
585
+ session.lastActivity = new Date().toISOString();
586
+ saveCapabilitySession(session, args);
587
+ return session;
588
+ }
589
+ catch (error) {
590
+ // Log error but don't throw - return null to create new session
591
+ return null;
592
+ }
593
+ }
594
+ /**
595
+ * Save session to file system following established pattern
596
+ */
597
+ function saveCapabilitySession(session, args) {
598
+ try {
599
+ const sessionPath = getCapabilitySessionPath(session.sessionId, args);
600
+ fs.writeFileSync(sessionPath, JSON.stringify(session, null, 2), 'utf8');
601
+ }
602
+ catch (error) {
603
+ // Log error but don't throw - workflow can continue
604
+ console.warn('Failed to save capability session:', error);
605
+ }
606
+ }
607
+ /**
608
+ * Get or create session with file-based persistence
609
+ */
610
+ function getOrCreateCapabilitySession(sessionId, args, logger, requestId) {
611
+ if (sessionId) {
612
+ const existing = loadCapabilitySession(sessionId, args);
613
+ if (existing) {
614
+ logger.info('Loaded existing capability session', {
615
+ requestId,
616
+ sessionId,
617
+ currentStep: existing.currentStep
618
+ });
619
+ return existing;
620
+ }
621
+ }
622
+ // Create new session
623
+ const newSessionId = sessionId || `cap-scan-${Date.now()}`;
624
+ const session = {
625
+ sessionId: newSessionId,
626
+ currentStep: 'resource-selection',
627
+ startedAt: new Date().toISOString(),
628
+ lastActivity: new Date().toISOString()
453
629
  };
630
+ saveCapabilitySession(session, args);
631
+ logger.info('Created new capability session', {
632
+ requestId,
633
+ sessionId: newSessionId,
634
+ currentStep: session.currentStep
635
+ });
636
+ return session;
637
+ }
638
+ /**
639
+ * Validate client is on the correct step - step parameter is MANDATORY
640
+ */
641
+ function validateCapabilityStep(session, clientStep) {
642
+ if (!clientStep) {
643
+ return {
644
+ valid: false,
645
+ error: `Step parameter is required. You are currently on step '${session.currentStep}'. Please call with step='${session.currentStep}' and appropriate parameters.`
646
+ };
647
+ }
648
+ if (clientStep !== session.currentStep) {
649
+ return {
650
+ valid: false,
651
+ error: `Step mismatch: you're on step '${session.currentStep}', but called with step '${clientStep}'. Please call with step='${session.currentStep}'.`
652
+ };
653
+ }
654
+ return { valid: true };
655
+ }
656
+ /**
657
+ * Transition session to next step with proper state updates
658
+ */
659
+ function transitionCapabilitySession(session, nextStep, updates, args) {
660
+ session.currentStep = nextStep;
661
+ session.lastActivity = new Date().toISOString();
662
+ if (updates) {
663
+ Object.assign(session, updates);
664
+ }
665
+ saveCapabilitySession(session, args);
666
+ }
667
+ /**
668
+ * Clean up session file after successful completion
669
+ */
670
+ function cleanupCapabilitySession(session, args, logger, requestId) {
671
+ try {
672
+ const sessionPath = getCapabilitySessionPath(session.sessionId, args);
673
+ if (fs.existsSync(sessionPath)) {
674
+ fs.unlinkSync(sessionPath);
675
+ logger.info('Capability session cleaned up after completion', {
676
+ requestId,
677
+ sessionId: session.sessionId
678
+ });
679
+ }
680
+ }
681
+ catch (error) {
682
+ // Log cleanup failure but don't fail the operation
683
+ logger.warn('Failed to cleanup capability session file', {
684
+ requestId,
685
+ sessionId: session.sessionId,
686
+ error: error instanceof Error ? error.message : String(error)
687
+ });
688
+ }
454
689
  }
455
690
  /**
456
- * Handle dependencies operations (placeholder for PRD #49)
691
+ * Handle capability scanning workflow with step-based state management
457
692
  */
458
- async function handleDependenciesOperation(operation, args, logger, requestId) {
459
- logger.info('Dependencies operation requested', { requestId, operation });
693
+ async function handleCapabilityScan(args, logger, requestId) {
694
+ // Validate Vector DB and embedding service dependencies upfront
695
+ // This prevents users from going through the entire workflow only to fail at storage
696
+ const capabilityService = new index_1.CapabilityVectorService();
697
+ // Check Vector DB connection and initialize collection
698
+ try {
699
+ const vectorDBHealthy = await capabilityService.healthCheck();
700
+ if (!vectorDBHealthy) {
701
+ return {
702
+ success: false,
703
+ operation: 'scan',
704
+ dataType: 'capabilities',
705
+ error: {
706
+ message: 'Vector DB (Qdrant) connection required for capability management',
707
+ details: 'Capability scanning requires Qdrant for storing and searching capabilities. The system cannot proceed without a working Vector DB connection.',
708
+ setup: {
709
+ required: 'Qdrant server must be running',
710
+ default: 'http://localhost:6333',
711
+ docker: 'docker run -p 6333:6333 qdrant/qdrant',
712
+ config: 'export QDRANT_URL=http://localhost:6333'
713
+ },
714
+ currentConfig: {
715
+ QDRANT_URL: process.env.QDRANT_URL || 'http://localhost:6333 (default)',
716
+ status: 'connection failed'
717
+ }
718
+ }
719
+ };
720
+ }
721
+ }
722
+ catch (error) {
723
+ logger.error('Vector DB connection check failed', error, { requestId });
724
+ return {
725
+ success: false,
726
+ operation: 'scan',
727
+ dataType: 'capabilities',
728
+ error: {
729
+ message: 'Vector DB (Qdrant) connection failed',
730
+ details: 'Cannot establish connection to Qdrant server. Please ensure Qdrant is running and accessible.',
731
+ technicalDetails: error instanceof Error ? error.message : String(error),
732
+ setup: {
733
+ required: 'Qdrant server must be running',
734
+ default: 'http://localhost:6333',
735
+ docker: 'docker run -p 6333:6333 qdrant/qdrant',
736
+ config: 'export QDRANT_URL=http://localhost:6333'
737
+ },
738
+ currentConfig: {
739
+ QDRANT_URL: process.env.QDRANT_URL || 'http://localhost:6333 (default)',
740
+ status: 'connection error'
741
+ }
742
+ }
743
+ };
744
+ }
745
+ // Initialize the collection now that we know Qdrant is healthy
746
+ try {
747
+ await capabilityService.initialize();
748
+ }
749
+ catch (error) {
750
+ logger.error('Failed to initialize capabilities collection', error, { requestId });
751
+ return {
752
+ success: false,
753
+ operation: 'scan',
754
+ dataType: 'capabilities',
755
+ error: {
756
+ message: 'Vector DB collection initialization failed',
757
+ details: 'Could not create or access the capabilities collection in Qdrant.',
758
+ technicalDetails: error instanceof Error ? error.message : String(error),
759
+ setup: {
760
+ possibleCauses: [
761
+ 'Qdrant version compatibility issue',
762
+ 'Insufficient permissions',
763
+ 'Collection dimension mismatch',
764
+ 'Corrupted existing collection'
765
+ ],
766
+ recommendations: [
767
+ 'Check Qdrant logs for detailed error information',
768
+ 'Verify Qdrant version compatibility',
769
+ 'Consider removing existing capabilities collection if corrupted'
770
+ ]
771
+ }
772
+ }
773
+ };
774
+ }
775
+ // Check embedding service (OpenAI API) availability
776
+ const embeddingCheck = await validateEmbeddingService(logger, requestId);
777
+ if (!embeddingCheck.success) {
778
+ return {
779
+ success: false,
780
+ operation: 'scan',
781
+ dataType: 'capabilities',
782
+ error: {
783
+ message: 'OpenAI API required for capability semantic search',
784
+ details: 'Capability scanning requires OpenAI embeddings for semantic search functionality. The system cannot proceed without proper OpenAI API configuration.',
785
+ ...embeddingCheck.error,
786
+ recommendation: 'Set up OpenAI API key to enable full capability scanning with semantic search'
787
+ }
788
+ };
789
+ }
790
+ logger.info('Capability scanning dependencies validated', {
791
+ requestId,
792
+ vectorDB: 'healthy',
793
+ embeddings: 'available'
794
+ });
795
+ // Get or create session with step-based state management
796
+ const session = getOrCreateCapabilitySession(args.sessionId, args, logger, requestId);
797
+ // Validate client is on correct step - only for existing sessions
798
+ // New sessions (no sessionId provided) are allowed to start without step parameter
799
+ // If sessionId is provided, client must follow step-based protocol
800
+ const clientProvidedSessionId = !!args.sessionId;
801
+ if (clientProvidedSessionId) {
802
+ const stepValidation = validateCapabilityStep(session, args.step);
803
+ if (!stepValidation.valid) {
804
+ return {
805
+ success: false,
806
+ operation: 'scan',
807
+ dataType: 'capabilities',
808
+ error: {
809
+ message: 'Step validation failed',
810
+ details: stepValidation.error,
811
+ currentStep: session.currentStep,
812
+ expectedCall: `Call with step='${session.currentStep}' and appropriate parameters`
813
+ }
814
+ };
815
+ }
816
+ }
817
+ // Handle workflow based on current step
818
+ switch (session.currentStep) {
819
+ case 'resource-selection':
820
+ return await handleResourceSelection(session, args, logger, requestId);
821
+ case 'resource-specification':
822
+ return await handleResourceSpecification(session, args, logger, requestId);
823
+ case 'processing-mode':
824
+ return await handleProcessingMode(session, args, logger, requestId, capabilityService);
825
+ case 'scanning':
826
+ return await handleScanning(session, args, logger, requestId, capabilityService);
827
+ case 'complete':
828
+ return {
829
+ success: false,
830
+ operation: 'scan',
831
+ dataType: 'capabilities',
832
+ error: {
833
+ message: 'Workflow already complete',
834
+ details: `Session ${session.sessionId} has already completed capability scanning.`,
835
+ sessionId: session.sessionId
836
+ }
837
+ };
838
+ default:
839
+ return {
840
+ success: false,
841
+ operation: 'scan',
842
+ dataType: 'capabilities',
843
+ error: {
844
+ message: 'Invalid workflow state',
845
+ details: `Unknown step: ${session.currentStep}`,
846
+ currentStep: session.currentStep
847
+ }
848
+ };
849
+ }
850
+ }
851
+ /**
852
+ * Handle resource selection step
853
+ */
854
+ async function handleResourceSelection(session, args, _logger, _requestId) {
855
+ if (!args.response) {
856
+ // Show initial resource selection prompt
857
+ return {
858
+ success: true,
859
+ operation: 'scan',
860
+ dataType: 'capabilities',
861
+ // CRITICAL: Put required parameters at top level for maximum visibility
862
+ REQUIRED_NEXT_CALL: {
863
+ tool: 'dot-ai:manageOrgData',
864
+ parameters: {
865
+ dataType: 'capabilities',
866
+ operation: 'scan',
867
+ sessionId: session.sessionId,
868
+ step: 'resource-selection', // MANDATORY PARAMETER
869
+ response: 'user_choice_here' // Replace with actual user choice
870
+ },
871
+ note: 'The step parameter is MANDATORY when sessionId is provided'
872
+ },
873
+ workflow: {
874
+ step: 'resource-selection',
875
+ question: 'Scan all cluster resources or specify subset?',
876
+ options: [
877
+ { number: 1, value: 'all', display: '1. all - Scan all available cluster resources' },
878
+ { number: 2, value: 'specific', display: '2. specific - Specify particular resource types to scan' }
879
+ ],
880
+ sessionId: session.sessionId,
881
+ instruction: 'IMPORTANT: You MUST ask the user to make a choice. Do NOT automatically select an option.',
882
+ userPrompt: 'Would you like to scan all cluster resources or specify a subset?',
883
+ clientInstructions: {
884
+ behavior: 'interactive',
885
+ requirement: 'Ask user to choose between options',
886
+ prohibit: 'Do not auto-select options',
887
+ nextStep: `Call with step='resource-selection', sessionId='${session.sessionId}', and response parameter containing the semantic value (all or specific)`,
888
+ responseFormat: 'Convert user input to semantic values: 1→all, 2→specific, or pass through semantic words directly',
889
+ requiredParameters: {
890
+ step: 'resource-selection',
891
+ sessionId: session.sessionId,
892
+ response: 'user choice (all or specific)'
893
+ }
894
+ }
895
+ }
896
+ };
897
+ }
898
+ // Process user response
899
+ const normalizedResponse = parseNumericResponse(args.response, ['all', 'specific']);
900
+ if (normalizedResponse === 'all') {
901
+ // Transition to processing mode for all resources
902
+ transitionCapabilitySession(session, 'processing-mode', { selectedResources: 'all' }, args);
903
+ return {
904
+ success: true,
905
+ operation: 'scan',
906
+ dataType: 'capabilities',
907
+ // CRITICAL: Put required parameters at top level for maximum visibility
908
+ REQUIRED_NEXT_CALL: {
909
+ tool: 'dot-ai:manageOrgData',
910
+ parameters: {
911
+ dataType: 'capabilities',
912
+ operation: 'scan',
913
+ sessionId: session.sessionId,
914
+ step: 'processing-mode', // MANDATORY PARAMETER
915
+ response: 'user_choice_here' // Replace with actual user choice
916
+ },
917
+ note: 'The step parameter is MANDATORY when sessionId is provided'
918
+ },
919
+ workflow: {
920
+ step: 'processing-mode',
921
+ question: 'Processing mode: auto (batch process) or manual (review each)?',
922
+ options: [
923
+ { number: 1, value: 'auto', display: '1. auto - Batch process automatically' },
924
+ { number: 2, value: 'manual', display: '2. manual - Review each step' }
925
+ ],
926
+ sessionId: session.sessionId,
927
+ selectedResources: 'all',
928
+ instruction: 'IMPORTANT: You MUST ask the user to make a choice. Do NOT automatically select a processing mode.',
929
+ userPrompt: 'How would you like to process the resources?',
930
+ clientInstructions: {
931
+ behavior: 'interactive',
932
+ requirement: 'Ask user to choose processing mode',
933
+ prohibit: 'Do not auto-select processing mode',
934
+ nextStep: `Call with step='processing-mode', sessionId='${session.sessionId}', and response parameter containing the semantic value (auto or manual)`,
935
+ responseFormat: 'Convert user input to semantic values: 1→auto, 2→manual, or pass through semantic words directly',
936
+ requiredParameters: {
937
+ step: 'processing-mode',
938
+ sessionId: session.sessionId,
939
+ response: 'user choice (auto or manual)'
940
+ }
941
+ }
942
+ }
943
+ };
944
+ }
945
+ if (normalizedResponse === 'specific') {
946
+ // Transition to resource specification
947
+ transitionCapabilitySession(session, 'resource-specification', {}, args);
948
+ return {
949
+ success: true,
950
+ operation: 'scan',
951
+ dataType: 'capabilities',
952
+ // CRITICAL: Put required parameters at top level for maximum visibility
953
+ REQUIRED_NEXT_CALL: {
954
+ tool: 'dot-ai:manageOrgData',
955
+ parameters: {
956
+ dataType: 'capabilities',
957
+ operation: 'scan',
958
+ sessionId: session.sessionId,
959
+ step: 'resource-specification', // MANDATORY PARAMETER
960
+ resourceList: 'user_resource_list_here' // Replace with actual resource list
961
+ },
962
+ note: 'The step parameter is MANDATORY when sessionId is provided'
963
+ },
964
+ workflow: {
965
+ step: 'resource-specification',
966
+ question: 'Which resources would you like to scan?',
967
+ sessionId: session.sessionId,
968
+ instruction: 'IMPORTANT: You MUST ask the user to specify which resources to scan. Do NOT provide a default list.',
969
+ userPrompt: 'Please specify which resources you want to scan.',
970
+ resourceFormat: {
971
+ description: 'Specify resources using Kubernetes naming convention',
972
+ format: 'Kind.group for CRDs, Kind for core resources',
973
+ examples: {
974
+ crds: ['SQL.devopstoolkit.live', 'Server.dbforpostgresql.azure.upbound.io'],
975
+ core: ['Pod', 'Service', 'ConfigMap'],
976
+ apps: ['Deployment.apps', 'StatefulSet.apps']
977
+ },
978
+ input: 'Comma-separated list (e.g.: SQL.devopstoolkit.live, Deployment.apps, Pod)'
979
+ },
980
+ clientInstructions: {
981
+ behavior: 'interactive',
982
+ requirement: 'Ask user to provide specific resource list',
983
+ prohibit: 'Do not suggest or auto-select resources',
984
+ nextStep: `Call with step='resource-specification', sessionId='${session.sessionId}', and resourceList parameter`,
985
+ requiredParameters: {
986
+ step: 'resource-specification',
987
+ sessionId: session.sessionId,
988
+ resourceList: 'comma-separated list of resources'
989
+ }
990
+ }
991
+ }
992
+ };
993
+ }
460
994
  return {
461
995
  success: false,
462
- operation,
463
- dataType: 'dependencies',
996
+ operation: 'scan',
997
+ dataType: 'capabilities',
464
998
  error: {
465
- message: 'Resource dependencies management not yet implemented',
466
- details: 'This feature is planned for PRD #49 - Resource Dependencies Discovery & Integration',
467
- status: 'coming-soon',
468
- implementationPlan: {
469
- prd: 'PRD #49',
470
- description: 'Resource dependency discovery and complete solution assembly',
471
- expectedFeatures: [
472
- 'Dependency relationship discovery',
473
- 'Complete solution assembly',
474
- 'Deployment order optimization',
475
- 'Vector DB storage and search'
476
- ]
999
+ message: 'Invalid resource selection response',
1000
+ details: `Expected 'all' or 'specific', got: ${args.response}`,
1001
+ currentStep: session.currentStep
1002
+ }
1003
+ };
1004
+ }
1005
+ /**
1006
+ * Handle resource specification step
1007
+ */
1008
+ async function handleResourceSpecification(session, args, _logger, _requestId) {
1009
+ if (!args.resourceList) {
1010
+ return {
1011
+ success: false,
1012
+ operation: 'scan',
1013
+ dataType: 'capabilities',
1014
+ error: {
1015
+ message: 'Missing resource list',
1016
+ details: 'Expected resourceList parameter with comma-separated resource names',
1017
+ currentStep: session.currentStep,
1018
+ expectedCall: `Call with step='resource-specification' and resourceList parameter`
1019
+ }
1020
+ };
1021
+ }
1022
+ // Parse and validate resource list
1023
+ const resources = args.resourceList.split(',').map((r) => r.trim()).filter((r) => r.length > 0);
1024
+ if (resources.length === 0) {
1025
+ return {
1026
+ success: false,
1027
+ operation: 'scan',
1028
+ dataType: 'capabilities',
1029
+ error: {
1030
+ message: 'Empty resource list',
1031
+ details: 'Resource list cannot be empty',
1032
+ currentStep: session.currentStep
477
1033
  }
1034
+ };
1035
+ }
1036
+ // Transition to processing mode for specific resources
1037
+ transitionCapabilitySession(session, 'processing-mode', {
1038
+ selectedResources: resources,
1039
+ resourceList: args.resourceList
1040
+ }, args);
1041
+ return {
1042
+ success: true,
1043
+ operation: 'scan',
1044
+ dataType: 'capabilities',
1045
+ // CRITICAL: Put required parameters at top level for maximum visibility
1046
+ REQUIRED_NEXT_CALL: {
1047
+ tool: 'dot-ai:manageOrgData',
1048
+ parameters: {
1049
+ dataType: 'capabilities',
1050
+ operation: 'scan',
1051
+ sessionId: session.sessionId,
1052
+ step: 'processing-mode', // MANDATORY PARAMETER
1053
+ response: 'user_choice_here' // Replace with actual user choice
1054
+ },
1055
+ note: 'The step parameter is MANDATORY when sessionId is provided'
478
1056
  },
479
- message: 'Dependencies management will be available after PRD #49 implementation'
1057
+ workflow: {
1058
+ step: 'processing-mode',
1059
+ question: `Processing mode for ${resources.length} selected resources: auto (batch process) or manual (review each)?`,
1060
+ options: [
1061
+ { number: 1, value: 'auto', display: '1. auto - Batch process automatically' },
1062
+ { number: 2, value: 'manual', display: '2. manual - Review each step' }
1063
+ ],
1064
+ sessionId: session.sessionId,
1065
+ selectedResources: resources,
1066
+ instruction: 'IMPORTANT: You MUST ask the user to choose processing mode for the specified resources.',
1067
+ userPrompt: `How would you like to process these ${resources.length} resources?`,
1068
+ clientInstructions: {
1069
+ behavior: 'interactive',
1070
+ requirement: 'Ask user to choose processing mode for specific resources',
1071
+ context: `Processing ${resources.length} user-specified resources: ${resources.join(', ')}`,
1072
+ prohibit: 'Do not auto-select processing mode',
1073
+ nextStep: `Call with step='processing-mode', sessionId='${session.sessionId}', and response parameter containing the semantic value (auto or manual)`,
1074
+ responseFormat: 'Convert user input to semantic values: 1→auto, 2→manual, or pass through semantic words directly',
1075
+ requiredParameters: {
1076
+ step: 'processing-mode',
1077
+ sessionId: session.sessionId,
1078
+ response: 'user choice (auto or manual)'
1079
+ }
1080
+ }
1081
+ }
480
1082
  };
481
1083
  }
1084
+ /**
1085
+ * Handle processing mode step
1086
+ */
1087
+ async function handleProcessingMode(session, args, logger, requestId, capabilityService) {
1088
+ if (!args.response) {
1089
+ return {
1090
+ success: false,
1091
+ operation: 'scan',
1092
+ dataType: 'capabilities',
1093
+ error: {
1094
+ message: 'Missing processing mode response',
1095
+ details: 'Expected response parameter with processing mode choice',
1096
+ currentStep: session.currentStep,
1097
+ expectedCall: `Call with step='processing-mode' and response parameter (auto or manual)`
1098
+ }
1099
+ };
1100
+ }
1101
+ // Process user response
1102
+ const processingModeResponse = parseNumericResponse(args.response, ['auto', 'manual']);
1103
+ if (processingModeResponse !== 'auto' && processingModeResponse !== 'manual') {
1104
+ return {
1105
+ success: false,
1106
+ operation: 'scan',
1107
+ dataType: 'capabilities',
1108
+ error: {
1109
+ message: 'Invalid processing mode response',
1110
+ details: `Expected 'auto' or 'manual', got: ${args.response}`,
1111
+ currentStep: session.currentStep
1112
+ }
1113
+ };
1114
+ }
1115
+ // Transition to scanning with processing mode and initialize resource tracking
1116
+ transitionCapabilitySession(session, 'scanning', {
1117
+ processingMode: processingModeResponse,
1118
+ currentResourceIndex: 0 // Start with first resource
1119
+ }, args);
1120
+ // Begin actual capability scanning - clear response from previous step
1121
+ return await handleScanning(session, { ...args, response: undefined }, logger, requestId, capabilityService);
1122
+ }
1123
+ /**
1124
+ * Create user-friendly error message for resource definition failures
1125
+ */
1126
+ function createResourceDefinitionErrorMessage(resourceName, error) {
1127
+ const errorMessage = error instanceof Error ? error.message : String(error);
1128
+ let userFriendlyMessage = `Cannot analyze resource '${resourceName}': `;
1129
+ if (errorMessage.includes('not found') || errorMessage.includes('NotFound')) {
1130
+ userFriendlyMessage += `Resource does not exist in cluster. Please ensure the CRD is installed.`;
1131
+ }
1132
+ else if (errorMessage.includes('connection refused') || errorMessage.includes('timeout')) {
1133
+ userFriendlyMessage += `Cannot connect to Kubernetes cluster. Please check cluster connectivity.`;
1134
+ }
1135
+ else if (errorMessage.includes('forbidden') || errorMessage.includes('Forbidden')) {
1136
+ userFriendlyMessage += `Insufficient permissions to read resource definitions. Please check RBAC settings.`;
1137
+ }
1138
+ else {
1139
+ userFriendlyMessage += `${errorMessage}`;
1140
+ }
1141
+ return userFriendlyMessage;
1142
+ }
1143
+ /**
1144
+ * Handle scanning step (actual capability analysis)
1145
+ */
1146
+ async function handleScanning(session, args, logger, requestId, capabilityService) {
1147
+ try {
1148
+ // If this is a response to a manual mode preview, handle it first
1149
+ if (session.processingMode === 'manual' && args.response) {
1150
+ const userResponse = parseNumericResponse(args.response, ['yes', 'no', 'stop']);
1151
+ if (userResponse === 'stop') {
1152
+ // User wants to stop scanning
1153
+ transitionCapabilitySession(session, 'complete', {}, args);
1154
+ cleanupCapabilitySession(session, args, logger, requestId);
1155
+ const currentIndex = session.currentResourceIndex || 0;
1156
+ const totalResources = Array.isArray(session.selectedResources) ? session.selectedResources.length : 1;
1157
+ return createCapabilityScanCompletionResponse(session.sessionId, totalResources, currentIndex, // Resources processed so far
1158
+ 0, 'stopped interactively', 'manual', true // stopped = true
1159
+ );
1160
+ }
1161
+ if (userResponse === 'yes' || userResponse === 'no') {
1162
+ // TODO: If 'yes', store the capability in Vector DB (Milestone 2)
1163
+ // For now, just log the decision
1164
+ logger.info(`User ${userResponse === 'yes' ? 'accepted' : 'skipped'} capability for resource`, {
1165
+ requestId,
1166
+ sessionId: session.sessionId,
1167
+ resourceIndex: session.currentResourceIndex,
1168
+ decision: userResponse
1169
+ });
1170
+ // Move to the next resource
1171
+ const nextIndex = (session.currentResourceIndex || 0) + 1;
1172
+ transitionCapabilitySession(session, 'scanning', { currentResourceIndex: nextIndex }, args);
1173
+ // Continue processing (will handle the next resource or complete if done)
1174
+ return await handleScanning(session, { ...args, response: undefined }, logger, requestId, capabilityService);
1175
+ }
1176
+ // Invalid response
1177
+ return {
1178
+ success: false,
1179
+ operation: 'scan',
1180
+ dataType: 'capabilities',
1181
+ error: {
1182
+ message: 'Invalid response to capability preview',
1183
+ details: `Expected 'yes', 'no', or 'stop', got: ${args.response}`,
1184
+ currentStep: session.currentStep
1185
+ }
1186
+ };
1187
+ }
1188
+ // Import capability engine
1189
+ const { CapabilityInferenceEngine } = await Promise.resolve().then(() => __importStar(require('../core/capabilities')));
1190
+ const { ClaudeIntegration } = await Promise.resolve().then(() => __importStar(require('../core/claude')));
1191
+ // Validate Claude API key
1192
+ const apiKey = process.env.ANTHROPIC_API_KEY;
1193
+ if (!apiKey) {
1194
+ return {
1195
+ success: false,
1196
+ operation: 'scan',
1197
+ dataType: 'capabilities',
1198
+ error: {
1199
+ message: 'ANTHROPIC_API_KEY required for capability inference',
1200
+ details: 'Set ANTHROPIC_API_KEY environment variable to enable AI-powered capability analysis'
1201
+ }
1202
+ };
1203
+ }
1204
+ // Initialize capability engine
1205
+ const claudeIntegration = new ClaudeIntegration(apiKey);
1206
+ const engine = new CapabilityInferenceEngine(claudeIntegration, logger);
1207
+ // Get the resource to analyze
1208
+ let resourceName;
1209
+ let currentIndex;
1210
+ let totalResources;
1211
+ if (session.selectedResources === 'all') {
1212
+ // For 'all' mode, discover actual cluster resources first
1213
+ try {
1214
+ logger.info('Discovering all cluster resources for capability scanning', { requestId, sessionId: session.sessionId });
1215
+ // Import discovery engine
1216
+ const { KubernetesDiscovery } = await Promise.resolve().then(() => __importStar(require('../core/discovery')));
1217
+ const discovery = new KubernetesDiscovery();
1218
+ await discovery.connect();
1219
+ // Discover all available resources
1220
+ const resourceMap = await discovery.discoverResources();
1221
+ const allResources = [...resourceMap.resources, ...resourceMap.custom];
1222
+ // Extract resource names for capability analysis
1223
+ const discoveredResourceNames = allResources.map(resource => {
1224
+ // For CRDs (custom resources), prioritize full name format
1225
+ if (resource.name && resource.name.includes('.')) {
1226
+ return resource.name;
1227
+ }
1228
+ // For standard resources, use kind
1229
+ if (resource.kind) {
1230
+ return resource.kind;
1231
+ }
1232
+ // Fallback to name if no kind
1233
+ if (resource.name) {
1234
+ return resource.name;
1235
+ }
1236
+ return 'unknown-resource';
1237
+ }).filter(name => name !== 'unknown-resource');
1238
+ logger.info('Discovered cluster resources for capability scanning', {
1239
+ requestId,
1240
+ sessionId: session.sessionId,
1241
+ totalDiscovered: discoveredResourceNames.length,
1242
+ sampleResources: discoveredResourceNames.slice(0, 5)
1243
+ });
1244
+ if (discoveredResourceNames.length === 0) {
1245
+ return {
1246
+ success: false,
1247
+ operation: 'scan',
1248
+ dataType: 'capabilities',
1249
+ error: {
1250
+ message: 'No resources discovered in cluster',
1251
+ details: 'Cluster resource discovery returned empty results. Check cluster connectivity and permissions.',
1252
+ sessionId: session.sessionId
1253
+ }
1254
+ };
1255
+ }
1256
+ // Update session with discovered resources and start batch processing
1257
+ transitionCapabilitySession(session, 'scanning', {
1258
+ selectedResources: discoveredResourceNames,
1259
+ processingMode: session.processingMode,
1260
+ currentResourceIndex: 0
1261
+ }, args);
1262
+ // Continue to batch processing with discovered resources
1263
+ resourceName = discoveredResourceNames[0]; // First resource for manual mode
1264
+ currentIndex = 0;
1265
+ totalResources = discoveredResourceNames.length;
1266
+ }
1267
+ catch (error) {
1268
+ logger.error('Failed to discover cluster resources', error, {
1269
+ requestId,
1270
+ sessionId: session.sessionId
1271
+ });
1272
+ return {
1273
+ success: false,
1274
+ operation: 'scan',
1275
+ dataType: 'capabilities',
1276
+ error: {
1277
+ message: 'Cluster resource discovery failed',
1278
+ details: error instanceof Error ? error.message : String(error),
1279
+ sessionId: session.sessionId,
1280
+ suggestedActions: [
1281
+ 'Check cluster connectivity',
1282
+ 'Verify kubectl access permissions',
1283
+ 'Try specifying specific resources instead of "all"'
1284
+ ]
1285
+ }
1286
+ };
1287
+ }
1288
+ }
1289
+ else if (Array.isArray(session.selectedResources)) {
1290
+ // Get the current resource based on currentResourceIndex
1291
+ currentIndex = session.currentResourceIndex || 0;
1292
+ totalResources = session.selectedResources.length;
1293
+ if (currentIndex >= totalResources) {
1294
+ // All resources processed - mark as complete
1295
+ transitionCapabilitySession(session, 'complete', {}, args);
1296
+ cleanupCapabilitySession(session, args, logger, requestId);
1297
+ return createCapabilityScanCompletionResponse(session.sessionId, totalResources, totalResources, // Assume all successful for manual mode
1298
+ 0, 'completed interactively', 'manual');
1299
+ }
1300
+ resourceName = session.selectedResources[currentIndex];
1301
+ if (!resourceName) {
1302
+ throw new Error(`No resource found at index ${currentIndex}`);
1303
+ }
1304
+ }
1305
+ else {
1306
+ throw new Error('Invalid selectedResources in session state');
1307
+ }
1308
+ // Get complete resource definition for comprehensive analysis
1309
+ let resourceDefinition;
1310
+ try {
1311
+ // Import and connect to discovery engine for kubectl access
1312
+ const { KubernetesDiscovery } = await Promise.resolve().then(() => __importStar(require('../core/discovery')));
1313
+ const discovery = new KubernetesDiscovery();
1314
+ await discovery.connect();
1315
+ // Get complete CRD definition if it's a custom resource
1316
+ if (resourceName.includes('.')) {
1317
+ const crdOutput = await discovery.executeKubectl(['get', 'crd', resourceName, '-o', 'yaml']);
1318
+ resourceDefinition = crdOutput;
1319
+ logger.info('Found complete CRD definition for capability analysis', {
1320
+ requestId,
1321
+ sessionId: session.sessionId,
1322
+ resource: resourceName,
1323
+ hasDefinition: !!resourceDefinition,
1324
+ definitionSize: resourceDefinition?.length || 0
1325
+ });
1326
+ }
1327
+ else {
1328
+ // For core resources, use kubectl explain to get schema information
1329
+ const explainOutput = await discovery.explainResource(resourceName);
1330
+ resourceDefinition = explainOutput;
1331
+ logger.info('Found core resource explanation for capability analysis', {
1332
+ requestId,
1333
+ sessionId: session.sessionId,
1334
+ resource: resourceName,
1335
+ hasDefinition: !!resourceDefinition
1336
+ });
1337
+ }
1338
+ }
1339
+ catch (error) {
1340
+ logger.error('Failed to retrieve resource definition for capability analysis', error, {
1341
+ requestId,
1342
+ sessionId: session.sessionId,
1343
+ resource: resourceName
1344
+ });
1345
+ throw new Error(createResourceDefinitionErrorMessage(resourceName, error));
1346
+ }
1347
+ logger.info('Analyzing resource for capability inference', {
1348
+ requestId,
1349
+ sessionId: session.sessionId,
1350
+ resource: resourceName,
1351
+ mode: session.processingMode
1352
+ });
1353
+ if (session.processingMode === 'manual') {
1354
+ // Manual mode: Show capability data for user review
1355
+ const capability = await engine.inferCapabilities(resourceName, resourceDefinition);
1356
+ const capabilityId = CapabilityInferenceEngine.generateCapabilityId(resourceName);
1357
+ return {
1358
+ success: true,
1359
+ operation: 'scan',
1360
+ dataType: 'capabilities',
1361
+ mode: 'manual',
1362
+ step: 'scanning',
1363
+ sessionId: session.sessionId,
1364
+ preview: {
1365
+ resource: resourceName,
1366
+ resourceIndex: `${currentIndex + 1}/${totalResources}`,
1367
+ id: capabilityId,
1368
+ data: capability,
1369
+ question: 'Continue storing this capability?',
1370
+ options: [
1371
+ { number: 1, value: 'yes', display: '1. yes - Store this capability' },
1372
+ { number: 2, value: 'no', display: '2. no - Skip this resource' },
1373
+ { number: 3, value: 'stop', display: '3. stop - End scanning process' }
1374
+ ],
1375
+ instruction: 'Review the capability analysis results before storing',
1376
+ clientInstructions: {
1377
+ behavior: 'interactive',
1378
+ requirement: 'Ask user to review capability data and decide on storage',
1379
+ nextStep: `Call with step='scanning' and response parameter containing their choice (yes/no/stop)`,
1380
+ responseFormat: 'Convert user input to semantic values: 1→yes, 2→no, 3→stop'
1381
+ }
1382
+ }
1383
+ };
1384
+ }
1385
+ else {
1386
+ // Auto mode: Process ALL resources in batch without user interaction
1387
+ // At this point, selectedResources should always be an array (either discovered or specified)
1388
+ if (!Array.isArray(session.selectedResources)) {
1389
+ throw new Error(`Invalid selectedResources state: expected array, got ${typeof session.selectedResources}. This indicates a bug in resource discovery.`);
1390
+ }
1391
+ const resources = session.selectedResources;
1392
+ const totalResources = resources.length;
1393
+ const processedResults = [];
1394
+ const errors = [];
1395
+ logger.info('Starting auto batch processing', {
1396
+ requestId,
1397
+ sessionId: session.sessionId,
1398
+ totalResources,
1399
+ resources: resources
1400
+ });
1401
+ // Initialize progress tracking
1402
+ const startTime = Date.now();
1403
+ const updateProgress = (current, currentResource, successful, failed, recentErrors) => {
1404
+ const elapsed = Date.now() - startTime;
1405
+ const percentage = Math.round((current / totalResources) * 100);
1406
+ // Calculate estimated time remaining
1407
+ let estimatedTimeRemaining;
1408
+ if (current > 0) {
1409
+ const avgTimePerResource = elapsed / current;
1410
+ const remainingResources = totalResources - current;
1411
+ const estimatedRemainingMs = remainingResources * avgTimePerResource;
1412
+ const estimatedMinutes = Math.round(estimatedRemainingMs / 60000 * 10) / 10;
1413
+ estimatedTimeRemaining = estimatedMinutes > 1 ?
1414
+ `${estimatedMinutes} minutes` :
1415
+ `${Math.round(estimatedRemainingMs / 1000)} seconds`;
1416
+ }
1417
+ const progressData = {
1418
+ status: 'processing',
1419
+ current: current,
1420
+ total: totalResources,
1421
+ percentage: percentage,
1422
+ currentResource: currentResource,
1423
+ startedAt: new Date(startTime).toISOString(),
1424
+ lastUpdated: new Date().toISOString(),
1425
+ estimatedTimeRemaining,
1426
+ successfulResources: successful,
1427
+ failedResources: failed,
1428
+ errors: recentErrors.slice(-5) // Keep last 5 errors for troubleshooting
1429
+ };
1430
+ // Update session file with progress
1431
+ transitionCapabilitySession(session, 'scanning', {
1432
+ processingMode: session.processingMode,
1433
+ selectedResources: session.selectedResources,
1434
+ currentResourceIndex: current - 1,
1435
+ progress: progressData
1436
+ }, args);
1437
+ };
1438
+ // Setup kubectl access for getting complete resource definitions
1439
+ let discovery;
1440
+ try {
1441
+ const { KubernetesDiscovery } = await Promise.resolve().then(() => __importStar(require('../core/discovery')));
1442
+ discovery = new KubernetesDiscovery();
1443
+ await discovery.connect();
1444
+ logger.info('Connected to Kubernetes for batch resource definition retrieval', {
1445
+ requestId,
1446
+ sessionId: session.sessionId
1447
+ });
1448
+ }
1449
+ catch (error) {
1450
+ logger.warn('Could not connect to Kubernetes for batch processing, falling back to name-based inference', {
1451
+ requestId,
1452
+ sessionId: session.sessionId,
1453
+ error: error instanceof Error ? error.message : String(error)
1454
+ });
1455
+ }
1456
+ // Process each resource in the batch with progress tracking
1457
+ for (let i = 0; i < resources.length; i++) {
1458
+ const currentResource = resources[i];
1459
+ // Get complete resource definition for this resource
1460
+ let currentResourceDefinition;
1461
+ if (discovery) {
1462
+ try {
1463
+ if (currentResource.includes('.')) {
1464
+ // Get complete CRD definition
1465
+ currentResourceDefinition = await discovery.executeKubectl(['get', 'crd', currentResource, '-o', 'yaml']);
1466
+ }
1467
+ else {
1468
+ // Get core resource explanation
1469
+ currentResourceDefinition = await discovery.explainResource(currentResource);
1470
+ }
1471
+ }
1472
+ catch (error) {
1473
+ logger.error(`Failed to get resource definition for ${currentResource}`, error, {
1474
+ requestId,
1475
+ sessionId: session.sessionId,
1476
+ resource: currentResource
1477
+ });
1478
+ // Add to errors array and skip processing this resource
1479
+ errors.push({
1480
+ resource: currentResource,
1481
+ error: createResourceDefinitionErrorMessage(currentResource, error),
1482
+ timestamp: new Date().toISOString()
1483
+ });
1484
+ // Skip processing this resource
1485
+ continue;
1486
+ }
1487
+ }
1488
+ // Update progress before processing
1489
+ updateProgress(i + 1, currentResource, processedResults.length, errors.length, errors);
1490
+ try {
1491
+ logger.info(`Processing resource ${i + 1}/${totalResources}`, {
1492
+ requestId,
1493
+ sessionId: session.sessionId,
1494
+ resource: currentResource,
1495
+ percentage: Math.round(((i + 1) / totalResources) * 100)
1496
+ });
1497
+ const capability = await engine.inferCapabilities(currentResource, currentResourceDefinition);
1498
+ const capabilityId = CapabilityInferenceEngine.generateCapabilityId(currentResource);
1499
+ // Store capability in Vector DB
1500
+ await capabilityService.storeCapability(capability);
1501
+ processedResults.push({
1502
+ resource: currentResource,
1503
+ id: capabilityId,
1504
+ capabilities: capability.capabilities,
1505
+ providers: capability.providers,
1506
+ complexity: capability.complexity,
1507
+ confidence: capability.confidence
1508
+ });
1509
+ logger.info(`Successfully processed resource ${i + 1}/${totalResources}`, {
1510
+ requestId,
1511
+ sessionId: session.sessionId,
1512
+ resource: currentResource,
1513
+ capabilitiesFound: capability.capabilities.length,
1514
+ percentage: Math.round(((i + 1) / totalResources) * 100)
1515
+ });
1516
+ }
1517
+ catch (error) {
1518
+ const errorMessage = error instanceof Error ? error.message : String(error);
1519
+ const errorDetail = {
1520
+ resource: currentResource,
1521
+ error: errorMessage,
1522
+ index: i + 1,
1523
+ timestamp: new Date().toISOString()
1524
+ };
1525
+ logger.error(`Failed to process resource ${i + 1}/${totalResources}`, error, {
1526
+ requestId,
1527
+ sessionId: session.sessionId,
1528
+ resource: currentResource,
1529
+ percentage: Math.round(((i + 1) / totalResources) * 100)
1530
+ });
1531
+ errors.push(errorDetail);
1532
+ }
1533
+ }
1534
+ // Final progress update - mark as completed
1535
+ const finalElapsed = Date.now() - startTime;
1536
+ const finalMinutes = Math.round(finalElapsed / 60000 * 10) / 10;
1537
+ const successful = processedResults.length;
1538
+ const failed = errors.length;
1539
+ const completionData = {
1540
+ status: 'completed',
1541
+ current: totalResources,
1542
+ total: totalResources,
1543
+ percentage: 100,
1544
+ currentResource: 'Processing complete',
1545
+ startedAt: new Date(startTime).toISOString(),
1546
+ lastUpdated: new Date().toISOString(),
1547
+ completedAt: new Date().toISOString(),
1548
+ totalProcessingTime: finalMinutes > 1 ? `${finalMinutes} minutes` : `${Math.round(finalElapsed / 1000)} seconds`,
1549
+ successfulResources: successful,
1550
+ failedResources: failed,
1551
+ errors: errors.slice(-5)
1552
+ };
1553
+ // Update session with completion status
1554
+ transitionCapabilitySession(session, 'complete', {
1555
+ progress: completionData
1556
+ }, args);
1557
+ logger.info('Auto batch processing completed', {
1558
+ requestId,
1559
+ sessionId: session.sessionId,
1560
+ processed: totalResources,
1561
+ successful,
1562
+ failed,
1563
+ processingTime: completionData.totalProcessingTime
1564
+ });
1565
+ // Clean up session file after a brief delay to allow progress viewing
1566
+ setTimeout(() => {
1567
+ cleanupCapabilitySession(session, args, logger, requestId);
1568
+ }, 30000); // Keep for 30 seconds after completion
1569
+ return createCapabilityScanCompletionResponse(session.sessionId, totalResources, successful, failed, completionData.totalProcessingTime || 'completed', 'auto');
1570
+ }
1571
+ }
1572
+ catch (error) {
1573
+ logger.error('Capability scanning failed', error, {
1574
+ requestId,
1575
+ sessionId: session.sessionId,
1576
+ resource: (error && typeof error === 'object' && 'resourceName' in error) ? error.resourceName : 'unknown',
1577
+ step: session.currentStep
1578
+ });
1579
+ // Clean up session on error
1580
+ cleanupCapabilitySession(session, args, logger, requestId);
1581
+ return {
1582
+ success: false,
1583
+ operation: 'scan',
1584
+ dataType: 'capabilities',
1585
+ error: {
1586
+ message: 'Capability scanning failed',
1587
+ details: error instanceof Error ? error.message : String(error)
1588
+ }
1589
+ };
1590
+ }
1591
+ }
1592
+ /**
1593
+ * Handle capability listing (placeholder for future implementation)
1594
+ */
1595
+ async function handleCapabilityList(args, logger, requestId, capabilityService) {
1596
+ try {
1597
+ // Get all capabilities with optional limit
1598
+ const limit = args.limit || 10;
1599
+ const capabilities = await capabilityService.getAllCapabilities(limit);
1600
+ const count = await capabilityService.getCapabilitiesCount();
1601
+ logger.info('Capabilities listed successfully', {
1602
+ requestId,
1603
+ count: capabilities.length,
1604
+ totalCount: count,
1605
+ limit
1606
+ });
1607
+ return {
1608
+ success: true,
1609
+ operation: 'list',
1610
+ dataType: 'capabilities',
1611
+ data: {
1612
+ capabilities: capabilities.map(cap => ({
1613
+ id: cap.id,
1614
+ resourceName: cap.resourceName,
1615
+ capabilities: cap.capabilities,
1616
+ description: cap.description.length > 100 ? cap.description.substring(0, 100) + '...' : cap.description,
1617
+ complexity: cap.complexity,
1618
+ confidence: cap.confidence,
1619
+ analyzedAt: cap.analyzedAt
1620
+ })),
1621
+ totalCount: count,
1622
+ returnedCount: capabilities.length,
1623
+ limit
1624
+ },
1625
+ message: `Retrieved ${capabilities.length} capabilities (${count} total)`,
1626
+ clientInstructions: {
1627
+ behavior: 'Display capability list with IDs prominently visible for user reference',
1628
+ requirement: 'Each capability must show: ID, resource name, main capabilities, and description',
1629
+ format: 'List format with ID clearly labeled (e.g., "ID: abc123") so users can reference specific capabilities',
1630
+ prohibit: 'Do not hide or omit capability IDs from the display - users need them for get operations'
1631
+ }
1632
+ };
1633
+ }
1634
+ catch (error) {
1635
+ logger.error('Failed to list capabilities', error, {
1636
+ requestId
1637
+ });
1638
+ return {
1639
+ success: false,
1640
+ operation: 'list',
1641
+ dataType: 'capabilities',
1642
+ error: {
1643
+ message: 'Failed to list capabilities',
1644
+ details: error instanceof Error ? error.message : String(error)
1645
+ }
1646
+ };
1647
+ }
1648
+ }
1649
+ /**
1650
+ * Handle capability retrieval (placeholder for future implementation)
1651
+ */
1652
+ async function handleCapabilityGet(args, logger, requestId, capabilityService) {
1653
+ try {
1654
+ // Validate required parameters
1655
+ if (!args.id) {
1656
+ return {
1657
+ success: false,
1658
+ operation: 'get',
1659
+ dataType: 'capabilities',
1660
+ error: {
1661
+ message: 'Missing required parameter: id',
1662
+ details: 'Specify id to retrieve capability data',
1663
+ example: { id: 'capability-id-example' }
1664
+ }
1665
+ };
1666
+ }
1667
+ // Get capability by ID
1668
+ const capability = await capabilityService.getCapability(args.id);
1669
+ if (!capability) {
1670
+ logger.warn('Capability not found', {
1671
+ requestId,
1672
+ capabilityId: args.id
1673
+ });
1674
+ return {
1675
+ success: false,
1676
+ operation: 'get',
1677
+ dataType: 'capabilities',
1678
+ error: {
1679
+ message: `Capability not found for ID: ${args.id}`,
1680
+ details: 'Resource capability may not have been scanned yet',
1681
+ suggestion: 'Use scan operation to analyze this resource first'
1682
+ }
1683
+ };
1684
+ }
1685
+ logger.info('Capability retrieved successfully', {
1686
+ requestId,
1687
+ capabilityId: args.id,
1688
+ resourceName: capability.resourceName,
1689
+ capabilitiesFound: capability.capabilities.length,
1690
+ confidence: capability.confidence
1691
+ });
1692
+ return {
1693
+ success: true,
1694
+ operation: 'get',
1695
+ dataType: 'capabilities',
1696
+ data: capability,
1697
+ message: `Retrieved capability data for ${capability.resourceName} (ID: ${args.id})`,
1698
+ clientInstructions: {
1699
+ behavior: 'Display comprehensive capability details in organized sections',
1700
+ requirement: 'Show resource name, capabilities, providers, complexity, use case, and confidence prominently',
1701
+ format: 'Structured display with clear sections: Resource Info, Capabilities, Technical Details, and Analysis Results',
1702
+ sections: {
1703
+ resourceInfo: 'Resource name and description with use case',
1704
+ capabilities: 'List all capabilities, providers, and abstractions clearly',
1705
+ technicalDetails: 'Complexity level and provider information',
1706
+ analysisResults: 'Confidence score, analysis timestamp, and ID for reference'
1707
+ }
1708
+ }
1709
+ };
1710
+ }
1711
+ catch (error) {
1712
+ logger.error('Failed to get capability', error, {
1713
+ requestId,
1714
+ capabilityId: args.id
1715
+ });
1716
+ return {
1717
+ success: false,
1718
+ operation: 'get',
1719
+ dataType: 'capabilities',
1720
+ error: {
1721
+ message: 'Failed to retrieve capability',
1722
+ details: error instanceof Error ? error.message : String(error)
1723
+ }
1724
+ };
1725
+ }
1726
+ }
1727
+ /**
1728
+ * Handle capability progress query (check progress of running scan)
1729
+ */
1730
+ async function handleCapabilityProgress(args, logger, requestId) {
1731
+ try {
1732
+ logger.info('Capability progress query requested', {
1733
+ requestId,
1734
+ sessionId: args.sessionId
1735
+ });
1736
+ // Get session directory first
1737
+ const sessionDir = (0, session_utils_1.getAndValidateSessionDirectory)(args, false);
1738
+ let sessionId = args.sessionId;
1739
+ let sessionFilePath;
1740
+ // If no sessionId provided, auto-discover the latest session
1741
+ if (!sessionId) {
1742
+ logger.info('No sessionId provided, searching for latest session file', { requestId });
1743
+ try {
1744
+ // Check if capability-sessions subdirectory exists
1745
+ const sessionSubDir = path.join(sessionDir, 'capability-sessions');
1746
+ if (!fs.existsSync(sessionSubDir)) {
1747
+ logger.info('No capability-sessions directory found', { requestId, sessionDir });
1748
+ return {
1749
+ success: false,
1750
+ operation: 'progress',
1751
+ dataType: 'capabilities',
1752
+ error: {
1753
+ message: 'No capability scan sessions found',
1754
+ details: 'No capability-sessions directory found in the session directory',
1755
+ help: 'Start a new capability scan with the scan operation first',
1756
+ sessionDirectory: sessionDir
1757
+ }
1758
+ };
1759
+ }
1760
+ // Find all capability scan session files in the subdirectory
1761
+ const sessionFiles = fs.readdirSync(sessionSubDir)
1762
+ .filter(file => file.endsWith('.json'))
1763
+ .map(file => {
1764
+ const filePath = path.join(sessionSubDir, file);
1765
+ const stats = fs.statSync(filePath);
1766
+ return {
1767
+ filename: file,
1768
+ path: filePath,
1769
+ mtime: stats.mtime,
1770
+ sessionId: file.replace('.json', '') // Remove .json extension to get sessionId
1771
+ };
1772
+ })
1773
+ .sort((a, b) => b.mtime.getTime() - a.mtime.getTime()); // Sort by newest first
1774
+ if (sessionFiles.length === 0) {
1775
+ logger.info('No capability scan session files found', { requestId, sessionDir });
1776
+ return {
1777
+ success: false,
1778
+ operation: 'progress',
1779
+ dataType: 'capabilities',
1780
+ error: {
1781
+ message: 'No capability scan sessions found',
1782
+ details: 'No active or recent capability scans found in the session directory',
1783
+ help: 'Start a new capability scan with the scan operation first',
1784
+ sessionDirectory: sessionDir
1785
+ }
1786
+ };
1787
+ }
1788
+ // Use the latest session (first after sorting)
1789
+ const latestSession = sessionFiles[0];
1790
+ sessionId = latestSession.sessionId;
1791
+ sessionFilePath = latestSession.path;
1792
+ logger.info('Using latest session file', {
1793
+ requestId,
1794
+ sessionId,
1795
+ totalSessions: sessionFiles.length,
1796
+ sessionFile: latestSession.filename
1797
+ });
1798
+ }
1799
+ catch (error) {
1800
+ logger.error('Failed to discover session files', error, { requestId, sessionDir });
1801
+ return {
1802
+ success: false,
1803
+ operation: 'progress',
1804
+ dataType: 'capabilities',
1805
+ error: {
1806
+ message: 'Failed to discover session files',
1807
+ details: error instanceof Error ? error.message : String(error),
1808
+ sessionDirectory: sessionDir
1809
+ }
1810
+ };
1811
+ }
1812
+ }
1813
+ else {
1814
+ // Use provided sessionId - look in capability-sessions subdirectory
1815
+ const sessionSubDir = path.join(sessionDir, 'capability-sessions');
1816
+ sessionFilePath = path.join(sessionSubDir, `${sessionId}.json`);
1817
+ if (!fs.existsSync(sessionFilePath)) {
1818
+ logger.warn('Session file not found for provided sessionId', {
1819
+ requestId,
1820
+ sessionId,
1821
+ filePath: sessionFilePath
1822
+ });
1823
+ return {
1824
+ success: false,
1825
+ operation: 'progress',
1826
+ dataType: 'capabilities',
1827
+ error: {
1828
+ message: 'Session not found',
1829
+ details: `No capability scan found for session: ${sessionId}`,
1830
+ help: 'Use the scan operation to start a new capability scan, or omit sessionId to use the latest session'
1831
+ }
1832
+ };
1833
+ }
1834
+ }
1835
+ // Read and parse session file
1836
+ const sessionData = JSON.parse(fs.readFileSync(sessionFilePath, 'utf8'));
1837
+ // Extract progress information
1838
+ const progress = sessionData.progress;
1839
+ if (!progress) {
1840
+ return {
1841
+ success: true,
1842
+ operation: 'progress',
1843
+ dataType: 'capabilities',
1844
+ sessionId: sessionId,
1845
+ status: 'no-progress-data',
1846
+ message: 'Session exists but no progress tracking is active',
1847
+ currentStep: sessionData.currentStep,
1848
+ startedAt: sessionData.startedAt,
1849
+ lastActivity: sessionData.lastActivity
1850
+ };
1851
+ }
1852
+ // Build comprehensive progress response
1853
+ const response = {
1854
+ success: true,
1855
+ operation: 'progress',
1856
+ dataType: 'capabilities',
1857
+ sessionId: sessionId,
1858
+ progress: {
1859
+ status: progress.status,
1860
+ current: progress.current,
1861
+ total: progress.total,
1862
+ percentage: progress.percentage,
1863
+ currentResource: progress.currentResource,
1864
+ startedAt: progress.startedAt,
1865
+ lastUpdated: progress.lastUpdated
1866
+ },
1867
+ sessionInfo: {
1868
+ currentStep: sessionData.currentStep,
1869
+ processingMode: sessionData.processingMode,
1870
+ resourceCount: Array.isArray(sessionData.selectedResources)
1871
+ ? sessionData.selectedResources.length
1872
+ : (sessionData.selectedResources === 'all' ? 'all resources' : 'unknown'),
1873
+ startedAt: sessionData.startedAt,
1874
+ lastActivity: sessionData.lastActivity
1875
+ }
1876
+ };
1877
+ // Add completion information if scan is done
1878
+ if (progress.status === 'completed') {
1879
+ response.progress.completedAt = progress.completedAt;
1880
+ response.progress.totalProcessingTime = progress.totalProcessingTime;
1881
+ response.message = 'Capability scan completed successfully';
1882
+ }
1883
+ else {
1884
+ response.progress.estimatedTimeRemaining = progress.estimatedTimeRemaining;
1885
+ response.message = `Capability scan in progress: ${progress.current}/${progress.total} resources processed`;
1886
+ }
1887
+ // Add user-friendly display information with client formatting instructions
1888
+ response.display = {
1889
+ summary: progress.status === 'completed'
1890
+ ? `✅ Scan complete: processed ${progress.total} resources in ${progress.totalProcessingTime}`
1891
+ : `⏳ Processing: ${progress.current}/${progress.total} (${progress.percentage}%) - ${progress.estimatedTimeRemaining} remaining`,
1892
+ currentResource: progress.currentResource,
1893
+ timeline: {
1894
+ started: progress.startedAt,
1895
+ lastUpdate: progress.lastUpdated,
1896
+ ...(progress.completedAt && { completed: progress.completedAt })
1897
+ }
1898
+ };
1899
+ // Add client instructions for readable formatting
1900
+ response.clientInstructions = {
1901
+ behavior: 'Display capability scan progress in a clean, readable format',
1902
+ requirement: 'Show progress information in separate lines, not as a single condensed line',
1903
+ format: progress.status === 'completed'
1904
+ ? 'Completion format: Status, total processed, processing time on separate lines'
1905
+ : 'Progress format: Status line, current resource line, time estimates line, timestamps line',
1906
+ example: progress.status === 'completed'
1907
+ ? '✅ Capability Scan Complete\\n📊 Processed: X resources\\n⏰ Processing time: X minutes'
1908
+ : '⏳ Progress: X/Y resources (Z%)\\n📊 Current resource: ResourceName\\n⏰ Est. remaining: X minutes\\n🕒 Started: timestamp\\n🔄 Last updated: timestamp',
1909
+ prohibit: 'Do not display all progress information on a single line'
1910
+ };
1911
+ logger.info('Progress query completed successfully', {
1912
+ requestId,
1913
+ sessionId: args.sessionId,
1914
+ status: progress.status,
1915
+ percentage: progress.percentage
1916
+ });
1917
+ return response;
1918
+ }
1919
+ catch (error) {
1920
+ logger.error('Failed to query capability progress', error, {
1921
+ requestId,
1922
+ sessionId: args.sessionId
1923
+ });
1924
+ return {
1925
+ success: false,
1926
+ operation: 'progress',
1927
+ dataType: 'capabilities',
1928
+ error: {
1929
+ message: 'Failed to query scan progress',
1930
+ details: error instanceof Error ? error.message : String(error)
1931
+ }
1932
+ };
1933
+ }
1934
+ }
1935
+ /**
1936
+ * Handle capability deletion (delete single capability by ID)
1937
+ */
1938
+ async function handleCapabilityDelete(args, logger, requestId, capabilityService) {
1939
+ try {
1940
+ // Validate required parameters
1941
+ if (!args.id) {
1942
+ return {
1943
+ success: false,
1944
+ operation: 'delete',
1945
+ dataType: 'capabilities',
1946
+ error: {
1947
+ message: 'Missing required parameter: id',
1948
+ details: 'Specify id to delete capability data',
1949
+ example: { id: 'capability-id-example' }
1950
+ }
1951
+ };
1952
+ }
1953
+ // Check if capability exists before deletion
1954
+ const capability = await capabilityService.getCapability(args.id);
1955
+ if (!capability) {
1956
+ logger.warn('Capability not found for deletion', {
1957
+ requestId,
1958
+ capabilityId: args.id
1959
+ });
1960
+ return {
1961
+ success: false,
1962
+ operation: 'delete',
1963
+ dataType: 'capabilities',
1964
+ error: {
1965
+ message: `Capability not found for ID: ${args.id}`,
1966
+ details: 'Cannot delete capability that does not exist'
1967
+ }
1968
+ };
1969
+ }
1970
+ // Delete capability by ID
1971
+ await capabilityService.deleteCapabilityById(args.id);
1972
+ logger.info('Capability deleted successfully', {
1973
+ requestId,
1974
+ capabilityId: args.id,
1975
+ resourceName: capability.resourceName
1976
+ });
1977
+ return {
1978
+ success: true,
1979
+ operation: 'delete',
1980
+ dataType: 'capabilities',
1981
+ deletedCapability: {
1982
+ id: args.id,
1983
+ resourceName: capability.resourceName
1984
+ },
1985
+ message: `Capability deleted: ${capability.resourceName}`
1986
+ };
1987
+ }
1988
+ catch (error) {
1989
+ logger.error('Failed to delete capability', error, {
1990
+ requestId,
1991
+ capabilityId: args.id
1992
+ });
1993
+ return {
1994
+ success: false,
1995
+ operation: 'delete',
1996
+ dataType: 'capabilities',
1997
+ error: {
1998
+ message: 'Failed to delete capability',
1999
+ details: error instanceof Error ? error.message : String(error)
2000
+ }
2001
+ };
2002
+ }
2003
+ }
2004
+ /**
2005
+ * Handle capability bulk deletion (delete all capabilities)
2006
+ */
2007
+ async function handleCapabilityDeleteAll(args, logger, requestId, capabilityService) {
2008
+ try {
2009
+ // Get count first to provide feedback (but don't retrieve all data)
2010
+ const totalCount = await capabilityService.getCapabilitiesCount();
2011
+ if (totalCount === 0) {
2012
+ logger.info('No capabilities found to delete', { requestId });
2013
+ return {
2014
+ success: true,
2015
+ operation: 'deleteAll',
2016
+ dataType: 'capabilities',
2017
+ deletedCount: 0,
2018
+ totalCount: 0,
2019
+ message: 'No capabilities found to delete'
2020
+ };
2021
+ }
2022
+ logger.info('Starting efficient bulk capability deletion', {
2023
+ requestId,
2024
+ totalCapabilities: totalCount,
2025
+ method: 'collection recreation'
2026
+ });
2027
+ // Efficiently delete all capabilities by recreating collection
2028
+ await capabilityService.deleteAllCapabilities();
2029
+ logger.info('Bulk capability deletion completed successfully', {
2030
+ requestId,
2031
+ deleted: totalCount,
2032
+ method: 'collection recreation'
2033
+ });
2034
+ return {
2035
+ success: true,
2036
+ operation: 'deleteAll',
2037
+ dataType: 'capabilities',
2038
+ deletedCount: totalCount,
2039
+ totalCount,
2040
+ errorCount: 0,
2041
+ message: `Successfully deleted all ${totalCount} capabilities`,
2042
+ confirmation: 'All capability data has been permanently removed from the Vector DB',
2043
+ method: 'Efficient collection recreation (no individual record retrieval)'
2044
+ };
2045
+ }
2046
+ catch (error) {
2047
+ logger.error('Failed to delete all capabilities', error, {
2048
+ requestId
2049
+ });
2050
+ return {
2051
+ success: false,
2052
+ operation: 'deleteAll',
2053
+ dataType: 'capabilities',
2054
+ error: {
2055
+ message: 'Failed to delete all capabilities',
2056
+ details: error instanceof Error ? error.message : String(error)
2057
+ }
2058
+ };
2059
+ }
2060
+ }
2061
+ /**
2062
+ * Handle capability search operation
2063
+ */
2064
+ async function handleCapabilitySearch(args, logger, requestId, capabilityService) {
2065
+ try {
2066
+ // Validate required search query (stored in id field)
2067
+ if (!args.id || typeof args.id !== 'string' || args.id.trim() === '') {
2068
+ return {
2069
+ success: false,
2070
+ operation: 'search',
2071
+ dataType: 'capabilities',
2072
+ error: {
2073
+ message: 'Search query required',
2074
+ details: 'The id field must contain a search query (e.g., "postgresql database in azure")'
2075
+ }
2076
+ };
2077
+ }
2078
+ const searchQuery = args.id.trim();
2079
+ const limit = args.limit || 25;
2080
+ logger.info('Searching capabilities', {
2081
+ requestId,
2082
+ query: searchQuery,
2083
+ limit
2084
+ });
2085
+ // Perform the search using existing service
2086
+ const searchResults = await capabilityService.searchCapabilities(searchQuery, { limit });
2087
+ // Format results for user display
2088
+ const formattedResults = searchResults.map((result, index) => ({
2089
+ rank: index + 1,
2090
+ score: Math.round(result.score * 100) / 100, // Round to 2 decimal places
2091
+ id: capabilities_1.CapabilityInferenceEngine.generateCapabilityId(result.data.resourceName),
2092
+ resourceName: result.data.resourceName,
2093
+ capabilities: result.data.capabilities,
2094
+ providers: result.data.providers,
2095
+ complexity: result.data.complexity,
2096
+ description: result.data.description,
2097
+ useCase: result.data.useCase,
2098
+ confidence: result.data.confidence,
2099
+ analyzedAt: result.data.analyzedAt
2100
+ }));
2101
+ logger.info('Capability search completed', {
2102
+ requestId,
2103
+ query: searchQuery,
2104
+ resultsFound: searchResults.length,
2105
+ limit
2106
+ });
2107
+ return {
2108
+ success: true,
2109
+ operation: 'search',
2110
+ dataType: 'capabilities',
2111
+ data: {
2112
+ query: searchQuery,
2113
+ results: formattedResults,
2114
+ resultCount: searchResults.length,
2115
+ limit
2116
+ },
2117
+ message: `Found ${searchResults.length} capabilities matching "${searchQuery}"`,
2118
+ clientInstructions: {
2119
+ behavior: 'Display search results with relevance scores and capability details',
2120
+ sections: {
2121
+ searchSummary: 'Show query and result count prominently',
2122
+ resultsList: 'Display each result with rank, score, resource name, and capabilities',
2123
+ capabilityDetails: 'Include providers, complexity, and description for context',
2124
+ actionGuidance: 'Show IDs for get operations and suggest refinement if needed'
2125
+ },
2126
+ format: 'Ranked list with scores (higher scores = better matches)',
2127
+ emphasize: 'Resource names and main capabilities for easy scanning'
2128
+ }
2129
+ };
2130
+ }
2131
+ catch (error) {
2132
+ logger.error('Failed to search capabilities', error, {
2133
+ requestId,
2134
+ query: args.id
2135
+ });
2136
+ return {
2137
+ success: false,
2138
+ operation: 'search',
2139
+ dataType: 'capabilities',
2140
+ error: {
2141
+ message: 'Capability search failed',
2142
+ details: error instanceof Error ? error.message : String(error)
2143
+ }
2144
+ };
2145
+ }
2146
+ }
482
2147
  /**
483
2148
  * Main tool handler - routes to appropriate data type handler
484
2149
  */
@@ -515,15 +2180,12 @@ async function handleOrganizationalDataTool(args, _dotAI, logger, requestId) {
515
2180
  case 'capabilities':
516
2181
  result = await handleCapabilitiesOperation(args.operation, args, logger, requestId);
517
2182
  break;
518
- case 'dependencies':
519
- result = await handleDependenciesOperation(args.operation, args, logger, requestId);
520
- break;
521
2183
  default:
522
- throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.VALIDATION, error_handling_1.ErrorSeverity.HIGH, `Unsupported data type: ${args.dataType}. Currently supported: pattern, capabilities, dependencies`, {
2184
+ throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.VALIDATION, error_handling_1.ErrorSeverity.HIGH, `Unsupported data type: ${args.dataType}. Currently supported: pattern, capabilities`, {
523
2185
  operation: 'data_type_validation',
524
2186
  component: 'OrganizationalDataTool',
525
2187
  requestId,
526
- input: { dataType: args.dataType, supportedTypes: ['pattern', 'capabilities', 'dependencies'] }
2188
+ input: { dataType: args.dataType, supportedTypes: ['pattern', 'capabilities'] }
527
2189
  });
528
2190
  }
529
2191
  logger.info('Organizational-data tool request completed successfully', {