@vfarcic/dot-ai 1.12.0 → 1.14.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.
@@ -51,6 +51,7 @@ const fs = __importStar(require("fs"));
51
51
  const path = __importStar(require("path"));
52
52
  const request_context_1 = require("../interfaces/request-context");
53
53
  const rbac_1 = require("../core/rbac");
54
+ const session_events_1 = require("../core/session-events");
54
55
  const internal_tools_1 = require("../core/internal-tools");
55
56
  // PRD #143 Milestone 1: Hybrid approach - AI can use kubectl_api_resources tool OR continue with JSON dataRequests
56
57
  // Tool metadata for direct MCP registration
@@ -256,6 +257,13 @@ async function conductInvestigation(session, sessionManager, aiProvider, logger,
256
257
  finalAnalysis: output,
257
258
  status: 'analysis_complete',
258
259
  });
260
+ (0, session_events_1.getSessionEventBus)().publish(session_events_1.SESSION_EVENTS.SESSION_UPDATED, {
261
+ sessionId: session.sessionId,
262
+ toolName: 'remediate',
263
+ status: 'analysis_complete',
264
+ issue: session.data.issue,
265
+ timestamp: new Date().toISOString(),
266
+ });
259
267
  logger.info('Investigation and analysis completed', {
260
268
  requestId,
261
269
  sessionId: session.sessionId,
@@ -271,6 +279,13 @@ async function conductInvestigation(session, sessionManager, aiProvider, logger,
271
279
  });
272
280
  // Mark session as failed
273
281
  sessionManager.updateSession(session.sessionId, { status: 'failed' });
282
+ (0, session_events_1.getSessionEventBus)().publish(session_events_1.SESSION_EVENTS.SESSION_UPDATED, {
283
+ sessionId: session.sessionId,
284
+ toolName: 'remediate',
285
+ status: 'failed',
286
+ issue: session.data.issue,
287
+ timestamp: new Date().toISOString(),
288
+ });
274
289
  throw error_handling_1.ErrorHandler.createError(error_handling_1.ErrorCategory.AI_SERVICE, error_handling_1.ErrorSeverity.HIGH, `Investigation failed: ${error instanceof Error ? error.message : 'Unknown error'}`, {
275
290
  operation: 'investigation_loop',
276
291
  component: 'RemediateTool',
@@ -401,7 +416,31 @@ async function executeUserChoice(sessionManager, sessionId, choice, logger, requ
401
416
  return await executeRemediationCommands(session, sessionManager, logger, requestId, currentInteractionId);
402
417
  case 2: {
403
418
  // Execute via agent
404
- // Use validation intent directly from final analysis
419
+ const actions = session.data.finalAnalysis.remediation.actions;
420
+ const gitSourceActions = actions.filter(a => a.gitSource && !a.command);
421
+ const kubectlActions = actions.filter(a => a.command && !a.gitSource);
422
+ if (gitSourceActions.length > 0 && kubectlActions.length === 0) {
423
+ return {
424
+ content: [
425
+ {
426
+ type: 'text',
427
+ text: JSON.stringify({
428
+ status: 'success',
429
+ sessionId: sessionId,
430
+ message: 'GitOps remediation detected - use automatic execution (choice 1) for PR creation',
431
+ remediation: session.data.finalAnalysis.remediation,
432
+ instructions: {
433
+ nextSteps: [
434
+ 'This remediation requires GitOps PR creation which is handled automatically.',
435
+ 'Please use choice 1 (Execute automatically) to create the PR.',
436
+ 'Alternatively, manually apply the file changes from gitSource.files to your repository.',
437
+ ],
438
+ },
439
+ }, null, 2),
440
+ },
441
+ ],
442
+ };
443
+ }
405
444
  const validationIntent = session.data.finalAnalysis.validationIntent ||
406
445
  'Check the status of the affected resources to verify the issue has been resolved';
407
446
  return {
@@ -453,6 +492,7 @@ async function executeRemediationCommands(session, sessionManager, logger, reque
453
492
  const finalAnalysis = session.data.finalAnalysis;
454
493
  let overallSuccess = true;
455
494
  let executedCommandCount = 0;
495
+ let pullRequestInfo;
456
496
  logger.info('Starting remediation command execution', {
457
497
  requestId,
458
498
  sessionId: session.sessionId,
@@ -463,21 +503,76 @@ async function executeRemediationCommands(session, sessionManager, logger, reque
463
503
  const action = finalAnalysis.remediation.actions[i];
464
504
  const actionId = `action_${i + 1}`;
465
505
  try {
466
- // PRD #407: Skip gitSource actions — these are Git-based remediation
467
- // instructions for GitOps-managed resources, not executable commands
506
+ // PRD #408: Handle gitSource actions — create PR instead of kubectl
468
507
  if (action.gitSource && !action.command) {
469
- logger.info('Skipping gitSource remediation action (not executable)', {
508
+ logger.info('Processing gitSource remediation action', {
470
509
  requestId,
471
510
  sessionId: session.sessionId,
472
511
  actionId,
473
512
  repoURL: action.gitSource.repoURL,
513
+ repoPath: action.gitSource.repoPath,
474
514
  });
475
- results.push({
476
- action: `${actionId}: ${action.description} (skipped: Git-based)`,
477
- success: false,
478
- output: 'Git-based remediation — apply changes in the source repository',
479
- timestamp: new Date(),
480
- });
515
+ if (!action.gitSource.repoPath) {
516
+ results.push({
517
+ action: `${actionId}: ${action.description} (failed: missing repoPath)`,
518
+ success: false,
519
+ output: 'Git-based remediation requires repoPath from investigation phase',
520
+ timestamp: new Date(),
521
+ });
522
+ overallSuccess = false;
523
+ continue;
524
+ }
525
+ const prInput = {
526
+ repoPath: action.gitSource.repoPath,
527
+ files: action.gitSource.files.map((f) => ({
528
+ path: f.path,
529
+ content: f.content,
530
+ })),
531
+ title: `fix: ${action.description}`,
532
+ body: `## Remediation\n\n${action.rationale}\n\n**Risk Level:** ${action.risk}`,
533
+ branchName: `remediate/${session.sessionId.slice(0, 12)}-${Date.now()}`,
534
+ baseBranch: action.gitSource.branch || 'main',
535
+ };
536
+ const prExecutor = (0, internal_tools_1.createInternalToolExecutor)(session.sessionId);
537
+ const prResult = (await prExecutor('git_create_pr', prInput));
538
+ if (prResult.success && 'prUrl' in prResult) {
539
+ const filesList = prResult.filesChanged && prResult.filesChanged.length > 0
540
+ ? prResult.filesChanged.join(', ')
541
+ : 'none';
542
+ results.push({
543
+ action: `${actionId}: ${action.description} (PR created)`,
544
+ success: true,
545
+ output: `PR #${prResult.prNumber}: ${prResult.prUrl}\nBranch: ${prResult.branch}\nFiles changed: ${filesList}`,
546
+ timestamp: new Date(),
547
+ });
548
+ pullRequestInfo = {
549
+ url: prResult.prUrl,
550
+ number: prResult.prNumber,
551
+ branch: prResult.branch,
552
+ baseBranch: prResult.baseBranch,
553
+ filesChanged: prResult.filesChanged,
554
+ };
555
+ }
556
+ else if (prResult.success && 'error' in prResult) {
557
+ const filesList = prResult.filesChanged && prResult.filesChanged.length > 0
558
+ ? prResult.filesChanged.join(', ')
559
+ : 'none';
560
+ results.push({
561
+ action: `${actionId}: ${action.description} (branch pushed, manual PR needed)`,
562
+ success: true,
563
+ output: `Branch: ${prResult.branch}\nFiles changed: ${filesList}\nNote: ${prResult.error}`,
564
+ timestamp: new Date(),
565
+ });
566
+ }
567
+ else {
568
+ overallSuccess = false;
569
+ results.push({
570
+ action: `${actionId}: ${action.description} (failed)`,
571
+ success: false,
572
+ output: prResult.error,
573
+ timestamp: new Date(),
574
+ });
575
+ }
481
576
  continue;
482
577
  }
483
578
  logger.info('Executing remediation action', {
@@ -620,6 +715,7 @@ IMPORTANT: You MUST respond with the final JSON analysis format as specified in
620
715
  success: false,
621
716
  summary: 'Validation found remaining issues after remediation',
622
717
  },
718
+ pullRequest: pullRequestInfo,
623
719
  };
624
720
  return {
625
721
  content: [
@@ -657,6 +753,7 @@ IMPORTANT: You MUST respond with the final JSON analysis format as specified in
657
753
  success: true,
658
754
  summary: 'Validation confirmed issue resolution',
659
755
  },
756
+ pullRequest: pullRequestInfo,
660
757
  };
661
758
  const content = [
662
759
  {
@@ -684,6 +781,73 @@ IMPORTANT: You MUST respond with the final JSON analysis format as specified in
684
781
  status: overallSuccess ? 'executed_successfully' : 'executed_with_errors',
685
782
  executionResults: results,
686
783
  });
784
+ (0, session_events_1.getSessionEventBus)().publish(session_events_1.SESSION_EVENTS.SESSION_UPDATED, {
785
+ sessionId: session.sessionId,
786
+ toolName: 'remediate',
787
+ status: overallSuccess ? 'executed_successfully' : 'executed_with_errors',
788
+ issue: session.data.issue,
789
+ timestamp: new Date().toISOString(),
790
+ });
791
+ const hasOnlyGitOps = executedCommandCount === 0 && pullRequestInfo !== undefined;
792
+ const prInfo = pullRequestInfo;
793
+ let nextSteps;
794
+ if (hasOnlyGitOps && prInfo) {
795
+ nextSteps = [
796
+ 'Changes have been pushed to a Git branch for GitOps reconciliation:',
797
+ ` PR: ${prInfo.url}`,
798
+ ` Branch: ${prInfo.branch} → ${prInfo.baseBranch}`,
799
+ ` Files changed: ${prInfo.filesChanged.join(', ')}`,
800
+ '',
801
+ 'Next steps:',
802
+ ' 1. Review and merge the PR in your Git repository',
803
+ ' 2. Wait for Argo CD/Flux to sync the changes',
804
+ ' 3. Verify the issue is resolved after reconciliation',
805
+ '',
806
+ `You can verify the fix by running: remediate("Verify that ${finalAnalysis.analysis.rootCause.toLowerCase()} has been resolved")`,
807
+ ];
808
+ }
809
+ else if (overallSuccess) {
810
+ if (validationResult) {
811
+ nextSteps = [
812
+ 'The following kubectl commands were executed to remediate the issue:',
813
+ ...finalAnalysis.remediation.actions
814
+ .filter(a => a.command)
815
+ .map((action, index) => {
816
+ const resultIndex = finalAnalysis.remediation.actions.indexOf(action);
817
+ return ` ${index + 1}. ${action.command} ${results[resultIndex]?.success ? '✓' : '✗'}`;
818
+ }),
819
+ 'Automatic validation has been completed - see validation results above',
820
+ 'Monitor your cluster to ensure the issue remains resolved',
821
+ ];
822
+ }
823
+ else {
824
+ nextSteps = [
825
+ 'The following kubectl commands were executed to remediate the issue:',
826
+ ...finalAnalysis.remediation.actions
827
+ .filter(a => a.command)
828
+ .map((action, index) => {
829
+ const resultIndex = finalAnalysis.remediation.actions.indexOf(action);
830
+ return ` ${index + 1}. ${action.command} ${results[resultIndex]?.success ? '✓' : '✗'}`;
831
+ }),
832
+ `You can verify the fix by running: remediate("Verify that ${finalAnalysis.analysis.rootCause.toLowerCase()} has been resolved")`,
833
+ 'Monitor your cluster to ensure the issue is fully resolved',
834
+ ];
835
+ }
836
+ }
837
+ else {
838
+ nextSteps = [
839
+ 'The following kubectl commands were attempted:',
840
+ ...finalAnalysis.remediation.actions
841
+ .filter(a => a.command)
842
+ .map((action, index) => {
843
+ const resultIndex = finalAnalysis.remediation.actions.indexOf(action);
844
+ return ` ${index + 1}. ${action.command} ${results[resultIndex]?.success ? '✓' : '✗'}`;
845
+ }),
846
+ 'Some remediation commands failed - check the results above',
847
+ 'Review the error messages and address any underlying issues',
848
+ 'You may need to run additional commands or investigate further',
849
+ ];
850
+ }
687
851
  const response = {
688
852
  status: overallSuccess ? 'success' : 'failed',
689
853
  sessionId: session.sessionId,
@@ -691,37 +855,20 @@ IMPORTANT: You MUST respond with the final JSON analysis format as specified in
691
855
  results: results,
692
856
  executedCommands: results.map(r => r.action),
693
857
  message: overallSuccess
694
- ? `Successfully executed ${results.length} remediation actions`
858
+ ? hasOnlyGitOps
859
+ ? `Successfully created PR for ${results.length} GitOps remediation action(s)`
860
+ : `Successfully executed ${results.length} remediation actions`
695
861
  : `Executed ${results.length} actions with ${results.filter(r => !r.success).length} failures`,
696
862
  validation: validationResult,
697
863
  instructions: {
698
- showExecutedCommands: true,
699
- showActualKubectlCommands: true,
700
- nextSteps: overallSuccess
701
- ? validationResult
702
- ? [
703
- 'The following kubectl commands were executed to remediate the issue:',
704
- ...finalAnalysis.remediation.actions.map((action, index) => ` ${index + 1}. ${action.command} ${results[index]?.success ? '✓' : '✗'}`),
705
- 'Automatic validation has been completed - see validation results above',
706
- 'Monitor your cluster to ensure the issue remains resolved',
707
- ]
708
- : [
709
- 'The following kubectl commands were executed to remediate the issue:',
710
- ...finalAnalysis.remediation.actions.map((action, index) => ` ${index + 1}. ${action.command} ${results[index]?.success ? '✓' : '✗'}`),
711
- `You can verify the fix by running: remediate("Verify that ${finalAnalysis.analysis.rootCause.toLowerCase()} has been resolved")`,
712
- 'Monitor your cluster to ensure the issue is fully resolved',
713
- ]
714
- : [
715
- 'The following kubectl commands were attempted:',
716
- ...finalAnalysis.remediation.actions.map((action, index) => ` ${index + 1}. ${action.command} ${results[index]?.success ? '✓' : '✗'}`),
717
- 'Some remediation commands failed - check the results above',
718
- 'Review the error messages and address any underlying issues',
719
- 'You may need to run additional commands or investigate further',
720
- ],
864
+ showExecutedCommands: !hasOnlyGitOps,
865
+ showActualKubectlCommands: !hasOnlyGitOps,
866
+ nextSteps,
721
867
  },
722
868
  investigation: finalAnalysis.investigation,
723
869
  analysis: finalAnalysis.analysis,
724
870
  remediation: finalAnalysis.remediation,
871
+ pullRequest: pullRequestInfo,
725
872
  };
726
873
  logger.info('Remediation execution completed', {
727
874
  requestId,
@@ -796,6 +943,13 @@ async function handleRemediateTool(args) {
796
943
  interaction_id: validatedInput.interaction_id,
797
944
  status: 'investigating',
798
945
  });
946
+ (0, session_events_1.getSessionEventBus)().publish(session_events_1.SESSION_EVENTS.SESSION_CREATED, {
947
+ sessionId: session.sessionId,
948
+ toolName: 'remediate',
949
+ status: 'investigating',
950
+ issue: validatedInput.issue,
951
+ timestamp: session.createdAt,
952
+ });
799
953
  logger.info('Investigation session created', {
800
954
  requestId,
801
955
  sessionId: session.sessionId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vfarcic/dot-ai",
3
- "version": "1.12.0",
3
+ "version": "1.14.0",
4
4
  "description": "AI-powered development productivity platform that enhances software development workflows through intelligent automation and AI-driven assistance",
5
5
  "mcpName": "io.github.vfarcic/dot-ai",
6
6
  "main": "dist/index.js",
@@ -107,7 +107,7 @@
107
107
  "vitest": "^3.2.4"
108
108
  },
109
109
  "dependencies": {
110
- "@ai-sdk/alibaba": "^1.0.10",
110
+ "@ai-sdk/alibaba": "^1.0.13",
111
111
  "@ai-sdk/amazon-bedrock": "^4.0.77",
112
112
  "@ai-sdk/anthropic": "^3.0.58",
113
113
  "@ai-sdk/google": "^3.0.43",
@@ -65,6 +65,7 @@ Once investigation is complete, respond with ONLY this JSON format:
65
65
  "gitSource": {
66
66
  "repoURL": "source repository URL — only when resource is GitOps-managed",
67
67
  "branch": "branch name",
68
+ "repoPath": "relative path to cloned repo (as returned by git_clone) — required for GitOps remediation",
68
69
  "files": [
69
70
  {
70
71
  "path": "path relative to repo root",
@@ -104,8 +105,9 @@ Once investigation is complete, respond with ONLY this JSON format:
104
105
  After identifying the problematic resource, check whether it is managed by a GitOps controller (e.g., Argo CD, Flux).
105
106
 
106
107
  **When GitOps management is detected**:
107
- - Clone the source repo, navigate and read the manifests to find the file(s) that need changing
108
- - Include `gitSource` in your remediation actions with the repo URL, branch, and full corrected file contents for each file that needs modification
108
+ - Clone the source repo and capture the local path for use as `repoPath` in the remediation actions
109
+ - Navigate and read the manifests to find the file(s) that need changing
110
+ - Include `gitSource` in your remediation actions with `repoPath`, `repoURL`, `branch`, and full corrected file contents for each file that needs modification
109
111
 
110
112
  **When GitOps management is NOT detected**:
111
113
  - Proceed with standard kubectl-based remediation
@@ -190,6 +192,7 @@ After identifying the problematic resource, check whether it is managed by a Git
190
192
  "gitSource": {
191
193
  "repoURL": "https://github.com/org/infra-repo.git",
192
194
  "branch": "main",
195
+ "repoPath": "session-abc123/org-infra-repo",
193
196
  "files": [
194
197
  {
195
198
  "path": "apps/production/deployment.yaml",