@vfarcic/dot-ai 1.12.0 → 1.13.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.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Internal Agentic-Loop Tools (PRD #407)
2
+ * Internal Agentic-Loop Tools (PRD #407, PRD #408)
3
3
  *
4
4
  * Tools that run locally in the MCP server, available to the AI
5
5
  * during investigation loops alongside plugin tools. NOT exposed
@@ -9,6 +9,7 @@
9
9
  * - git_clone: Clone a Git repo
10
10
  * - fs_list: List files at a path
11
11
  * - fs_read: Read a file at a path
12
+ * - git_create_pr: Create a PR with file changes (PRD #408)
12
13
  *
13
14
  * All filesystem operations are scoped to ./tmp/gitops-clones/
14
15
  * to prevent path traversal attacks.
@@ -20,7 +21,39 @@ import type { AITool, ToolExecutor } from './ai-provider.interface.js';
20
21
  * Exported for testing.
21
22
  */
22
23
  export declare function validatePathWithinClones(inputPath: string): string;
24
+ /**
25
+ * Returns internal tools available to the AI during investigation.
26
+ * Note: git_create_pr is executor-only and not exposed to investigation.
27
+ */
23
28
  export declare function getInternalTools(): AITool[];
29
+ export interface GitCreatePrInput {
30
+ repoPath: string;
31
+ files: Array<{
32
+ path: string;
33
+ content: string;
34
+ }>;
35
+ title: string;
36
+ body?: string;
37
+ branchName: string;
38
+ baseBranch?: string;
39
+ }
40
+ export type GitCreatePrResult = {
41
+ success: true;
42
+ prUrl: string;
43
+ prNumber: number;
44
+ branch: string;
45
+ baseBranch: string;
46
+ filesChanged: string[];
47
+ } | {
48
+ success: true;
49
+ branch: string;
50
+ baseBranch: string;
51
+ filesChanged: string[];
52
+ error: string;
53
+ } | {
54
+ success: false;
55
+ error: string;
56
+ };
24
57
  /**
25
58
  * Create a ToolExecutor that handles internal agentic-loop tools.
26
59
  * Designed to be passed as the fallbackExecutor to pluginManager.createToolExecutor().
@@ -1 +1 @@
1
- {"version":3,"file":"internal-tools.d.ts","sourceRoot":"","sources":["../../src/core/internal-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAcvE;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAUlE;AAkBD,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAgD3C;AAmHD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,CAoB1E;AAID;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,GAAE,MAAuB,GAAG,IAAI,CAoBxE"}
1
+ {"version":3,"file":"internal-tools.d.ts","sourceRoot":"","sources":["../../src/core/internal-tools.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAIH,OAAO,KAAK,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAsBvE;;;;GAIG;AACH,wBAAgB,wBAAwB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,CAUlE;AAkBD;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,MAAM,EAAE,CAkD3C;AAiHD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChD,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,iBAAiB,GACzB;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAA;CAAE,GAC9G;IAAE,OAAO,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,MAAM,EAAE,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAC5F;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAgItC;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,CAqB1E;AAID;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,GAAE,MAAuB,GAAG,IAAI,CAoBxE"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  /**
3
- * Internal Agentic-Loop Tools (PRD #407)
3
+ * Internal Agentic-Loop Tools (PRD #407, PRD #408)
4
4
  *
5
5
  * Tools that run locally in the MCP server, available to the AI
6
6
  * during investigation loops alongside plugin tools. NOT exposed
@@ -10,6 +10,7 @@
10
10
  * - git_clone: Clone a Git repo
11
11
  * - fs_list: List files at a path
12
12
  * - fs_read: Read a file at a path
13
+ * - git_create_pr: Create a PR with file changes (PRD #408)
13
14
  *
14
15
  * All filesystem operations are scoped to ./tmp/gitops-clones/
15
16
  * to prevent path traversal attacks.
@@ -47,6 +48,9 @@ var __importStar = (this && this.__importStar) || (function () {
47
48
  return result;
48
49
  };
49
50
  })();
51
+ var __importDefault = (this && this.__importDefault) || function (mod) {
52
+ return (mod && mod.__esModule) ? mod : { "default": mod };
53
+ };
50
54
  Object.defineProperty(exports, "__esModule", { value: true });
51
55
  exports.validatePathWithinClones = validatePathWithinClones;
52
56
  exports.getInternalTools = getInternalTools;
@@ -56,6 +60,7 @@ const fs = __importStar(require("fs"));
56
60
  const path = __importStar(require("path"));
57
61
  const git_utils_js_1 = require("./git-utils.js");
58
62
  const solution_utils_js_1 = require("./solution-utils.js");
63
+ const simple_git_1 = __importDefault(require("simple-git"));
59
64
  const CLONES_SUBDIR = 'gitops-clones';
60
65
  const MAX_FILE_SIZE = 100 * 1024; // 100KB
61
66
  const DEFAULT_TTL_MS = 60 * 60 * 1000; // 1 hour
@@ -94,6 +99,10 @@ function repoUrlToDirectoryName(repoUrl) {
94
99
  }
95
100
  }
96
101
  // ─── Tool definitions ───
102
+ /**
103
+ * Returns internal tools available to the AI during investigation.
104
+ * Note: git_create_pr is executor-only and not exposed to investigation.
105
+ */
97
106
  function getInternalTools() {
98
107
  return [
99
108
  {
@@ -138,6 +147,8 @@ function getInternalTools() {
138
147
  required: ['path'],
139
148
  },
140
149
  },
150
+ // git_create_pr is executor-only - not exposed during investigation
151
+ // It's only available via createInternalToolExecutor() during execution
141
152
  ];
142
153
  }
143
154
  // ─── Handlers ───
@@ -235,6 +246,109 @@ function handleFsRead(args) {
235
246
  }
236
247
  return fs.readFileSync(resolved, 'utf-8');
237
248
  }
249
+ async function handleGitCreatePr(args) {
250
+ const repoPath = args.repoPath;
251
+ const files = args.files;
252
+ const title = args.title;
253
+ const body = args.body || '';
254
+ const branchName = args.branchName;
255
+ const baseBranch = args.baseBranch || 'main';
256
+ if (!repoPath) {
257
+ return { success: false, error: 'repoPath is required' };
258
+ }
259
+ if (!files || !Array.isArray(files) || files.length === 0) {
260
+ return { success: false, error: 'files array is required and must not be empty' };
261
+ }
262
+ if (!title) {
263
+ return { success: false, error: 'title is required' };
264
+ }
265
+ if (!branchName) {
266
+ return { success: false, error: 'branchName is required' };
267
+ }
268
+ let resolvedRepoPath;
269
+ try {
270
+ resolvedRepoPath = validatePathWithinClones(repoPath);
271
+ }
272
+ catch (err) {
273
+ return {
274
+ success: false,
275
+ error: `Invalid repo path: ${err instanceof Error ? err.message : String(err)}`,
276
+ };
277
+ }
278
+ if (!fs.existsSync(resolvedRepoPath)) {
279
+ return {
280
+ success: false,
281
+ error: `Repository not found at path: ${repoPath}. It may have been cleaned up.`,
282
+ };
283
+ }
284
+ const stat = fs.statSync(resolvedRepoPath);
285
+ if (!stat.isDirectory()) {
286
+ return { success: false, error: `Path is not a directory: ${repoPath}` };
287
+ }
288
+ try {
289
+ const git = (0, simple_git_1.default)(resolvedRepoPath);
290
+ await git.checkout(baseBranch);
291
+ const pushResult = await (0, git_utils_js_1.pushRepo)(resolvedRepoPath, files, title, {
292
+ branch: branchName,
293
+ });
294
+ const authConfig = (0, git_utils_js_1.getGitAuthConfigFromEnv)();
295
+ const token = await (0, git_utils_js_1.getAuthToken)(authConfig);
296
+ const remotes = await git.getRemotes(true);
297
+ const origin = remotes.find(r => r.name === 'origin');
298
+ if (!origin?.refs?.fetch) {
299
+ return { success: false, error: 'Could not find origin remote URL' };
300
+ }
301
+ const remoteUrl = (0, git_utils_js_1.scrubCredentials)(origin.refs.fetch);
302
+ const repoMatch = remoteUrl.match(/github\.com[/:]([^/]+\/[^/]+?)(?:\.git)?$/);
303
+ if (!repoMatch) {
304
+ return {
305
+ success: true,
306
+ branch: branchName,
307
+ baseBranch,
308
+ filesChanged: pushResult.filesAdded,
309
+ error: 'Automatic PR creation is only supported for GitHub repositories. Changes were pushed to the branch — create a PR/MR manually.',
310
+ };
311
+ }
312
+ const ownerRepo = repoMatch[1];
313
+ const [owner, repo] = ownerRepo.split('/');
314
+ const prResponse = await fetch(`https://api.github.com/repos/${owner}/${repo}/pulls`, {
315
+ method: 'POST',
316
+ headers: {
317
+ Authorization: `Bearer ${token}`,
318
+ 'Content-Type': 'application/json',
319
+ Accept: 'application/vnd.github+json',
320
+ 'User-Agent': 'dot-ai-remediate',
321
+ },
322
+ body: JSON.stringify({
323
+ title,
324
+ body,
325
+ head: branchName,
326
+ base: baseBranch,
327
+ }),
328
+ signal: AbortSignal.timeout(30000),
329
+ });
330
+ if (!prResponse.ok) {
331
+ const errorBody = await prResponse.text();
332
+ return {
333
+ success: false,
334
+ error: `GitHub API error (${prResponse.status}): ${errorBody}`,
335
+ };
336
+ }
337
+ const prData = (await prResponse.json());
338
+ return {
339
+ success: true,
340
+ prUrl: prData.html_url,
341
+ prNumber: prData.number,
342
+ branch: branchName,
343
+ baseBranch,
344
+ filesChanged: pushResult.filesAdded,
345
+ };
346
+ }
347
+ catch (err) {
348
+ const message = err instanceof Error ? err.message : String(err);
349
+ return { success: false, error: (0, git_utils_js_1.scrubCredentials)(message) };
350
+ }
351
+ }
238
352
  // ─── Combined executor ───
239
353
  /**
240
354
  * Create a ToolExecutor that handles internal agentic-loop tools.
@@ -245,6 +359,7 @@ function createInternalToolExecutor(sessionId) {
245
359
  git_clone: (args) => handleGitClone(args, sessionId),
246
360
  fs_list: handleFsList,
247
361
  fs_read: handleFsRead,
362
+ git_create_pr: handleGitCreatePr,
248
363
  };
249
364
  return async (toolName, input) => {
250
365
  const handler = handlers[toolName];
@@ -54,6 +54,7 @@ export interface RemediationAction {
54
54
  rationale: string;
55
55
  gitSource?: {
56
56
  repoURL: string;
57
+ repoPath?: string;
57
58
  branch: string;
58
59
  files: Array<{
59
60
  path: string;
@@ -102,6 +103,13 @@ export interface RemediateOutput {
102
103
  results?: ExecutionResult[];
103
104
  fallbackReason?: string;
104
105
  mode?: 'manual' | 'automatic';
106
+ pullRequest?: {
107
+ url: string;
108
+ number: number;
109
+ branch: string;
110
+ baseBranch: string;
111
+ filesChanged: string[];
112
+ };
105
113
  }
106
114
  /**
107
115
  * Remediate tool response type
@@ -1 +1 @@
1
- {"version":3,"file":"remediate.d.ts","sourceRoot":"","sources":["../../src/tools/remediate.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"remediate.d.ts","sourceRoot":"","sources":["../../src/tools/remediate.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAaxB,OAAO,EAEL,qBAAqB,EACtB,MAAM,uBAAuB,CAAC;AA6B/B,eAAO,MAAM,mBAAmB,cAAc,CAAC;AAC/C,eAAO,MAAM,0BAA0B,yfACgd,CAAC;AAGxf,eAAO,MAAM,2BAA2B;;;;;;;;;;;;;;;;CAsDvC,CAAC;AAGF,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IAC9B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,YAAY,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IACzC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAID,MAAM,WAAW,oBAAqB,SAAQ,qBAAqB;IACjE,QAAQ,EAAE,WAAW,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,QAAQ,GAAG,WAAW,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,eAAe,CAAC;IAChC,MAAM,EACF,eAAe,GACf,mBAAmB,GACnB,QAAQ,GACR,uBAAuB,GACvB,sBAAsB,GACtB,WAAW,CAAC;IAChB,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;CACtC;AAGD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,oBAAoB,CAAC;CAC5B,CAAC;AAEF,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;IAChC,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,CAAC,EAAE;QACV,OAAO,EAAE,MAAM,CAAC;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC;QACf,KAAK,EAAE,KAAK,CAAC;YACX,IAAI,EAAE,MAAM,CAAC;YACb,OAAO,EAAE,MAAM,CAAC;YAChB,WAAW,EAAE,MAAM,CAAC;SACrB,CAAC,CAAC;KACJ,CAAC;CACH;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,IAAI,CAAC;CACjB;AAED,MAAM,WAAW,eAAe;IAC9B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,SAAS,GAAG,QAAQ,GAAG,wBAAwB,GAAG,oBAAoB,CAAC;IAC/E,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE;QACb,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,EAAE,CAAC;KACxB,CAAC;IACF,QAAQ,EAAE;QACR,SAAS,EAAE,MAAM,CAAC;QAClB,UAAU,EAAE,MAAM,CAAC;QACnB,OAAO,EAAE,MAAM,EAAE,CAAC;KACnB,CAAC;IACF,WAAW,EAAE;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,iBAAiB,EAAE,CAAC;QAC7B,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;KACjC,CAAC;IAEF,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,gBAAgB,CAAC,EAAE,eAAe,EAAE,CAAC;IACrC,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,eAAe,EAAE,CAAC;IAC5B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,CAAC,EAAE,QAAQ,GAAG,WAAW,CAAC;IAC9B,WAAW,CAAC,EAAE;QACZ,GAAG,EAAE,MAAM,CAAC;QACZ,MAAM,EAAE,MAAM,CAAC;QACf,MAAM,EAAE,MAAM,CAAC;QACf,UAAU,EAAE,MAAM,CAAC;QACnB,YAAY,EAAE,MAAM,EAAE,CAAC;KACxB,CAAC;CACH;AAID;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AA6OD;;GAEG;AACH,UAAU,uBAAuB;IAC/B,WAAW,EAAE,QAAQ,GAAG,UAAU,GAAG,cAAc,CAAC;IACpD,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,WAAW,EAAE;QACX,OAAO,EAAE,MAAM,CAAC;QAChB,OAAO,EAAE,iBAAiB,EAAE,CAAC;QAC7B,IAAI,EAAE,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;KACjC,CAAC;IACF,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC3B;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,UAAU,EAAE,MAAM,GACjB,uBAAuB,CAyHzB;AA0lBD;;;;;GAKG;AACH,wBAAsB,mBAAmB,CACvC,IAAI,EAAE,cAAc,GACnB,OAAO,CAAC,qBAAqB,CAAC,CA8QhC"}
@@ -401,7 +401,31 @@ async function executeUserChoice(sessionManager, sessionId, choice, logger, requ
401
401
  return await executeRemediationCommands(session, sessionManager, logger, requestId, currentInteractionId);
402
402
  case 2: {
403
403
  // Execute via agent
404
- // Use validation intent directly from final analysis
404
+ const actions = session.data.finalAnalysis.remediation.actions;
405
+ const gitSourceActions = actions.filter(a => a.gitSource && !a.command);
406
+ const kubectlActions = actions.filter(a => a.command && !a.gitSource);
407
+ if (gitSourceActions.length > 0 && kubectlActions.length === 0) {
408
+ return {
409
+ content: [
410
+ {
411
+ type: 'text',
412
+ text: JSON.stringify({
413
+ status: 'success',
414
+ sessionId: sessionId,
415
+ message: 'GitOps remediation detected - use automatic execution (choice 1) for PR creation',
416
+ remediation: session.data.finalAnalysis.remediation,
417
+ instructions: {
418
+ nextSteps: [
419
+ 'This remediation requires GitOps PR creation which is handled automatically.',
420
+ 'Please use choice 1 (Execute automatically) to create the PR.',
421
+ 'Alternatively, manually apply the file changes from gitSource.files to your repository.',
422
+ ],
423
+ },
424
+ }, null, 2),
425
+ },
426
+ ],
427
+ };
428
+ }
405
429
  const validationIntent = session.data.finalAnalysis.validationIntent ||
406
430
  'Check the status of the affected resources to verify the issue has been resolved';
407
431
  return {
@@ -453,6 +477,7 @@ async function executeRemediationCommands(session, sessionManager, logger, reque
453
477
  const finalAnalysis = session.data.finalAnalysis;
454
478
  let overallSuccess = true;
455
479
  let executedCommandCount = 0;
480
+ let pullRequestInfo;
456
481
  logger.info('Starting remediation command execution', {
457
482
  requestId,
458
483
  sessionId: session.sessionId,
@@ -463,21 +488,76 @@ async function executeRemediationCommands(session, sessionManager, logger, reque
463
488
  const action = finalAnalysis.remediation.actions[i];
464
489
  const actionId = `action_${i + 1}`;
465
490
  try {
466
- // PRD #407: Skip gitSource actions — these are Git-based remediation
467
- // instructions for GitOps-managed resources, not executable commands
491
+ // PRD #408: Handle gitSource actions — create PR instead of kubectl
468
492
  if (action.gitSource && !action.command) {
469
- logger.info('Skipping gitSource remediation action (not executable)', {
493
+ logger.info('Processing gitSource remediation action', {
470
494
  requestId,
471
495
  sessionId: session.sessionId,
472
496
  actionId,
473
497
  repoURL: action.gitSource.repoURL,
498
+ repoPath: action.gitSource.repoPath,
474
499
  });
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
- });
500
+ if (!action.gitSource.repoPath) {
501
+ results.push({
502
+ action: `${actionId}: ${action.description} (failed: missing repoPath)`,
503
+ success: false,
504
+ output: 'Git-based remediation requires repoPath from investigation phase',
505
+ timestamp: new Date(),
506
+ });
507
+ overallSuccess = false;
508
+ continue;
509
+ }
510
+ const prInput = {
511
+ repoPath: action.gitSource.repoPath,
512
+ files: action.gitSource.files.map((f) => ({
513
+ path: f.path,
514
+ content: f.content,
515
+ })),
516
+ title: `fix: ${action.description}`,
517
+ body: `## Remediation\n\n${action.rationale}\n\n**Risk Level:** ${action.risk}`,
518
+ branchName: `remediate/${session.sessionId.slice(0, 12)}-${Date.now()}`,
519
+ baseBranch: action.gitSource.branch || 'main',
520
+ };
521
+ const prExecutor = (0, internal_tools_1.createInternalToolExecutor)(session.sessionId);
522
+ const prResult = (await prExecutor('git_create_pr', prInput));
523
+ if (prResult.success && 'prUrl' in prResult) {
524
+ const filesList = prResult.filesChanged && prResult.filesChanged.length > 0
525
+ ? prResult.filesChanged.join(', ')
526
+ : 'none';
527
+ results.push({
528
+ action: `${actionId}: ${action.description} (PR created)`,
529
+ success: true,
530
+ output: `PR #${prResult.prNumber}: ${prResult.prUrl}\nBranch: ${prResult.branch}\nFiles changed: ${filesList}`,
531
+ timestamp: new Date(),
532
+ });
533
+ pullRequestInfo = {
534
+ url: prResult.prUrl,
535
+ number: prResult.prNumber,
536
+ branch: prResult.branch,
537
+ baseBranch: prResult.baseBranch,
538
+ filesChanged: prResult.filesChanged,
539
+ };
540
+ }
541
+ else if (prResult.success && 'error' in prResult) {
542
+ const filesList = prResult.filesChanged && prResult.filesChanged.length > 0
543
+ ? prResult.filesChanged.join(', ')
544
+ : 'none';
545
+ results.push({
546
+ action: `${actionId}: ${action.description} (branch pushed, manual PR needed)`,
547
+ success: true,
548
+ output: `Branch: ${prResult.branch}\nFiles changed: ${filesList}\nNote: ${prResult.error}`,
549
+ timestamp: new Date(),
550
+ });
551
+ }
552
+ else {
553
+ overallSuccess = false;
554
+ results.push({
555
+ action: `${actionId}: ${action.description} (failed)`,
556
+ success: false,
557
+ output: prResult.error,
558
+ timestamp: new Date(),
559
+ });
560
+ }
481
561
  continue;
482
562
  }
483
563
  logger.info('Executing remediation action', {
@@ -620,6 +700,7 @@ IMPORTANT: You MUST respond with the final JSON analysis format as specified in
620
700
  success: false,
621
701
  summary: 'Validation found remaining issues after remediation',
622
702
  },
703
+ pullRequest: pullRequestInfo,
623
704
  };
624
705
  return {
625
706
  content: [
@@ -657,6 +738,7 @@ IMPORTANT: You MUST respond with the final JSON analysis format as specified in
657
738
  success: true,
658
739
  summary: 'Validation confirmed issue resolution',
659
740
  },
741
+ pullRequest: pullRequestInfo,
660
742
  };
661
743
  const content = [
662
744
  {
@@ -684,6 +766,66 @@ IMPORTANT: You MUST respond with the final JSON analysis format as specified in
684
766
  status: overallSuccess ? 'executed_successfully' : 'executed_with_errors',
685
767
  executionResults: results,
686
768
  });
769
+ const hasOnlyGitOps = executedCommandCount === 0 && pullRequestInfo !== undefined;
770
+ const prInfo = pullRequestInfo;
771
+ let nextSteps;
772
+ if (hasOnlyGitOps && prInfo) {
773
+ nextSteps = [
774
+ 'Changes have been pushed to a Git branch for GitOps reconciliation:',
775
+ ` PR: ${prInfo.url}`,
776
+ ` Branch: ${prInfo.branch} → ${prInfo.baseBranch}`,
777
+ ` Files changed: ${prInfo.filesChanged.join(', ')}`,
778
+ '',
779
+ 'Next steps:',
780
+ ' 1. Review and merge the PR in your Git repository',
781
+ ' 2. Wait for Argo CD/Flux to sync the changes',
782
+ ' 3. Verify the issue is resolved after reconciliation',
783
+ '',
784
+ `You can verify the fix by running: remediate("Verify that ${finalAnalysis.analysis.rootCause.toLowerCase()} has been resolved")`,
785
+ ];
786
+ }
787
+ else if (overallSuccess) {
788
+ if (validationResult) {
789
+ nextSteps = [
790
+ 'The following kubectl commands were executed to remediate the issue:',
791
+ ...finalAnalysis.remediation.actions
792
+ .filter(a => a.command)
793
+ .map((action, index) => {
794
+ const resultIndex = finalAnalysis.remediation.actions.indexOf(action);
795
+ return ` ${index + 1}. ${action.command} ${results[resultIndex]?.success ? '✓' : '✗'}`;
796
+ }),
797
+ 'Automatic validation has been completed - see validation results above',
798
+ 'Monitor your cluster to ensure the issue remains resolved',
799
+ ];
800
+ }
801
+ else {
802
+ nextSteps = [
803
+ 'The following kubectl commands were executed to remediate the issue:',
804
+ ...finalAnalysis.remediation.actions
805
+ .filter(a => a.command)
806
+ .map((action, index) => {
807
+ const resultIndex = finalAnalysis.remediation.actions.indexOf(action);
808
+ return ` ${index + 1}. ${action.command} ${results[resultIndex]?.success ? '✓' : '✗'}`;
809
+ }),
810
+ `You can verify the fix by running: remediate("Verify that ${finalAnalysis.analysis.rootCause.toLowerCase()} has been resolved")`,
811
+ 'Monitor your cluster to ensure the issue is fully resolved',
812
+ ];
813
+ }
814
+ }
815
+ else {
816
+ nextSteps = [
817
+ 'The following kubectl commands were attempted:',
818
+ ...finalAnalysis.remediation.actions
819
+ .filter(a => a.command)
820
+ .map((action, index) => {
821
+ const resultIndex = finalAnalysis.remediation.actions.indexOf(action);
822
+ return ` ${index + 1}. ${action.command} ${results[resultIndex]?.success ? '✓' : '✗'}`;
823
+ }),
824
+ 'Some remediation commands failed - check the results above',
825
+ 'Review the error messages and address any underlying issues',
826
+ 'You may need to run additional commands or investigate further',
827
+ ];
828
+ }
687
829
  const response = {
688
830
  status: overallSuccess ? 'success' : 'failed',
689
831
  sessionId: session.sessionId,
@@ -691,37 +833,20 @@ IMPORTANT: You MUST respond with the final JSON analysis format as specified in
691
833
  results: results,
692
834
  executedCommands: results.map(r => r.action),
693
835
  message: overallSuccess
694
- ? `Successfully executed ${results.length} remediation actions`
836
+ ? hasOnlyGitOps
837
+ ? `Successfully created PR for ${results.length} GitOps remediation action(s)`
838
+ : `Successfully executed ${results.length} remediation actions`
695
839
  : `Executed ${results.length} actions with ${results.filter(r => !r.success).length} failures`,
696
840
  validation: validationResult,
697
841
  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
- ],
842
+ showExecutedCommands: !hasOnlyGitOps,
843
+ showActualKubectlCommands: !hasOnlyGitOps,
844
+ nextSteps,
721
845
  },
722
846
  investigation: finalAnalysis.investigation,
723
847
  analysis: finalAnalysis.analysis,
724
848
  remediation: finalAnalysis.remediation,
849
+ pullRequest: pullRequestInfo,
725
850
  };
726
851
  logger.info('Remediation execution completed', {
727
852
  requestId,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vfarcic/dot-ai",
3
- "version": "1.12.0",
3
+ "version": "1.13.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",