@eldrforge/kodrdriv 1.2.21 → 1.2.23

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 (67) hide show
  1. package/WORKFLOW-PRECHECK-IMPLEMENTATION.md +239 -0
  2. package/WORKFLOW-SKIP-SUMMARY.md +121 -0
  3. package/dist/arguments.js +2 -2
  4. package/dist/arguments.js.map +1 -1
  5. package/dist/commands/audio-commit.js +15 -6
  6. package/dist/commands/audio-commit.js.map +1 -1
  7. package/dist/commands/audio-review.js +31 -15
  8. package/dist/commands/audio-review.js.map +1 -1
  9. package/dist/commands/commit.js +30 -19
  10. package/dist/commands/commit.js.map +1 -1
  11. package/dist/commands/link.js +27 -27
  12. package/dist/commands/link.js.map +1 -1
  13. package/dist/commands/publish.js +74 -21
  14. package/dist/commands/publish.js.map +1 -1
  15. package/dist/commands/release.js +30 -17
  16. package/dist/commands/release.js.map +1 -1
  17. package/dist/commands/review.js +33 -26
  18. package/dist/commands/review.js.map +1 -1
  19. package/dist/commands/select-audio.js +4 -4
  20. package/dist/commands/select-audio.js.map +1 -1
  21. package/dist/commands/tree.js +122 -35
  22. package/dist/commands/tree.js.map +1 -1
  23. package/dist/commands/unlink.js +13 -13
  24. package/dist/commands/unlink.js.map +1 -1
  25. package/dist/commands/updates.js +21 -0
  26. package/dist/commands/updates.js.map +1 -1
  27. package/dist/commands/versions.js +5 -5
  28. package/dist/commands/versions.js.map +1 -1
  29. package/dist/constants.js +4 -4
  30. package/dist/constants.js.map +1 -1
  31. package/dist/content/files.js +4 -4
  32. package/dist/content/files.js.map +1 -1
  33. package/dist/logging.js +3 -3
  34. package/dist/logging.js.map +1 -1
  35. package/dist/util/aiAdapter.js +28 -0
  36. package/dist/util/aiAdapter.js.map +1 -0
  37. package/dist/util/general.js +5 -5
  38. package/dist/util/general.js.map +1 -1
  39. package/dist/util/interactive.js +6 -437
  40. package/dist/util/interactive.js.map +1 -1
  41. package/dist/util/loggerAdapter.js +24 -0
  42. package/dist/util/loggerAdapter.js.map +1 -0
  43. package/dist/util/performance.js +4 -4
  44. package/dist/util/performance.js.map +1 -1
  45. package/dist/util/safety.js +4 -4
  46. package/dist/util/safety.js.map +1 -1
  47. package/dist/util/storage.js +2 -2
  48. package/dist/util/storage.js.map +1 -1
  49. package/dist/util/storageAdapter.js +25 -0
  50. package/dist/util/storageAdapter.js.map +1 -0
  51. package/package.json +7 -6
  52. package/GITHUB-TOOLS-INTEGRATION.md +0 -323
  53. package/INTEGRATION-SUMMARY.md +0 -232
  54. package/TEST-STATUS.md +0 -168
  55. package/dist/prompt/commit.js +0 -76
  56. package/dist/prompt/commit.js.map +0 -1
  57. package/dist/prompt/instructions/commit.md +0 -133
  58. package/dist/prompt/instructions/release.md +0 -188
  59. package/dist/prompt/instructions/review.md +0 -169
  60. package/dist/prompt/personas/releaser.md +0 -24
  61. package/dist/prompt/personas/you.md +0 -55
  62. package/dist/prompt/release.js +0 -100
  63. package/dist/prompt/release.js.map +0 -1
  64. package/dist/prompt/review.js +0 -64
  65. package/dist/prompt/review.js.map +0 -1
  66. package/dist/util/openai.js +0 -365
  67. package/dist/util/openai.js.map +0 -1
@@ -2,26 +2,27 @@
2
2
  import { Formatter } from '@riotprompt/riotprompt';
3
3
  import { ValidationError, FileOperationError, CommandError } from '../error/CommandErrors.js';
4
4
  import { getLogger } from '../logging.js';
5
- import { getModelForCommand, createCompletion, getOpenAIMaxOutputTokensForCommand, getOpenAIReasoningForCommand } from '../util/openai.js';
6
- import { createPrompt } from '../prompt/review.js';
5
+ import { getUserChoice, createReviewPrompt, createCompletion } from '@eldrforge/ai-service';
6
+ import { toAIConfig } from '../util/aiAdapter.js';
7
+ import { createStorageAdapter } from '../util/storageAdapter.js';
8
+ import { createLoggerAdapter } from '../util/loggerAdapter.js';
7
9
  import { create as create$1 } from '../content/log.js';
8
10
  import { getReviewExcludedPatterns, getRecentDiffsForReview } from '../content/diff.js';
9
11
  import { handleIssueCreation, getReleaseNotesContent, getIssuesContent } from '@eldrforge/github-tools';
10
12
  import { DEFAULT_EXCLUDED_PATTERNS, DEFAULT_OUTPUT_DIRECTORY } from '../constants.js';
11
13
  import { getTimestampedReviewNotesFilename, getOutputPath, getTimestampedReviewFilename, getTimestampedResponseFilename, getTimestampedRequestFilename } from '../util/general.js';
12
14
  import { create } from '../util/storage.js';
13
- import { getUserChoice } from '../util/interactive.js';
14
- import path__default from 'path';
15
- import os__default from 'os';
15
+ import path from 'path';
16
+ import os from 'os';
16
17
  import { spawn } from 'child_process';
17
- import fs__default from 'fs/promises';
18
+ import fs from 'fs/promises';
18
19
 
19
20
  // Utility function to read a review note from a file
20
21
  const readReviewNoteFromFile = async (filePath)=>{
21
22
  const logger = getLogger();
22
23
  try {
23
24
  logger.debug(`Reading review note from file: ${filePath}`);
24
- const content = await fs__default.readFile(filePath, 'utf8');
25
+ const content = await fs.readFile(filePath, 'utf8');
25
26
  if (!content.trim()) {
26
27
  throw new ValidationError(`Review file is empty: ${filePath}`);
27
28
  }
@@ -42,11 +43,11 @@ const getReviewFilesInDirectory = async (directoryPath)=>{
42
43
  const logger = getLogger();
43
44
  try {
44
45
  logger.debug(`Scanning directory for review files: ${directoryPath}`);
45
- const entries = await fs__default.readdir(directoryPath, {
46
+ const entries = await fs.readdir(directoryPath, {
46
47
  withFileTypes: true
47
48
  });
48
49
  // Filter for regular files (not directories) and get full paths
49
- const files = entries.filter((entry)=>entry.isFile()).map((entry)=>path__default.join(directoryPath, entry.name)).sort(); // Sort alphabetically
50
+ const files = entries.filter((entry)=>entry.isFile()).map((entry)=>path.join(directoryPath, entry.name)).sort(); // Sort alphabetically
50
51
  logger.debug(`Found ${files.length} files in directory: ${directoryPath}`);
51
52
  return files;
52
53
  } catch (error) {
@@ -120,20 +121,20 @@ const selectFilesForProcessing = async (reviewFiles, senditMode)=>{
120
121
  // Safe temp file handling with proper permissions and validation
121
122
  const createSecureTempFile = async ()=>{
122
123
  const logger = getLogger();
123
- const tmpDir = os__default.tmpdir();
124
+ const tmpDir = os.tmpdir();
124
125
  // Ensure temp directory exists and is writable
125
126
  try {
126
127
  // Use constant value directly to avoid import restrictions
127
128
  const W_OK = 2; // fs.constants.W_OK value
128
- await fs__default.access(tmpDir, W_OK);
129
+ await fs.access(tmpDir, W_OK);
129
130
  } catch (error) {
130
131
  logger.error(`Temp directory not writable: ${tmpDir}`);
131
132
  throw new FileOperationError(`Temp directory not writable: ${error.message}`, tmpDir, error);
132
133
  }
133
- const tmpFilePath = path__default.join(tmpDir, `kodrdriv_review_${Date.now()}_${Math.random().toString(36).substring(7)}.md`);
134
+ const tmpFilePath = path.join(tmpDir, `kodrdriv_review_${Date.now()}_${Math.random().toString(36).substring(7)}.md`);
134
135
  // Create file with restrictive permissions (owner read/write only)
135
136
  try {
136
- const fd = await fs__default.open(tmpFilePath, 'w', 0o600);
137
+ const fd = await fs.open(tmpFilePath, 'w', 0o600);
137
138
  await fd.close();
138
139
  logger.debug(`Created secure temp file: ${tmpFilePath}`);
139
140
  return tmpFilePath;
@@ -146,7 +147,7 @@ const createSecureTempFile = async ()=>{
146
147
  const cleanupTempFile = async (filePath)=>{
147
148
  const logger = getLogger();
148
149
  try {
149
- await fs__default.unlink(filePath);
150
+ await fs.unlink(filePath);
150
151
  logger.debug(`Cleaned up temp file: ${filePath}`);
151
152
  } catch (error) {
152
153
  // Only ignore ENOENT (file not found) errors, log others
@@ -271,14 +272,14 @@ const safeWriteFile = async (filePath, content, encoding = 'utf-8')=>{
271
272
  const logger = getLogger();
272
273
  try {
273
274
  // Check if parent directory exists and is writable
274
- const parentDir = path__default.dirname(filePath);
275
+ const parentDir = path.dirname(filePath);
275
276
  const W_OK = 2; // fs.constants.W_OK value
276
- await fs__default.access(parentDir, W_OK);
277
+ await fs.access(parentDir, W_OK);
277
278
  // Check available disk space (basic check by writing a small test)
278
279
  const testFile = `${filePath}.test`;
279
280
  try {
280
- await fs__default.writeFile(testFile, 'test', encoding);
281
- await fs__default.unlink(testFile);
281
+ await fs.writeFile(testFile, 'test', encoding);
282
+ await fs.unlink(testFile);
282
283
  } catch (error) {
283
284
  if (error.code === 'ENOSPC') {
284
285
  throw new Error(`Insufficient disk space to write file: ${filePath}`);
@@ -286,7 +287,7 @@ const safeWriteFile = async (filePath, content, encoding = 'utf-8')=>{
286
287
  throw error;
287
288
  }
288
289
  // Write the actual file
289
- await fs__default.writeFile(filePath, content, encoding);
290
+ await fs.writeFile(filePath, content, encoding);
290
291
  logger.debug(`Successfully wrote file: ${filePath} (${content.length} characters)`);
291
292
  } catch (error) {
292
293
  logger.error(`Failed to write file ${filePath}: ${error.message}`);
@@ -295,7 +296,7 @@ const safeWriteFile = async (filePath, content, encoding = 'utf-8')=>{
295
296
  };
296
297
  // Helper function to process a single review note
297
298
  const processSingleReview = async (reviewNote, runConfig, outputDirectory)=>{
298
- var _runConfig_review, _runConfig_review1, _runConfig_review2, _runConfig_review3, _runConfig_review_context, _runConfig_review4, _runConfig_review5, _analysisResult_issues;
299
+ var _runConfig_review, _runConfig_review1, _runConfig_review2, _runConfig_review3, _runConfig_review_context, _runConfig_review4, _runConfig_review5, _aiConfig_commands_review, _aiConfig_commands, _analysisResult_issues;
299
300
  const logger = getLogger();
300
301
  // Gather additional context based on configuration with improved error handling
301
302
  let logContext = '';
@@ -398,6 +399,10 @@ const processSingleReview = async (reviewNote, runConfig, outputDirectory)=>{
398
399
  overridePaths: runConfig.discoveredConfigDirs || [],
399
400
  overrides: runConfig.overrides || false
400
401
  };
402
+ // Create adapters for ai-service
403
+ const aiConfig = toAIConfig(runConfig);
404
+ const aiStorageAdapter = createStorageAdapter();
405
+ const aiLogger = createLoggerAdapter(runConfig.dryRun || false);
401
406
  const promptContent = {
402
407
  notes: reviewNote
403
408
  };
@@ -408,23 +413,25 @@ const processSingleReview = async (reviewNote, runConfig, outputDirectory)=>{
408
413
  releaseNotesContext,
409
414
  issuesContext
410
415
  };
411
- const prompt = await createPrompt(promptConfig, promptContent, promptContext);
412
- const modelToUse = getModelForCommand(runConfig, 'review');
416
+ const prompt = await createReviewPrompt(promptConfig, promptContent, promptContext);
417
+ const modelToUse = ((_aiConfig_commands = aiConfig.commands) === null || _aiConfig_commands === void 0 ? void 0 : (_aiConfig_commands_review = _aiConfig_commands.review) === null || _aiConfig_commands_review === void 0 ? void 0 : _aiConfig_commands_review.model) || aiConfig.model || 'gpt-4o-mini';
413
418
  const request = Formatter.create({
414
419
  logger
415
420
  }).formatPrompt(modelToUse, prompt);
416
421
  let analysisResult;
417
422
  try {
423
+ var _aiConfig_commands_review1, _aiConfig_commands1;
418
424
  const rawResult = await createCompletion(request.messages, {
419
425
  model: modelToUse,
420
- openaiReasoning: getOpenAIReasoningForCommand(runConfig, 'review'),
421
- openaiMaxOutputTokens: getOpenAIMaxOutputTokensForCommand(runConfig, 'review'),
426
+ openaiReasoning: ((_aiConfig_commands1 = aiConfig.commands) === null || _aiConfig_commands1 === void 0 ? void 0 : (_aiConfig_commands_review1 = _aiConfig_commands1.review) === null || _aiConfig_commands_review1 === void 0 ? void 0 : _aiConfig_commands_review1.reasoning) || aiConfig.reasoning,
422
427
  responseFormat: {
423
428
  type: 'json_object'
424
429
  },
425
430
  debug: runConfig.debug,
426
431
  debugRequestFile: getOutputPath(outputDirectory, getTimestampedRequestFilename('review-analysis')),
427
- debugResponseFile: getOutputPath(outputDirectory, getTimestampedResponseFilename('review-analysis'))
432
+ debugResponseFile: getOutputPath(outputDirectory, getTimestampedResponseFilename('review-analysis')),
433
+ storage: aiStorageAdapter,
434
+ logger: aiLogger
428
435
  });
429
436
  // Validate the API response before using it
430
437
  analysisResult = validateReviewResult(rawResult);
@@ -566,7 +573,7 @@ const executeInternal = async (runConfig)=>{
566
573
  const editorTimeout = (_runConfig_review25 = runConfig.review) === null || _runConfig_review25 === void 0 ? void 0 : _runConfig_review25.editorTimeout; // No default timeout - let user take their time
567
574
  await openEditorWithTimeout(editor, tmpFilePath, editorTimeout);
568
575
  // Read the file back in, stripping comment lines and whitespace.
569
- const fileContent = (await fs__default.readFile(tmpFilePath, 'utf8')).split('\n').filter((line)=>!line.trim().startsWith('#')).join('\n').trim();
576
+ const fileContent = (await fs.readFile(tmpFilePath, 'utf8')).split('\n').filter((line)=>!line.trim().startsWith('#')).join('\n').trim();
570
577
  if (!fileContent) {
571
578
  throw new ValidationError('Review note is empty – aborting. Provide a note as an argument, via STDIN, or through the editor.');
572
579
  }