@eldrforge/kodrdriv 1.2.21 → 1.2.22
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/WORKFLOW-PRECHECK-IMPLEMENTATION.md +239 -0
- package/WORKFLOW-SKIP-SUMMARY.md +121 -0
- package/dist/arguments.js +2 -2
- package/dist/arguments.js.map +1 -1
- package/dist/commands/audio-commit.js +15 -6
- package/dist/commands/audio-commit.js.map +1 -1
- package/dist/commands/audio-review.js +31 -15
- package/dist/commands/audio-review.js.map +1 -1
- package/dist/commands/commit.js +30 -19
- package/dist/commands/commit.js.map +1 -1
- package/dist/commands/link.js +27 -27
- package/dist/commands/link.js.map +1 -1
- package/dist/commands/publish.js +74 -21
- package/dist/commands/publish.js.map +1 -1
- package/dist/commands/release.js +30 -17
- package/dist/commands/release.js.map +1 -1
- package/dist/commands/review.js +33 -26
- package/dist/commands/review.js.map +1 -1
- package/dist/commands/select-audio.js +4 -4
- package/dist/commands/select-audio.js.map +1 -1
- package/dist/commands/tree.js +122 -35
- package/dist/commands/tree.js.map +1 -1
- package/dist/commands/unlink.js +13 -13
- package/dist/commands/unlink.js.map +1 -1
- package/dist/commands/updates.js +21 -0
- package/dist/commands/updates.js.map +1 -1
- package/dist/commands/versions.js +5 -5
- package/dist/commands/versions.js.map +1 -1
- package/dist/constants.js +4 -4
- package/dist/constants.js.map +1 -1
- package/dist/content/files.js +4 -4
- package/dist/content/files.js.map +1 -1
- package/dist/logging.js +3 -3
- package/dist/logging.js.map +1 -1
- package/dist/util/aiAdapter.js +28 -0
- package/dist/util/aiAdapter.js.map +1 -0
- package/dist/util/general.js +5 -5
- package/dist/util/general.js.map +1 -1
- package/dist/util/interactive.js +6 -437
- package/dist/util/interactive.js.map +1 -1
- package/dist/util/loggerAdapter.js +24 -0
- package/dist/util/loggerAdapter.js.map +1 -0
- package/dist/util/performance.js +4 -4
- package/dist/util/performance.js.map +1 -1
- package/dist/util/safety.js +4 -4
- package/dist/util/safety.js.map +1 -1
- package/dist/util/storage.js +2 -2
- package/dist/util/storage.js.map +1 -1
- package/dist/util/storageAdapter.js +25 -0
- package/dist/util/storageAdapter.js.map +1 -0
- package/package.json +5 -4
- package/GITHUB-TOOLS-INTEGRATION.md +0 -323
- package/INTEGRATION-SUMMARY.md +0 -232
- package/TEST-STATUS.md +0 -168
- package/dist/prompt/commit.js +0 -76
- package/dist/prompt/commit.js.map +0 -1
- package/dist/prompt/instructions/commit.md +0 -133
- package/dist/prompt/instructions/release.md +0 -188
- package/dist/prompt/instructions/review.md +0 -169
- package/dist/prompt/personas/releaser.md +0 -24
- package/dist/prompt/personas/you.md +0 -55
- package/dist/prompt/release.js +0 -100
- package/dist/prompt/release.js.map +0 -1
- package/dist/prompt/review.js +0 -64
- package/dist/prompt/review.js.map +0 -1
- package/dist/util/openai.js +0 -365
- package/dist/util/openai.js.map +0 -1
package/dist/commands/review.js
CHANGED
|
@@ -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 {
|
|
6
|
-
import {
|
|
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
|
|
14
|
-
import
|
|
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
|
|
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
|
|
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
|
|
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)=>
|
|
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 =
|
|
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
|
|
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 =
|
|
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
|
|
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
|
|
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 =
|
|
275
|
+
const parentDir = path.dirname(filePath);
|
|
275
276
|
const W_OK = 2; // fs.constants.W_OK value
|
|
276
|
-
await
|
|
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
|
|
281
|
-
await
|
|
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
|
|
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
|
|
412
|
-
const modelToUse =
|
|
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:
|
|
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
|
|
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
|
}
|