@vfarcic/dot-ai 1.16.1 → 1.16.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -85,3 +85,4 @@ DevOps AI Toolkit is built on:
85
85
  ---
86
86
 
87
87
  **DevOps AI Toolkit** - Making cloud native operations accessible through AI-powered intelligence.
88
+
@@ -1 +1 @@
1
- {"version":3,"file":"ai-provider-factory.d.ts","sourceRoot":"","sources":["../../src/core/ai-provider-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EACL,UAAU,EACV,gBAAgB,EACjB,MAAM,yBAAyB,CAAC;AA8BjC;;;;;;;;;;;;;;GAcG;AACH,qBAAa,iBAAiB;IAC5B;;;;;;OAMG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU;IA2BnD;;;;;;;;;;OAUG;IACH,MAAM,CAAC,aAAa,IAAI,UAAU;IAqGlC;;;;;OAKG;IACH,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAWrD;;;;OAIG;IACH,MAAM,CAAC,qBAAqB,IAAI,MAAM,EAAE;IAMxC;;;;;OAKG;IACH,MAAM,CAAC,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CAGxD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CAE7C"}
1
+ {"version":3,"file":"ai-provider-factory.d.ts","sourceRoot":"","sources":["../../src/core/ai-provider-factory.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAiCvE;;;;;;;;;;;;;;GAcG;AACH,qBAAa,iBAAiB;IAC5B;;;;;;OAMG;IACH,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,gBAAgB,GAAG,UAAU;IA+BnD;;;;;;;;;;OAUG;IACH,MAAM,CAAC,aAAa,IAAI,UAAU;IAsGlC;;;;;OAKG;IACH,MAAM,CAAC,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAWrD;;;;OAIG;IACH,MAAM,CAAC,qBAAqB,IAAI,MAAM,EAAE;IAMxC;;;;;OAKG;IACH,MAAM,CAAC,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;CAGxD;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CAE7C"}
@@ -30,6 +30,7 @@ const PROVIDER_ENV_KEYS = {
30
30
  kimi: 'MOONSHOT_API_KEY', // PRD #353: Moonshot AI Kimi K2.5
31
31
  alibaba: 'ALIBABA_API_KEY', // PRD #382: Alibaba Qwen 3.5 Plus
32
32
  xai: 'XAI_API_KEY',
33
+ custom: 'CUSTOM_LLM_API_KEY', // PRD #194 / Issue #474: Explicit opt-in to custom OpenAI-compatible endpoint
33
34
  };
34
35
  const IMPLEMENTED_PROVIDERS = Object.keys(model_config_1.CURRENT_MODELS);
35
36
  /**
@@ -1 +1 @@
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
+ {"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;AAgBD;;;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;IACE,OAAO,EAAE,IAAI,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;CACxB,GACD;IACE,OAAO,EAAE,IAAI,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,EAAE,MAAM,CAAC;CACf,GACD;IAAE,OAAO,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AA4ItC;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,MAAM,GAAG,YAAY,CAqB1E;AAID;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,QAAQ,GAAE,MAAuB,GAAG,IAAI,CAuBxE"}
@@ -89,9 +89,7 @@ function validatePathWithinClones(inputPath) {
89
89
  function repoUrlToDirectoryName(repoUrl) {
90
90
  try {
91
91
  const url = new URL(repoUrl);
92
- const repoPath = url.pathname
93
- .replace(/^\//, '')
94
- .replace(/\.git$/, '');
92
+ const repoPath = url.pathname.replace(/^\//, '').replace(/\.git$/, '');
95
93
  return (0, solution_utils_js_1.sanitizeIntentForLabel)(repoPath);
96
94
  }
97
95
  catch {
@@ -196,7 +194,7 @@ function handleFsList(args) {
196
194
  return `Error: path is not a directory: ${inputPath}`;
197
195
  }
198
196
  const entries = fs.readdirSync(resolved, { withFileTypes: true });
199
- return entries.map((entry) => ({
197
+ return entries.map(entry => ({
200
198
  name: entry.name,
201
199
  type: entry.isDirectory() ? 'directory' : 'file',
202
200
  }));
@@ -257,7 +255,10 @@ async function handleGitCreatePr(args) {
257
255
  return { success: false, error: 'repoPath is required' };
258
256
  }
259
257
  if (!files || !Array.isArray(files) || files.length === 0) {
260
- return { success: false, error: 'files array is required and must not be empty' };
258
+ return {
259
+ success: false,
260
+ error: 'files array is required and must not be empty',
261
+ };
261
262
  }
262
263
  if (!title) {
263
264
  return { success: false, error: 'title is required' };
@@ -324,6 +325,12 @@ async function handleGitCreatePr(args) {
324
325
  body,
325
326
  head: branchName,
326
327
  base: baseBranch,
328
+ // Test-only switch: integration tests set this so PRs they create
329
+ // don't trigger CodeRabbit (which has drafts: false in .coderabbit.yaml).
330
+ // Production never sets this env var.
331
+ ...(process.env.DOT_AI_GIT_CREATE_DRAFT_PRS === 'true' && {
332
+ draft: true,
333
+ }),
327
334
  }),
328
335
  signal: AbortSignal.timeout(30000),
329
336
  });
@@ -356,7 +363,7 @@ async function handleGitCreatePr(args) {
356
363
  */
357
364
  function createInternalToolExecutor(sessionId) {
358
365
  const handlers = {
359
- git_clone: (args) => handleGitClone(args, sessionId),
366
+ git_clone: args => handleGitClone(args, sessionId),
360
367
  fs_list: handleFsList,
361
368
  fs_read: handleFsRead,
362
369
  git_create_pr: handleGitCreatePr,
@@ -380,7 +387,9 @@ function createInternalToolExecutor(sessionId) {
380
387
  */
381
388
  function cleanupOldClones(maxAgeMs = DEFAULT_TTL_MS) {
382
389
  const clonesDir = getClonesDir();
383
- fs.promises.access(clonesDir).then(async () => {
390
+ fs.promises
391
+ .access(clonesDir)
392
+ .then(async () => {
384
393
  const now = Date.now();
385
394
  const entries = await fs.promises.readdir(clonesDir);
386
395
  for (const entry of entries) {
@@ -395,7 +404,8 @@ function cleanupOldClones(maxAgeMs = DEFAULT_TTL_MS) {
395
404
  // Ignore errors during cleanup (e.g., concurrent deletion)
396
405
  }
397
406
  }
398
- }).catch(() => {
407
+ })
408
+ .catch(() => {
399
409
  // Directory doesn't exist yet, nothing to clean
400
410
  });
401
411
  }
@@ -4,6 +4,22 @@
4
4
  import { z } from 'zod';
5
5
  import { DotAI } from '../core/index';
6
6
  import { Logger } from '../core/error-handling';
7
+ interface QuestionValidation {
8
+ required?: boolean;
9
+ message?: string;
10
+ min?: number;
11
+ max?: number;
12
+ pattern?: string;
13
+ options?: string[];
14
+ }
15
+ interface Question {
16
+ id?: string;
17
+ question: string;
18
+ type?: 'text' | 'number' | 'boolean' | 'select' | 'multiSelect';
19
+ validation?: QuestionValidation;
20
+ answer?: unknown;
21
+ options?: string[];
22
+ }
7
23
  export declare const ANSWERQUESTION_TOOL_NAME = "answerQuestion";
8
24
  export declare const ANSWERQUESTION_TOOL_DESCRIPTION = "Process user answers and return remaining questions or completion status. For open stage, use \"open\" as the answer key.";
9
25
  export declare const ANSWERQUESTION_TOOL_INPUT_SCHEMA: {
@@ -17,6 +33,10 @@ export declare const ANSWERQUESTION_TOOL_INPUT_SCHEMA: {
17
33
  answers: z.ZodRecord<z.ZodString, z.ZodAny>;
18
34
  interaction_id: z.ZodOptional<z.ZodString>;
19
35
  };
36
+ /**
37
+ * Validate answer against question schema
38
+ */
39
+ export declare function validateAnswer(answer: unknown, question: Question): string | null;
20
40
  /**
21
41
  * Direct MCP tool handler for answerQuestion functionality
22
42
  */
@@ -31,4 +51,5 @@ export declare function handleAnswerQuestionTool(args: {
31
51
  text: string;
32
52
  }[];
33
53
  }>;
54
+ export {};
34
55
  //# sourceMappingURL=answer-question.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"answer-question.d.ts","sourceRoot":"","sources":["../../src/tools/answer-question.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AA0EhD,eAAO,MAAM,wBAAwB,mBAAmB,CAAC;AACzD,eAAO,MAAM,+BAA+B,8HAC+E,CAAC;AAG5H,eAAO,MAAM,gCAAgC;;;;;;;;;;CAmB5C,CAAC;AAinBF;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE;IACJ,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;IAClD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,EACD,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,CAAC,CAgZxD"}
1
+ {"version":3,"file":"answer-question.d.ts","sourceRoot":"","sources":["../../src/tools/answer-question.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAOxB,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AACtC,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAUhD,UAAU,kBAAkB;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,UAAU,QAAQ;IAChB,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,GAAG,QAAQ,GAAG,SAAS,GAAG,QAAQ,GAAG,aAAa,CAAC;IAChE,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACpB;AAgDD,eAAO,MAAM,wBAAwB,mBAAmB,CAAC;AACzD,eAAO,MAAM,+BAA+B,8HAC+E,CAAC;AAG5H,eAAO,MAAM,gCAAgC;;;;;;;;;;CAmB5C,CAAC;AAIF;;GAEG;AACH,wBAAgB,cAAc,CAC5B,MAAM,EAAE,OAAO,EACf,QAAQ,EAAE,QAAQ,GACjB,MAAM,GAAG,IAAI,CA4Ef;AAyiBD;;GAEG;AACH,wBAAsB,wBAAwB,CAC5C,IAAI,EAAE;IACJ,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,UAAU,GAAG,OAAO,GAAG,UAAU,GAAG,MAAM,CAAC;IAClD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,EACD,KAAK,EAAE,KAAK,EACZ,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC;IAAE,OAAO,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,EAAE,CAAA;CAAE,CAAC,CAgZxD"}
@@ -4,6 +4,7 @@
4
4
  */
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.ANSWERQUESTION_TOOL_INPUT_SCHEMA = exports.ANSWERQUESTION_TOOL_DESCRIPTION = exports.ANSWERQUESTION_TOOL_NAME = void 0;
7
+ exports.validateAnswer = validateAnswer;
7
8
  exports.handleAnswerQuestionTool = handleAnswerQuestionTool;
8
9
  const zod_1 = require("zod");
9
10
  const error_handling_1 = require("../core/error-handling");
@@ -36,9 +37,19 @@ exports.ANSWERQUESTION_TOOL_INPUT_SCHEMA = {
36
37
  * Validate answer against question schema
37
38
  */
38
39
  function validateAnswer(answer, question) {
39
- // Check required validation
40
+ // Check required validation.
41
+ // Issue #474: For 'select' questions, empty string is a valid answer when it
42
+ // is explicitly listed in `options` (e.g., options=["", "soft", "hard"] where
43
+ // "" represents the "none/disabled" choice). Skip the empty-string rejection
44
+ // in that case so the question's own option list is the source of truth.
45
+ const emptyAnswer = answer === undefined || answer === null || answer === '';
46
+ const isExplicitEmptySelectOption = question.type === 'select' &&
47
+ answer === '' &&
48
+ Array.isArray(question.options) &&
49
+ question.options.includes('');
40
50
  if (question.validation?.required &&
41
- (answer === undefined || answer === null || answer === '')) {
51
+ emptyAnswer &&
52
+ !isExplicitEmptySelectOption) {
42
53
  return question.validation.message || `${question.question} is required`;
43
54
  }
44
55
  // Skip validation if answer is empty and not required
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vfarcic/dot-ai",
3
- "version": "1.16.1",
3
+ "version": "1.16.3",
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",
@@ -103,7 +103,7 @@
103
103
  "ts-node": "^10.9.0",
104
104
  "typescript": "^5.0.0",
105
105
  "typescript-eslint": "^8.54.0",
106
- "uuid": "^13.0.0",
106
+ "uuid": "^14.0.0",
107
107
  "vitest": "^3.2.4"
108
108
  },
109
109
  "dependencies": {