@eldrforge/kodrdriv 0.0.14 ā 0.0.17
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 +1 -9
- package/dist/arguments.js +306 -55
- package/dist/arguments.js.map +1 -1
- package/dist/audio/devices.js +284 -0
- package/dist/audio/devices.js.map +1 -0
- package/dist/audio/index.js +31 -0
- package/dist/audio/index.js.map +1 -0
- package/dist/audio/processor.js +766 -0
- package/dist/audio/processor.js.map +1 -0
- package/dist/audio/types.js +16 -0
- package/dist/audio/types.js.map +1 -0
- package/dist/audio/validation.js +35 -0
- package/dist/audio/validation.js.map +1 -0
- package/dist/commands/audio-commit.js +64 -248
- package/dist/commands/audio-commit.js.map +1 -1
- package/dist/commands/audio-review.js +90 -701
- package/dist/commands/audio-review.js.map +1 -1
- package/dist/commands/commit.js +18 -18
- package/dist/commands/commit.js.map +1 -1
- package/dist/commands/release.js +14 -15
- package/dist/commands/release.js.map +1 -1
- package/dist/commands/review.js +152 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/select-audio.js +265 -0
- package/dist/commands/select-audio.js.map +1 -0
- package/dist/constants.js +86 -68
- package/dist/constants.js.map +1 -1
- package/dist/content/diff.js +155 -1
- package/dist/content/diff.js.map +1 -1
- package/dist/content/issues.js +256 -0
- package/dist/content/issues.js.map +1 -0
- package/dist/content/releaseNotes.js +90 -0
- package/dist/content/releaseNotes.js.map +1 -0
- package/dist/main.js +9 -2
- package/dist/main.js.map +1 -1
- package/dist/prompt/instructions/commit.md +18 -15
- package/dist/prompt/instructions/release.md +6 -5
- package/dist/prompt/instructions/{audio-review.md ā review.md} +24 -18
- package/dist/prompt/prompts.js +87 -19
- package/dist/prompt/prompts.js.map +1 -1
- package/dist/types.js +27 -3
- package/dist/types.js.map +1 -1
- package/dist/util/general.js +7 -1
- package/dist/util/general.js.map +1 -1
- package/dist/util/openai.js +34 -3
- package/dist/util/openai.js.map +1 -1
- package/dist/util/stdin.js +61 -0
- package/dist/util/stdin.js.map +1 -0
- package/package.json +6 -6
- package/.kodrdriv/config.yaml +0 -20
- package/.kodrdriv/context/content.md +0 -7
- package/RELEASE_NOTES.md +0 -14
- package/docs/index.html +0 -17
- package/docs/package.json +0 -36
- package/docs/pnpm-lock.yaml +0 -3441
- package/docs/public/README.md +0 -132
- package/docs/public/advanced-usage.md +0 -188
- package/docs/public/code-icon.svg +0 -4
- package/docs/public/commands.md +0 -116
- package/docs/public/configuration.md +0 -274
- package/docs/public/examples.md +0 -352
- package/docs/public/kodrdriv-logo.svg +0 -62
- package/docs/src/App.css +0 -387
- package/docs/src/App.tsx +0 -60
- package/docs/src/components/DocumentPage.tsx +0 -56
- package/docs/src/components/ErrorMessage.tsx +0 -15
- package/docs/src/components/LoadingSpinner.tsx +0 -14
- package/docs/src/components/MarkdownRenderer.tsx +0 -56
- package/docs/src/components/Navigation.css +0 -73
- package/docs/src/components/Navigation.tsx +0 -36
- package/docs/src/index.css +0 -61
- package/docs/src/main.tsx +0 -10
- package/docs/src/test/setup.ts +0 -1
- package/docs/src/vite-env.d.ts +0 -10
- package/docs/tsconfig.node.json +0 -13
- package/docs/vite.config.ts +0 -15
- package/docs/vitest.config.ts +0 -15
- package/eslint.config.mjs +0 -83
- package/nodemon.json +0 -14
- package/output/kodrdriv/250701-1442-release-notes.md +0 -3
- package/pnpm-workspace.yaml +0 -5
- package/tsconfig.tsbuildinfo +0 -1
- package/vite.config.ts +0 -90
- package/vitest.config.ts +0 -24
|
@@ -1,724 +1,113 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import path from 'path';
|
|
3
|
-
import fs from 'fs/promises';
|
|
4
2
|
import { getLogger } from '../logging.js';
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { run } from '../util/child.js';
|
|
8
|
-
import { create } from '../content/log.js';
|
|
9
|
-
import { create as create$1 } from '../content/diff.js';
|
|
10
|
-
import { DEFAULT_EXCLUDED_PATTERNS, DEFAULT_OUTPUT_DIRECTORY } from '../constants.js';
|
|
11
|
-
import { getOutputPath, getTimestampedResponseFilename, getTimestampedRequestFilename } from '../util/general.js';
|
|
12
|
-
import { create as create$2 } from '../util/storage.js';
|
|
13
|
-
import { getOpenIssues, createIssue } from '../util/github.js';
|
|
3
|
+
import { execute as execute$1 } from './review.js';
|
|
4
|
+
import { processAudio } from '../audio/index.js';
|
|
14
5
|
|
|
15
|
-
// Enhanced exclusion patterns specifically for audio review context
|
|
16
|
-
// These focus on excluding large files, binaries, and content that doesn't help with issue analysis
|
|
17
|
-
const getAudioReviewExcludedPatterns = (basePatterns)=>{
|
|
18
|
-
const audioReviewSpecificExclusions = [
|
|
19
|
-
// Lock files and dependency files (often massive)
|
|
20
|
-
"*lock*",
|
|
21
|
-
"*.lock",
|
|
22
|
-
"pnpm-lock.yaml",
|
|
23
|
-
"package-lock.json",
|
|
24
|
-
"yarn.lock",
|
|
25
|
-
"bun.lockb",
|
|
26
|
-
"composer.lock",
|
|
27
|
-
"Cargo.lock",
|
|
28
|
-
"Gemfile.lock",
|
|
29
|
-
"Pipfile.lock",
|
|
30
|
-
"poetry.lock",
|
|
31
|
-
// Image files (binary and large)
|
|
32
|
-
"*.png",
|
|
33
|
-
"*.jpg",
|
|
34
|
-
"*.jpeg",
|
|
35
|
-
"*.gif",
|
|
36
|
-
"*.bmp",
|
|
37
|
-
"*.tiff",
|
|
38
|
-
"*.webp",
|
|
39
|
-
"*.svg",
|
|
40
|
-
"*.ico",
|
|
41
|
-
"*.icns",
|
|
42
|
-
// Video and audio files
|
|
43
|
-
"*.mp4",
|
|
44
|
-
"*.avi",
|
|
45
|
-
"*.mov",
|
|
46
|
-
"*.wmv",
|
|
47
|
-
"*.flv",
|
|
48
|
-
"*.mp3",
|
|
49
|
-
"*.wav",
|
|
50
|
-
"*.flac",
|
|
51
|
-
// Archives and compressed files
|
|
52
|
-
"*.zip",
|
|
53
|
-
"*.tar",
|
|
54
|
-
"*.tar.gz",
|
|
55
|
-
"*.tgz",
|
|
56
|
-
"*.rar",
|
|
57
|
-
"*.7z",
|
|
58
|
-
"*.bz2",
|
|
59
|
-
"*.xz",
|
|
60
|
-
// Binary executables and libraries
|
|
61
|
-
"*.exe",
|
|
62
|
-
"*.dll",
|
|
63
|
-
"*.so",
|
|
64
|
-
"*.dylib",
|
|
65
|
-
"*.bin",
|
|
66
|
-
"*.app",
|
|
67
|
-
// Database files
|
|
68
|
-
"*.db",
|
|
69
|
-
"*.sqlite",
|
|
70
|
-
"*.sqlite3",
|
|
71
|
-
"*.mdb",
|
|
72
|
-
// Large generated files
|
|
73
|
-
"*.map",
|
|
74
|
-
"*.min.js",
|
|
75
|
-
"*.min.css",
|
|
76
|
-
"bundle.*",
|
|
77
|
-
"vendor.*",
|
|
78
|
-
// Documentation that's often large
|
|
79
|
-
"*.pdf",
|
|
80
|
-
"*.doc",
|
|
81
|
-
"*.docx",
|
|
82
|
-
"*.ppt",
|
|
83
|
-
"*.pptx",
|
|
84
|
-
// IDE and OS generated files
|
|
85
|
-
".DS_Store",
|
|
86
|
-
"Thumbs.db",
|
|
87
|
-
"*.swp",
|
|
88
|
-
"*.tmp",
|
|
89
|
-
// Certificate and key files
|
|
90
|
-
"*.pem",
|
|
91
|
-
"*.crt",
|
|
92
|
-
"*.key",
|
|
93
|
-
"*.p12",
|
|
94
|
-
"*.pfx",
|
|
95
|
-
// Large config/data files that are often auto-generated
|
|
96
|
-
"tsconfig.tsbuildinfo",
|
|
97
|
-
"*.cache",
|
|
98
|
-
".eslintcache"
|
|
99
|
-
];
|
|
100
|
-
// Combine base patterns with audio review specific exclusions, removing duplicates
|
|
101
|
-
const combinedPatterns = [
|
|
102
|
-
...new Set([
|
|
103
|
-
...basePatterns,
|
|
104
|
-
...audioReviewSpecificExclusions
|
|
105
|
-
])
|
|
106
|
-
];
|
|
107
|
-
return combinedPatterns;
|
|
108
|
-
};
|
|
109
|
-
// Function to truncate overly large diff content while preserving structure
|
|
110
|
-
const truncateLargeDiff = (diffContent, maxLength = 5000)=>{
|
|
111
|
-
if (diffContent.length <= maxLength) {
|
|
112
|
-
return diffContent;
|
|
113
|
-
}
|
|
114
|
-
const lines = diffContent.split('\n');
|
|
115
|
-
const truncatedLines = [];
|
|
116
|
-
let currentLength = 0;
|
|
117
|
-
let truncated = false;
|
|
118
|
-
for (const line of lines){
|
|
119
|
-
if (currentLength + line.length + 1 > maxLength) {
|
|
120
|
-
truncated = true;
|
|
121
|
-
break;
|
|
122
|
-
}
|
|
123
|
-
truncatedLines.push(line);
|
|
124
|
-
currentLength += line.length + 1; // +1 for newline
|
|
125
|
-
}
|
|
126
|
-
if (truncated) {
|
|
127
|
-
truncatedLines.push('');
|
|
128
|
-
truncatedLines.push(`... [TRUNCATED: Original diff was ${diffContent.length} characters, showing first ${currentLength}] ...`);
|
|
129
|
-
}
|
|
130
|
-
return truncatedLines.join('\n');
|
|
131
|
-
};
|
|
132
|
-
// Function to find and read recent release notes
|
|
133
|
-
const findRecentReleaseNotes = async (limit, outputDirectory)=>{
|
|
134
|
-
const logger = getLogger();
|
|
135
|
-
const releaseNotes = [];
|
|
136
|
-
if (limit <= 0) {
|
|
137
|
-
return releaseNotes;
|
|
138
|
-
}
|
|
139
|
-
try {
|
|
140
|
-
// Common release notes file patterns
|
|
141
|
-
const patterns = [
|
|
142
|
-
'RELEASE_NOTES.md',
|
|
143
|
-
'CHANGELOG.md',
|
|
144
|
-
'CHANGES.md',
|
|
145
|
-
'HISTORY.md',
|
|
146
|
-
'RELEASES.md'
|
|
147
|
-
];
|
|
148
|
-
// If outputDirectory is specified, check there first for RELEASE_NOTES.md
|
|
149
|
-
if (outputDirectory) {
|
|
150
|
-
try {
|
|
151
|
-
const outputReleaseNotesPath = getOutputPath(outputDirectory, 'RELEASE_NOTES.md');
|
|
152
|
-
const content = await fs.readFile(outputReleaseNotesPath, 'utf-8');
|
|
153
|
-
if (content.trim()) {
|
|
154
|
-
const truncatedContent = truncateLargeDiff(content, 3000);
|
|
155
|
-
releaseNotes.push(`=== ${outputReleaseNotesPath} ===\n${truncatedContent}`);
|
|
156
|
-
logger.debug(`Found release notes in output directory: ${outputReleaseNotesPath} (%d characters)`, content.length);
|
|
157
|
-
if (releaseNotes.length >= limit) {
|
|
158
|
-
return releaseNotes.slice(0, limit);
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
} catch {
|
|
162
|
-
// File doesn't exist in output directory, continue with other patterns
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
for (const pattern of patterns){
|
|
166
|
-
try {
|
|
167
|
-
const content = await fs.readFile(pattern, 'utf-8');
|
|
168
|
-
if (content.trim()) {
|
|
169
|
-
// Truncate very large release notes files
|
|
170
|
-
const truncatedContent = truncateLargeDiff(content, 3000); // Smaller limit for release notes
|
|
171
|
-
releaseNotes.push(`=== ${pattern} ===\n${truncatedContent}`);
|
|
172
|
-
if (truncatedContent.length < content.length) {
|
|
173
|
-
logger.debug(`Found release notes in ${pattern} (%d characters, truncated from %d)`, truncatedContent.length, content.length);
|
|
174
|
-
} else {
|
|
175
|
-
logger.debug(`Found release notes in ${pattern} (%d characters)`, content.length);
|
|
176
|
-
}
|
|
177
|
-
// For now, just take the first file found
|
|
178
|
-
// Could be enhanced to parse multiple releases from a single file
|
|
179
|
-
if (releaseNotes.length >= limit) {
|
|
180
|
-
break;
|
|
181
|
-
}
|
|
182
|
-
}
|
|
183
|
-
} catch {
|
|
184
|
-
continue;
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
if (releaseNotes.length === 0) {
|
|
188
|
-
logger.debug('No release notes files found');
|
|
189
|
-
}
|
|
190
|
-
} catch (error) {
|
|
191
|
-
logger.warn('Error searching for release notes: %s', error.message);
|
|
192
|
-
}
|
|
193
|
-
return releaseNotes.slice(0, limit);
|
|
194
|
-
};
|
|
195
6
|
const execute = async (runConfig)=>{
|
|
196
|
-
var _runConfig_audioReview, _runConfig_audioReview1, _runConfig_audioReview2, _runConfig_audioReview3, _runConfig_audioReview4, _runConfig_audioReview5, _runConfig_audioReview6, _runConfig_audioReview7, _runConfig_audioReview8, _runConfig_audioReview9,
|
|
7
|
+
var _runConfig_audioReview, _runConfig_audioReview1, _runConfig_audioReview2, _runConfig_audioReview3, _runConfig_audioReview4, _runConfig_audioReview5, _runConfig_audioReview6, _runConfig_audioReview7, _runConfig_audioReview8, _runConfig_audioReview9, _runConfig_review;
|
|
197
8
|
const logger = getLogger();
|
|
198
9
|
const isDryRun = runConfig.dryRun || false;
|
|
199
|
-
// Show configuration even in dry-run mode
|
|
200
|
-
logger.debug('Audio review context configuration:');
|
|
201
|
-
logger.debug(' Include commit history: %s', (_runConfig_audioReview = runConfig.audioReview) === null || _runConfig_audioReview === void 0 ? void 0 : _runConfig_audioReview.includeCommitHistory);
|
|
202
|
-
logger.debug(' Include recent diffs: %s', (_runConfig_audioReview1 = runConfig.audioReview) === null || _runConfig_audioReview1 === void 0 ? void 0 : _runConfig_audioReview1.includeRecentDiffs);
|
|
203
|
-
logger.debug(' Include release notes: %s', (_runConfig_audioReview2 = runConfig.audioReview) === null || _runConfig_audioReview2 === void 0 ? void 0 : _runConfig_audioReview2.includeReleaseNotes);
|
|
204
|
-
logger.debug(' Include GitHub issues: %s', (_runConfig_audioReview3 = runConfig.audioReview) === null || _runConfig_audioReview3 === void 0 ? void 0 : _runConfig_audioReview3.includeGithubIssues);
|
|
205
|
-
logger.debug(' Commit history limit: %d', (_runConfig_audioReview4 = runConfig.audioReview) === null || _runConfig_audioReview4 === void 0 ? void 0 : _runConfig_audioReview4.commitHistoryLimit);
|
|
206
|
-
logger.debug(' Diff history limit: %d', (_runConfig_audioReview5 = runConfig.audioReview) === null || _runConfig_audioReview5 === void 0 ? void 0 : _runConfig_audioReview5.diffHistoryLimit);
|
|
207
|
-
logger.debug(' Release notes limit: %d', (_runConfig_audioReview6 = runConfig.audioReview) === null || _runConfig_audioReview6 === void 0 ? void 0 : _runConfig_audioReview6.releaseNotesLimit);
|
|
208
|
-
logger.debug(' GitHub issues limit: %d', (_runConfig_audioReview7 = runConfig.audioReview) === null || _runConfig_audioReview7 === void 0 ? void 0 : _runConfig_audioReview7.githubIssuesLimit);
|
|
209
|
-
logger.debug(' Sendit mode (auto-create issues): %s', (_runConfig_audioReview8 = runConfig.audioReview) === null || _runConfig_audioReview8 === void 0 ? void 0 : _runConfig_audioReview8.sendit);
|
|
210
10
|
if (isDryRun) {
|
|
211
|
-
var
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
if ((_runConfig_audioReview13 = runConfig.audioReview) === null || _runConfig_audioReview13 === void 0 ? void 0 : _runConfig_audioReview13.sendit) {
|
|
216
|
-
logger.info('DRY RUN: Would automatically create GitHub issues (sendit mode enabled)');
|
|
11
|
+
var _runConfig_audioReview10, _runConfig_review1;
|
|
12
|
+
if ((_runConfig_audioReview10 = runConfig.audioReview) === null || _runConfig_audioReview10 === void 0 ? void 0 : _runConfig_audioReview10.file) {
|
|
13
|
+
logger.info('DRY RUN: Would process audio file: %s', runConfig.audioReview.file);
|
|
14
|
+
logger.info('DRY RUN: Would transcribe audio and use as context for review analysis');
|
|
217
15
|
} else {
|
|
218
|
-
logger.info('DRY RUN: Would
|
|
16
|
+
logger.info('DRY RUN: Would start audio recording for review context');
|
|
17
|
+
logger.info('DRY RUN: Would transcribe audio and use as context for review analysis');
|
|
219
18
|
}
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
}
|
|
228
|
-
return 'DRY RUN: Audio review command would record, transcribe, analyze audio, and create GitHub issues';
|
|
229
|
-
}
|
|
230
|
-
// Gather additional context based on configuration
|
|
231
|
-
let additionalContext = '';
|
|
232
|
-
// Fetch commit history if enabled
|
|
233
|
-
if ((_runConfig_audioReview9 = runConfig.audioReview) === null || _runConfig_audioReview9 === void 0 ? void 0 : _runConfig_audioReview9.includeCommitHistory) {
|
|
234
|
-
try {
|
|
235
|
-
logger.debug('Fetching recent commit history...');
|
|
236
|
-
const log = await create({
|
|
237
|
-
limit: runConfig.audioReview.commitHistoryLimit
|
|
238
|
-
});
|
|
239
|
-
const logContent = await log.get();
|
|
240
|
-
if (logContent.trim()) {
|
|
241
|
-
additionalContext += `\n\n[Recent Commit History]\n${logContent}`;
|
|
242
|
-
logger.debug('Added commit history to context (%d characters)', logContent.length);
|
|
19
|
+
logger.info('DRY RUN: Would then delegate to regular review command');
|
|
20
|
+
// In dry run, just call the regular review command with empty note
|
|
21
|
+
return execute$1({
|
|
22
|
+
...runConfig,
|
|
23
|
+
review: {
|
|
24
|
+
...runConfig.review,
|
|
25
|
+
note: ((_runConfig_review1 = runConfig.review) === null || _runConfig_review1 === void 0 ? void 0 : _runConfig_review1.note) || ''
|
|
243
26
|
}
|
|
244
|
-
}
|
|
245
|
-
logger.warn('Failed to fetch commit history: %s', error.message);
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
// Fetch recent diffs if enabled
|
|
249
|
-
if ((_runConfig_audioReview10 = runConfig.audioReview) === null || _runConfig_audioReview10 === void 0 ? void 0 : _runConfig_audioReview10.includeRecentDiffs) {
|
|
250
|
-
try {
|
|
251
|
-
logger.debug('Fetching recent commit diffs...');
|
|
252
|
-
const diffLimit = runConfig.audioReview.diffHistoryLimit || 5;
|
|
253
|
-
var _runConfig_excludedPatterns1;
|
|
254
|
-
// Get enhanced exclusion patterns for audio review context
|
|
255
|
-
const basePatterns = (_runConfig_excludedPatterns1 = runConfig.excludedPatterns) !== null && _runConfig_excludedPatterns1 !== void 0 ? _runConfig_excludedPatterns1 : DEFAULT_EXCLUDED_PATTERNS;
|
|
256
|
-
const audioReviewExcluded = getAudioReviewExcludedPatterns(basePatterns);
|
|
257
|
-
logger.debug('Using %d exclusion patterns for diff context (including %d audio-review specific)', audioReviewExcluded.length, audioReviewExcluded.length - basePatterns.length);
|
|
258
|
-
logger.debug('Sample exclusions: %s', audioReviewExcluded.slice(0, 10).join(', ') + (audioReviewExcluded.length > 10 ? '...' : ''));
|
|
259
|
-
// Get recent commits and their diffs
|
|
260
|
-
for(let i = 0; i < diffLimit; i++){
|
|
261
|
-
try {
|
|
262
|
-
const diffRange = i === 0 ? 'HEAD~1' : `HEAD~${i + 1}..HEAD~${i}`;
|
|
263
|
-
const diff = await create$1({
|
|
264
|
-
from: `HEAD~${i + 1}`,
|
|
265
|
-
to: `HEAD~${i}`,
|
|
266
|
-
excludedPatterns: audioReviewExcluded
|
|
267
|
-
});
|
|
268
|
-
const diffContent = await diff.get();
|
|
269
|
-
if (diffContent.trim()) {
|
|
270
|
-
const truncatedDiff = truncateLargeDiff(diffContent);
|
|
271
|
-
additionalContext += `\n\n[Recent Diff ${i + 1} (${diffRange})]\n${truncatedDiff}`;
|
|
272
|
-
if (truncatedDiff.length < diffContent.length) {
|
|
273
|
-
logger.debug('Added diff %d to context (%d characters, truncated from %d)', i + 1, truncatedDiff.length, diffContent.length);
|
|
274
|
-
} else {
|
|
275
|
-
logger.debug('Added diff %d to context (%d characters)', i + 1, diffContent.length);
|
|
276
|
-
}
|
|
277
|
-
} else {
|
|
278
|
-
logger.debug('Diff %d was empty after exclusions', i + 1);
|
|
279
|
-
}
|
|
280
|
-
} catch (error) {
|
|
281
|
-
logger.debug('Could not fetch diff %d: %s', i + 1, error.message);
|
|
282
|
-
break; // Stop if we can't fetch more diffs
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
} catch (error) {
|
|
286
|
-
logger.warn('Failed to fetch recent diffs: %s', error.message);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
// Fetch release notes if enabled
|
|
290
|
-
if ((_runConfig_audioReview11 = runConfig.audioReview) === null || _runConfig_audioReview11 === void 0 ? void 0 : _runConfig_audioReview11.includeReleaseNotes) {
|
|
291
|
-
try {
|
|
292
|
-
logger.debug('Fetching recent release notes...');
|
|
293
|
-
const outputDirectory = runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY;
|
|
294
|
-
const releaseNotes = await findRecentReleaseNotes(runConfig.audioReview.releaseNotesLimit || 3, outputDirectory);
|
|
295
|
-
if (releaseNotes.length > 0) {
|
|
296
|
-
additionalContext += `\n\n[Recent Release Notes]\n${releaseNotes.join('\n\n')}`;
|
|
297
|
-
logger.debug('Added %d release notes files to context', releaseNotes.length);
|
|
298
|
-
}
|
|
299
|
-
} catch (error) {
|
|
300
|
-
logger.warn('Failed to fetch release notes: %s', error.message);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
// Fetch GitHub issues if enabled
|
|
304
|
-
if ((_runConfig_audioReview12 = runConfig.audioReview) === null || _runConfig_audioReview12 === void 0 ? void 0 : _runConfig_audioReview12.includeGithubIssues) {
|
|
305
|
-
try {
|
|
306
|
-
logger.debug('Fetching open GitHub issues...');
|
|
307
|
-
const issuesLimit = Math.min(runConfig.audioReview.githubIssuesLimit || 20, 20); // Cap at 20
|
|
308
|
-
const githubIssues = await getOpenIssues(issuesLimit);
|
|
309
|
-
if (githubIssues.trim()) {
|
|
310
|
-
additionalContext += `\n\n[Open GitHub Issues]\n${githubIssues}`;
|
|
311
|
-
logger.debug('Added GitHub issues to context (%d characters)', githubIssues.length);
|
|
312
|
-
} else {
|
|
313
|
-
logger.debug('No open GitHub issues found');
|
|
314
|
-
}
|
|
315
|
-
} catch (error) {
|
|
316
|
-
logger.warn('Failed to fetch GitHub issues: %s', error.message);
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
if (additionalContext) {
|
|
320
|
-
logger.debug('Total additional context gathered: %d characters', additionalContext.length);
|
|
321
|
-
} else {
|
|
322
|
-
logger.debug('No additional context gathered');
|
|
27
|
+
});
|
|
323
28
|
}
|
|
324
|
-
|
|
325
|
-
logger.info('This command will use your system\'s default audio recording tool');
|
|
326
|
-
logger.info('Press Ctrl+C after you finish speaking to analyze the audio');
|
|
327
|
-
// Create temporary file for audio recording
|
|
328
|
-
const outputDirectory = runConfig.outputDirectory || DEFAULT_OUTPUT_DIRECTORY;
|
|
329
|
-
const storage = create$2({
|
|
330
|
-
log: logger.info
|
|
331
|
-
});
|
|
332
|
-
await storage.ensureDirectory(outputDirectory);
|
|
333
|
-
const tempDir = await fs.mkdtemp(path.join(outputDirectory, '.temp-audio-'));
|
|
334
|
-
const audioFilePath = path.join(tempDir, 'recording.wav');
|
|
29
|
+
let audioContext;
|
|
335
30
|
try {
|
|
336
|
-
var _runConfig_audioReview15;
|
|
337
|
-
//
|
|
338
|
-
logger.info('
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
} catch {
|
|
351
|
-
// ffmpeg not available, try sox/rec
|
|
352
|
-
try {
|
|
353
|
-
await run('which rec');
|
|
354
|
-
recordCommand = `rec -r 44100 -c 1 -t wav "${audioFilePath}" trim 0 30`;
|
|
355
|
-
} catch {
|
|
356
|
-
// Neither available, use manual fallback
|
|
357
|
-
throw new Error('MANUAL_RECORDING_NEEDED');
|
|
358
|
-
}
|
|
359
|
-
}
|
|
360
|
-
} else if (process.platform === 'win32') {
|
|
361
|
-
// Windows - use ffmpeg if available, otherwise fallback
|
|
362
|
-
try {
|
|
363
|
-
await run('where ffmpeg');
|
|
364
|
-
recordCommand = `ffmpeg -f dshow -i audio="Microphone" -t 30 -y "${audioFilePath}"`;
|
|
365
|
-
} catch {
|
|
366
|
-
throw new Error('MANUAL_RECORDING_NEEDED');
|
|
367
|
-
}
|
|
368
|
-
} else {
|
|
369
|
-
// Linux - use arecord (ALSA) or ffmpeg
|
|
370
|
-
try {
|
|
371
|
-
await run('which arecord');
|
|
372
|
-
recordCommand = `arecord -f cd -t wav -d 30 "${audioFilePath}"`;
|
|
373
|
-
} catch {
|
|
374
|
-
try {
|
|
375
|
-
await run('which ffmpeg');
|
|
376
|
-
recordCommand = `ffmpeg -f alsa -i default -t 30 -y "${audioFilePath}"`;
|
|
377
|
-
} catch {
|
|
378
|
-
throw new Error('MANUAL_RECORDING_NEEDED');
|
|
379
|
-
}
|
|
380
|
-
}
|
|
381
|
-
}
|
|
382
|
-
// Start recording as a background process
|
|
383
|
-
try {
|
|
384
|
-
recordingProcess = run(recordCommand);
|
|
385
|
-
} catch (error) {
|
|
386
|
-
if (error.message === 'MANUAL_RECORDING_NEEDED') {
|
|
387
|
-
// Provide helpful instructions for manual recording
|
|
388
|
-
logger.warn('ā ļø Automatic recording not available on this system.');
|
|
389
|
-
logger.warn('š± Please record audio manually using your system\'s built-in tools:');
|
|
390
|
-
logger.warn('');
|
|
391
|
-
if (process.platform === 'darwin') {
|
|
392
|
-
logger.warn('š macOS options:');
|
|
393
|
-
logger.warn(' 1. Use QuickTime Player: File ā New Audio Recording');
|
|
394
|
-
logger.warn(' 2. Use Voice Memos app');
|
|
395
|
-
logger.warn(' 3. Install ffmpeg: brew install ffmpeg');
|
|
396
|
-
logger.warn(' 4. Install sox: brew install sox');
|
|
397
|
-
} else if (process.platform === 'win32') {
|
|
398
|
-
logger.warn('šŖ Windows options:');
|
|
399
|
-
logger.warn(' 1. Use Voice Recorder app');
|
|
400
|
-
logger.warn(' 2. Install ffmpeg: https://ffmpeg.org/download.html');
|
|
401
|
-
} else {
|
|
402
|
-
logger.warn('š§ Linux options:');
|
|
403
|
-
logger.warn(' 1. Install alsa-utils: sudo apt install alsa-utils');
|
|
404
|
-
logger.warn(' 2. Install ffmpeg: sudo apt install ffmpeg');
|
|
405
|
-
}
|
|
406
|
-
logger.warn('');
|
|
407
|
-
logger.warn(`š¾ Save your recording as: ${audioFilePath}`);
|
|
408
|
-
logger.warn('šµ Recommended format: WAV, 44.1kHz, mono or stereo');
|
|
409
|
-
logger.warn('');
|
|
410
|
-
logger.warn('āØļø Press ENTER when you have saved the audio file...');
|
|
411
|
-
// Wait for user input
|
|
412
|
-
await new Promise((resolve)=>{
|
|
413
|
-
process.stdin.setRawMode(true);
|
|
414
|
-
process.stdin.resume();
|
|
415
|
-
process.stdin.on('data', (key)=>{
|
|
416
|
-
if (key[0] === 13) {
|
|
417
|
-
process.stdin.setRawMode(false);
|
|
418
|
-
process.stdin.pause();
|
|
419
|
-
resolve(void 0);
|
|
420
|
-
}
|
|
421
|
-
});
|
|
422
|
-
});
|
|
423
|
-
} else {
|
|
424
|
-
throw error;
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
// Set up graceful shutdown
|
|
428
|
-
const stopRecording = async ()=>{
|
|
429
|
-
if (!recordingFinished) {
|
|
430
|
-
recordingFinished = true;
|
|
431
|
-
if (recordingProcess && recordingProcess.kill) {
|
|
432
|
-
recordingProcess.kill();
|
|
433
|
-
}
|
|
434
|
-
logger.info('š Recording stopped');
|
|
435
|
-
}
|
|
436
|
-
};
|
|
437
|
-
// Listen for Ctrl+C
|
|
438
|
-
process.on('SIGINT', stopRecording);
|
|
439
|
-
process.on('SIGTERM', stopRecording);
|
|
440
|
-
// Wait for recording to complete (either by timeout or user interruption)
|
|
441
|
-
if (recordingProcess) {
|
|
442
|
-
try {
|
|
443
|
-
await recordingProcess;
|
|
444
|
-
} catch (error) {
|
|
445
|
-
// Check if this is just a normal interruption (expected behavior)
|
|
446
|
-
if (error.message.includes('signal 15') || error.message.includes('SIGTERM') || error.message.includes('Exiting normally')) {
|
|
447
|
-
// This is expected when we interrupt ffmpeg - not an actual error
|
|
448
|
-
logger.debug('Recording interrupted as expected: %s', error.message);
|
|
449
|
-
} else {
|
|
450
|
-
// This might be a real error, but let's check if we got an audio file anyway
|
|
451
|
-
logger.warn('Recording process exited with error, but checking for audio file: %s', error.message);
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
}
|
|
455
|
-
// Check if audio file exists
|
|
456
|
-
try {
|
|
457
|
-
await fs.access(audioFilePath);
|
|
458
|
-
} catch {
|
|
459
|
-
throw new Error('No audio file was created. Please ensure your system has audio recording capabilities.');
|
|
460
|
-
}
|
|
461
|
-
const stats = await fs.stat(audioFilePath);
|
|
462
|
-
if (stats.size === 0) {
|
|
463
|
-
throw new Error('Audio file is empty. Please check your microphone permissions and try again.');
|
|
464
|
-
}
|
|
465
|
-
logger.info('š¾ Audio recorded successfully');
|
|
466
|
-
// Transcribe audio using Whisper
|
|
467
|
-
logger.info('š¤ Transcribing audio with Whisper...');
|
|
468
|
-
const transcription = await transcribeAudio(audioFilePath, {
|
|
469
|
-
model: 'whisper-1',
|
|
470
|
-
debug: runConfig.debug,
|
|
471
|
-
debugRequestFile: runConfig.debug ? getOutputPath(outputDirectory, getTimestampedRequestFilename('audio-transcription')) : undefined,
|
|
472
|
-
debugResponseFile: runConfig.debug ? getOutputPath(outputDirectory, getTimestampedResponseFilename('audio-transcription')) : undefined
|
|
473
|
-
});
|
|
474
|
-
logger.info('š Transcription completed');
|
|
475
|
-
logger.debug('Transcription: %s', transcription.text);
|
|
476
|
-
// Analyze transcription for issues using OpenAI
|
|
477
|
-
logger.info('š¤ Analyzing transcription for project issues...');
|
|
478
|
-
const prompts = create$3(runConfig.model, runConfig);
|
|
479
|
-
// Combine additional context with user-provided context
|
|
480
|
-
let finalContext = additionalContext;
|
|
481
|
-
if ((_runConfig_audioReview15 = runConfig.audioReview) === null || _runConfig_audioReview15 === void 0 ? void 0 : _runConfig_audioReview15.context) {
|
|
482
|
-
finalContext = runConfig.audioReview.context + finalContext;
|
|
483
|
-
}
|
|
484
|
-
const analysisPrompt = await prompts.createAudioReviewPrompt(transcription.text, finalContext || undefined);
|
|
485
|
-
const request = prompts.format(analysisPrompt);
|
|
486
|
-
const result = await createCompletion(request.messages, {
|
|
487
|
-
model: runConfig.model,
|
|
488
|
-
responseFormat: {
|
|
489
|
-
type: 'json_object'
|
|
490
|
-
},
|
|
31
|
+
var _runConfig_audioReview11, _runConfig_audioReview12, _runConfig_audioReview13, _runConfig_audioReview14, _runConfig_audioReview15;
|
|
32
|
+
// Process audio using the audio subsystem
|
|
33
|
+
logger.info('šļø Starting audio processing for review context...');
|
|
34
|
+
if (!((_runConfig_audioReview11 = runConfig.audioReview) === null || _runConfig_audioReview11 === void 0 ? void 0 : _runConfig_audioReview11.file)) {
|
|
35
|
+
logger.info('This command will use your system\'s default audio recording tool');
|
|
36
|
+
logger.info('š” Tip: Run "kodrdriv select-audio" to choose a specific microphone');
|
|
37
|
+
logger.info('Press Ctrl+C after you finish speaking to generate your review analysis');
|
|
38
|
+
}
|
|
39
|
+
const result = await processAudio({
|
|
40
|
+
file: (_runConfig_audioReview12 = runConfig.audioReview) === null || _runConfig_audioReview12 === void 0 ? void 0 : _runConfig_audioReview12.file,
|
|
41
|
+
audioDevice: (_runConfig_audioReview13 = runConfig.audioReview) === null || _runConfig_audioReview13 === void 0 ? void 0 : _runConfig_audioReview13.audioDevice,
|
|
42
|
+
maxRecordingTime: (_runConfig_audioReview14 = runConfig.audioReview) === null || _runConfig_audioReview14 === void 0 ? void 0 : _runConfig_audioReview14.maxRecordingTime,
|
|
43
|
+
outputDirectory: runConfig.outputDirectory,
|
|
44
|
+
preferencesDirectory: runConfig.preferencesDirectory,
|
|
491
45
|
debug: runConfig.debug,
|
|
492
|
-
|
|
493
|
-
|
|
46
|
+
dryRun: isDryRun,
|
|
47
|
+
keepTemp: (_runConfig_audioReview15 = runConfig.audioReview) === null || _runConfig_audioReview15 === void 0 ? void 0 : _runConfig_audioReview15.keepTemp
|
|
494
48
|
});
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
// Interactive confirmation for each issue
|
|
507
|
-
logger.info(`\nš Issue ${i + 1} of ${result.issues.length}:`);
|
|
508
|
-
logger.info(` Title: ${issue.title}`);
|
|
509
|
-
logger.info(` Priority: ${issue.priority} | Category: ${issue.category}`);
|
|
510
|
-
logger.info(` Description: ${issue.description}`);
|
|
511
|
-
if (issue.suggestions && issue.suggestions.length > 0) {
|
|
512
|
-
logger.info(` Suggestions: ${issue.suggestions.join(', ')}`);
|
|
513
|
-
}
|
|
514
|
-
// Get user choice
|
|
515
|
-
const choice = await getUserChoice('\nWhat would you like to do with this issue?', [
|
|
516
|
-
{
|
|
517
|
-
key: 'c',
|
|
518
|
-
label: 'Create GitHub issue'
|
|
519
|
-
},
|
|
520
|
-
{
|
|
521
|
-
key: 's',
|
|
522
|
-
label: 'Skip this issue'
|
|
523
|
-
},
|
|
524
|
-
{
|
|
525
|
-
key: 'e',
|
|
526
|
-
label: 'Edit issue details'
|
|
527
|
-
}
|
|
528
|
-
]);
|
|
529
|
-
if (choice === 'c') {
|
|
530
|
-
shouldCreateIssue = true;
|
|
531
|
-
} else if (choice === 'e') {
|
|
532
|
-
// Allow user to edit the issue
|
|
533
|
-
const editedIssue = await editIssueInteractively(issue);
|
|
534
|
-
result.issues[i] = editedIssue;
|
|
535
|
-
shouldCreateIssue = true;
|
|
536
|
-
}
|
|
537
|
-
// If choice is 's', shouldCreateIssue remains false
|
|
538
|
-
}
|
|
539
|
-
if (shouldCreateIssue) {
|
|
540
|
-
try {
|
|
541
|
-
logger.info(`š Creating GitHub issue: "${issue.title}"`);
|
|
542
|
-
// Format issue body with additional details
|
|
543
|
-
const issueBody = formatIssueBody(issue);
|
|
544
|
-
// Create labels based on priority and category
|
|
545
|
-
const labels = [
|
|
546
|
-
`priority-${issue.priority}`,
|
|
547
|
-
`category-${issue.category}`,
|
|
548
|
-
'audio-review'
|
|
549
|
-
];
|
|
550
|
-
const createdIssue = await createIssue(issue.title, issueBody, labels);
|
|
551
|
-
createdIssues.push({
|
|
552
|
-
issue,
|
|
553
|
-
githubUrl: createdIssue.html_url,
|
|
554
|
-
number: createdIssue.number
|
|
555
|
-
});
|
|
556
|
-
logger.info(`ā
Created GitHub issue #${createdIssue.number}: ${createdIssue.html_url}`);
|
|
557
|
-
} catch (error) {
|
|
558
|
-
logger.error(`ā Failed to create GitHub issue for "${issue.title}": ${error.message}`);
|
|
559
|
-
}
|
|
560
|
-
}
|
|
561
|
-
}
|
|
562
|
-
// Update the result summary to include created issues
|
|
563
|
-
if (createdIssues.length > 0) {
|
|
564
|
-
return formatAudioReviewResultsWithIssues(result, createdIssues);
|
|
565
|
-
}
|
|
49
|
+
// If the recording was cancelled, exit
|
|
50
|
+
if (result.cancelled) {
|
|
51
|
+
logger.info('ā Audio review cancelled by user');
|
|
52
|
+
process.exit(0);
|
|
53
|
+
}
|
|
54
|
+
audioContext = result.transcript;
|
|
55
|
+
if (!audioContext.trim()) {
|
|
56
|
+
logger.warn('No audio content was transcribed. Proceeding without audio context.');
|
|
57
|
+
audioContext = '';
|
|
58
|
+
} else {
|
|
59
|
+
logger.info('š Using transcribed audio as review note');
|
|
566
60
|
}
|
|
567
|
-
// Format and return results (original behavior if no issues created)
|
|
568
|
-
return formatAudioReviewResults(result);
|
|
569
61
|
} catch (error) {
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
62
|
+
if (error.message.includes('No audio device configured')) {
|
|
63
|
+
logger.error('ā No audio device configured. Please run "kodrdriv select-audio" first to configure your audio device.');
|
|
64
|
+
logger.info('š” This will create %s/audio-device.yaml with your preferred audio device.', runConfig.preferencesDirectory);
|
|
65
|
+
process.exit(1);
|
|
66
|
+
}
|
|
67
|
+
// If audio recording failed, exit instead of continuing
|
|
68
|
+
if (error.message.includes('Audio recording failed')) {
|
|
69
|
+
logger.error('ā Audio recording failed. Cannot proceed with audio-review command.');
|
|
70
|
+
logger.info('š” Try running "kodrdriv select-audio" to choose a different audio device');
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
logger.error('Audio processing failed: %s', error.message);
|
|
74
|
+
logger.info('Proceeding with review analysis without audio context...');
|
|
75
|
+
audioContext = '';
|
|
76
|
+
}
|
|
77
|
+
// Now delegate to the regular review command with the audio context
|
|
78
|
+
logger.info('š¤ Analyzing review using audio context...');
|
|
79
|
+
const result = await execute$1({
|
|
80
|
+
...runConfig,
|
|
81
|
+
review: {
|
|
82
|
+
// Map audioReview configuration to review configuration
|
|
83
|
+
includeCommitHistory: (_runConfig_audioReview = runConfig.audioReview) === null || _runConfig_audioReview === void 0 ? void 0 : _runConfig_audioReview.includeCommitHistory,
|
|
84
|
+
includeRecentDiffs: (_runConfig_audioReview1 = runConfig.audioReview) === null || _runConfig_audioReview1 === void 0 ? void 0 : _runConfig_audioReview1.includeRecentDiffs,
|
|
85
|
+
includeReleaseNotes: (_runConfig_audioReview2 = runConfig.audioReview) === null || _runConfig_audioReview2 === void 0 ? void 0 : _runConfig_audioReview2.includeReleaseNotes,
|
|
86
|
+
includeGithubIssues: (_runConfig_audioReview3 = runConfig.audioReview) === null || _runConfig_audioReview3 === void 0 ? void 0 : _runConfig_audioReview3.includeGithubIssues,
|
|
87
|
+
commitHistoryLimit: (_runConfig_audioReview4 = runConfig.audioReview) === null || _runConfig_audioReview4 === void 0 ? void 0 : _runConfig_audioReview4.commitHistoryLimit,
|
|
88
|
+
diffHistoryLimit: (_runConfig_audioReview5 = runConfig.audioReview) === null || _runConfig_audioReview5 === void 0 ? void 0 : _runConfig_audioReview5.diffHistoryLimit,
|
|
89
|
+
releaseNotesLimit: (_runConfig_audioReview6 = runConfig.audioReview) === null || _runConfig_audioReview6 === void 0 ? void 0 : _runConfig_audioReview6.releaseNotesLimit,
|
|
90
|
+
githubIssuesLimit: (_runConfig_audioReview7 = runConfig.audioReview) === null || _runConfig_audioReview7 === void 0 ? void 0 : _runConfig_audioReview7.githubIssuesLimit,
|
|
91
|
+
sendit: (_runConfig_audioReview8 = runConfig.audioReview) === null || _runConfig_audioReview8 === void 0 ? void 0 : _runConfig_audioReview8.sendit,
|
|
92
|
+
context: (_runConfig_audioReview9 = runConfig.audioReview) === null || _runConfig_audioReview9 === void 0 ? void 0 : _runConfig_audioReview9.context,
|
|
93
|
+
// Use the transcribed audio as content
|
|
94
|
+
note: audioContext.trim() || ((_runConfig_review = runConfig.review) === null || _runConfig_review === void 0 ? void 0 : _runConfig_review.note) || ''
|
|
580
95
|
}
|
|
581
|
-
}
|
|
582
|
-
};
|
|
583
|
-
// Helper function to get user choice interactively
|
|
584
|
-
async function getUserChoice(prompt, choices) {
|
|
585
|
-
const logger = getLogger();
|
|
586
|
-
logger.info(prompt);
|
|
587
|
-
choices.forEach((choice)=>{
|
|
588
|
-
logger.info(` [${choice.key}] ${choice.label}`);
|
|
589
|
-
});
|
|
590
|
-
logger.info('');
|
|
591
|
-
return new Promise((resolve)=>{
|
|
592
|
-
process.stdin.setRawMode(true);
|
|
593
|
-
process.stdin.resume();
|
|
594
|
-
process.stdin.on('data', (key)=>{
|
|
595
|
-
const keyStr = key.toString().toLowerCase();
|
|
596
|
-
const choice = choices.find((c)=>c.key === keyStr);
|
|
597
|
-
if (choice) {
|
|
598
|
-
process.stdin.setRawMode(false);
|
|
599
|
-
process.stdin.pause();
|
|
600
|
-
logger.info(`Selected: ${choice.label}\n`);
|
|
601
|
-
resolve(choice.key);
|
|
602
|
-
}
|
|
603
|
-
});
|
|
604
|
-
});
|
|
605
|
-
}
|
|
606
|
-
// Helper function to edit issue interactively
|
|
607
|
-
async function editIssueInteractively(issue) {
|
|
608
|
-
const logger = getLogger();
|
|
609
|
-
const readline = await import('readline');
|
|
610
|
-
const rl = readline.createInterface({
|
|
611
|
-
input: process.stdin,
|
|
612
|
-
output: process.stdout
|
|
613
96
|
});
|
|
614
|
-
|
|
615
|
-
return new Promise((resolve)=>{
|
|
616
|
-
rl.question(prompt, resolve);
|
|
617
|
-
});
|
|
618
|
-
};
|
|
97
|
+
// Final cleanup to ensure process can exit
|
|
619
98
|
try {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
category: newCategory.trim() || issue.category,
|
|
630
|
-
suggestions: issue.suggestions
|
|
631
|
-
};
|
|
632
|
-
logger.info('ā
Issue updated successfully');
|
|
633
|
-
return updatedIssue;
|
|
634
|
-
} finally{
|
|
635
|
-
rl.close();
|
|
636
|
-
}
|
|
637
|
-
}
|
|
638
|
-
// Helper function to format issue body for GitHub
|
|
639
|
-
function formatIssueBody(issue) {
|
|
640
|
-
let body = `## Description\n\n${issue.description}\n\n`;
|
|
641
|
-
body += `## Details\n\n`;
|
|
642
|
-
body += `- **Priority:** ${issue.priority}\n`;
|
|
643
|
-
body += `- **Category:** ${issue.category}\n`;
|
|
644
|
-
body += `- **Source:** Audio Review\n\n`;
|
|
645
|
-
if (issue.suggestions && issue.suggestions.length > 0) {
|
|
646
|
-
body += `## Suggestions\n\n`;
|
|
647
|
-
issue.suggestions.forEach((suggestion)=>{
|
|
648
|
-
body += `- ${suggestion}\n`;
|
|
649
|
-
});
|
|
650
|
-
body += '\n';
|
|
651
|
-
}
|
|
652
|
-
body += `---\n\n`;
|
|
653
|
-
body += `*This issue was automatically created from an audio review session.*`;
|
|
654
|
-
return body;
|
|
655
|
-
}
|
|
656
|
-
// Helper function to format results with created GitHub issues
|
|
657
|
-
function formatAudioReviewResultsWithIssues(result, createdIssues) {
|
|
658
|
-
let output = `š¤ Audio Review Results\n\n`;
|
|
659
|
-
output += `š Summary: ${result.summary}\n`;
|
|
660
|
-
output += `š Total Issues Found: ${result.totalIssues}\n`;
|
|
661
|
-
output += `š GitHub Issues Created: ${createdIssues.length}\n\n`;
|
|
662
|
-
if (result.issues && result.issues.length > 0) {
|
|
663
|
-
output += `š Issues Identified:\n\n`;
|
|
664
|
-
result.issues.forEach((issue, index)=>{
|
|
665
|
-
const priorityEmoji = issue.priority === 'high' ? 'š“' : issue.priority === 'medium' ? 'š”' : 'š¢';
|
|
666
|
-
const categoryEmoji = issue.category === 'ui' ? 'šØ' : issue.category === 'content' ? 'š' : issue.category === 'functionality' ? 'āļø' : issue.category === 'accessibility' ? 'āæ' : issue.category === 'performance' ? 'ā”' : 'š§';
|
|
667
|
-
output += `${index + 1}. ${priorityEmoji} ${issue.title}\n`;
|
|
668
|
-
output += ` ${categoryEmoji} Category: ${issue.category} | Priority: ${issue.priority}\n`;
|
|
669
|
-
output += ` š Description: ${issue.description}\n`;
|
|
670
|
-
// Check if this issue was created as a GitHub issue
|
|
671
|
-
const createdIssue = createdIssues.find((ci)=>ci.issue === issue);
|
|
672
|
-
if (createdIssue) {
|
|
673
|
-
output += ` š GitHub Issue: #${createdIssue.number} - ${createdIssue.githubUrl}\n`;
|
|
674
|
-
}
|
|
675
|
-
if (issue.suggestions && issue.suggestions.length > 0) {
|
|
676
|
-
output += ` š” Suggestions:\n`;
|
|
677
|
-
issue.suggestions.forEach((suggestion)=>{
|
|
678
|
-
output += ` ⢠${suggestion}\n`;
|
|
679
|
-
});
|
|
680
|
-
}
|
|
681
|
-
output += `\n`;
|
|
682
|
-
});
|
|
683
|
-
} else {
|
|
684
|
-
output += `ā
No specific issues identified from the audio review.\n\n`;
|
|
685
|
-
}
|
|
686
|
-
if (createdIssues.length > 0) {
|
|
687
|
-
output += `\nšÆ Created GitHub Issues:\n`;
|
|
688
|
-
createdIssues.forEach((createdIssue)=>{
|
|
689
|
-
output += `⢠#${createdIssue.number}: ${createdIssue.issue.title} - ${createdIssue.githubUrl}\n`;
|
|
690
|
-
});
|
|
691
|
-
output += `\n`;
|
|
692
|
-
}
|
|
693
|
-
output += `š Next Steps: Review the created GitHub issues and prioritize them in your development workflow.`;
|
|
694
|
-
return output;
|
|
695
|
-
}
|
|
696
|
-
function formatAudioReviewResults(result) {
|
|
697
|
-
let output = `š¤ Audio Review Results\n\n`;
|
|
698
|
-
output += `š Summary: ${result.summary}\n`;
|
|
699
|
-
output += `š Total Issues Found: ${result.totalIssues}\n\n`;
|
|
700
|
-
if (result.issues && result.issues.length > 0) {
|
|
701
|
-
output += `š Issues Identified:\n\n`;
|
|
702
|
-
result.issues.forEach((issue, index)=>{
|
|
703
|
-
const priorityEmoji = issue.priority === 'high' ? 'š“' : issue.priority === 'medium' ? 'š”' : 'š¢';
|
|
704
|
-
const categoryEmoji = issue.category === 'ui' ? 'šØ' : issue.category === 'content' ? 'š' : issue.category === 'functionality' ? 'āļø' : issue.category === 'accessibility' ? 'āæ' : issue.category === 'performance' ? 'ā”' : 'š§';
|
|
705
|
-
output += `${index + 1}. ${priorityEmoji} ${issue.title}\n`;
|
|
706
|
-
output += ` ${categoryEmoji} Category: ${issue.category} | Priority: ${issue.priority}\n`;
|
|
707
|
-
output += ` š Description: ${issue.description}\n`;
|
|
708
|
-
if (issue.suggestions && issue.suggestions.length > 0) {
|
|
709
|
-
output += ` š” Suggestions:\n`;
|
|
710
|
-
issue.suggestions.forEach((suggestion)=>{
|
|
711
|
-
output += ` ⢠${suggestion}\n`;
|
|
712
|
-
});
|
|
713
|
-
}
|
|
714
|
-
output += `\n`;
|
|
715
|
-
});
|
|
716
|
-
} else {
|
|
717
|
-
output += `ā
No specific issues identified from the audio review.\n\n`;
|
|
99
|
+
if (process.stdin.setRawMode) {
|
|
100
|
+
process.stdin.setRawMode(false);
|
|
101
|
+
process.stdin.pause();
|
|
102
|
+
process.stdin.removeAllListeners();
|
|
103
|
+
}
|
|
104
|
+
process.removeAllListeners('SIGINT');
|
|
105
|
+
process.removeAllListeners('SIGTERM');
|
|
106
|
+
} catch (error) {
|
|
107
|
+
// Ignore cleanup errors
|
|
718
108
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
}
|
|
109
|
+
return result;
|
|
110
|
+
};
|
|
722
111
|
|
|
723
112
|
export { execute };
|
|
724
113
|
//# sourceMappingURL=audio-review.js.map
|