@grunnverk/kodrdriv 1.3.0 → 1.5.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.
- package/dist/constants.js +3 -3
- package/dist/mcp-server.js +6 -6
- package/dist/mcp-server.js.map +2 -2
- package/package.json +13 -13
- package/.claude/settings.local.json +0 -12
- package/.gitignore~ +0 -23
- package/input/250509-kodrdriv-library-rules.m4a +0 -0
- package/processed/250705-kodrdriv-confirm-editor-for-commit-and-release.m4a +0 -0
- package/processed/250705-kodrdriv-confirm-flag-release.m4a +0 -0
- package/processed/250705-kodrdriv-context-for-review.m4a +0 -0
- package/processed/250705-kodrdriv-feedback-on-publish-pipeline.m4a +0 -0
- package/processed/250705-kodrdriv-intelligent-eslint-style.m4a +0 -0
- package/processed/250705-kodrdriv-make-review-less-strict.m4a +0 -0
- package/processed/250705-kodrdriv-multilevel-transcription.m4a +0 -0
- package/processed/250705-kodrdriv-opinionated-review.m4a +0 -0
- package/processed/250705-kodrdriv-publish-next-version.m4a +0 -0
- package/processed/250705-kodrdriv-release-branches-and-milestones.m4a +0 -0
- package/processed/250705-kodrdriv-scope-check-fix-or-ignore.m4a +0 -0
- package/processed/250705-kodrdriv-scope-checker.m4a +0 -0
- package/processed/250705-kodrdriv-specify-a-release-note-for-publish.m4a +0 -0
- package/temp-dist/arguments.js +0 -817
- package/temp-dist/constants.js +0 -202
- package/temp-dist/logging.js +0 -130
- package/temp-dist/types.js +0 -112
- package/temp-dist/util/stdin.js +0 -132
- package/temp-dist/util/storage.js +0 -149
- package/temp-dist/util/validation.js +0 -110
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
// eslint-disable-next-line no-restricted-imports
|
|
2
|
-
import * as fs from 'fs';
|
|
3
|
-
import { glob } from 'glob';
|
|
4
|
-
import path from 'path';
|
|
5
|
-
import crypto from 'crypto';
|
|
6
|
-
export const create = (params) => {
|
|
7
|
-
// eslint-disable-next-line no-console
|
|
8
|
-
const log = params.log || console.log;
|
|
9
|
-
const exists = async (path) => {
|
|
10
|
-
try {
|
|
11
|
-
await fs.promises.stat(path);
|
|
12
|
-
return true;
|
|
13
|
-
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
14
|
-
}
|
|
15
|
-
catch (error) {
|
|
16
|
-
return false;
|
|
17
|
-
}
|
|
18
|
-
};
|
|
19
|
-
const isDirectory = async (path) => {
|
|
20
|
-
const stats = await fs.promises.stat(path);
|
|
21
|
-
if (!stats.isDirectory()) {
|
|
22
|
-
// Log at debug level since this is expected when scanning directories
|
|
23
|
-
// that contain both files and directories
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
return true;
|
|
27
|
-
};
|
|
28
|
-
const isFile = async (path) => {
|
|
29
|
-
const stats = await fs.promises.stat(path);
|
|
30
|
-
if (!stats.isFile()) {
|
|
31
|
-
// Log removed since this is expected when checking file types
|
|
32
|
-
return false;
|
|
33
|
-
}
|
|
34
|
-
return true;
|
|
35
|
-
};
|
|
36
|
-
const isReadable = async (path) => {
|
|
37
|
-
try {
|
|
38
|
-
await fs.promises.access(path, fs.constants.R_OK);
|
|
39
|
-
}
|
|
40
|
-
catch (error) {
|
|
41
|
-
log(`${path} is not readable: %s %s`, error.message, error.stack);
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
return true;
|
|
45
|
-
};
|
|
46
|
-
const isWritable = async (path) => {
|
|
47
|
-
try {
|
|
48
|
-
await fs.promises.access(path, fs.constants.W_OK);
|
|
49
|
-
}
|
|
50
|
-
catch (error) {
|
|
51
|
-
log(`${path} is not writable: %s %s`, error.message, error.stack);
|
|
52
|
-
return false;
|
|
53
|
-
}
|
|
54
|
-
return true;
|
|
55
|
-
};
|
|
56
|
-
const isFileReadable = async (path) => {
|
|
57
|
-
return await exists(path) && await isFile(path) && await isReadable(path);
|
|
58
|
-
};
|
|
59
|
-
const isDirectoryWritable = async (path) => {
|
|
60
|
-
return await exists(path) && await isDirectory(path) && await isWritable(path);
|
|
61
|
-
};
|
|
62
|
-
const isDirectoryReadable = async (path) => {
|
|
63
|
-
return await exists(path) && await isDirectory(path) && await isReadable(path);
|
|
64
|
-
};
|
|
65
|
-
const createDirectory = async (path) => {
|
|
66
|
-
try {
|
|
67
|
-
await fs.promises.mkdir(path, { recursive: true });
|
|
68
|
-
}
|
|
69
|
-
catch (mkdirError) {
|
|
70
|
-
throw new Error(`Failed to create output directory ${path}: ${mkdirError.message} ${mkdirError.stack}`);
|
|
71
|
-
}
|
|
72
|
-
};
|
|
73
|
-
const ensureDirectory = async (path) => {
|
|
74
|
-
if (!(await exists(path))) {
|
|
75
|
-
await createDirectory(path);
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
const removeDirectory = async (path) => {
|
|
79
|
-
try {
|
|
80
|
-
if (await exists(path)) {
|
|
81
|
-
await fs.promises.rm(path, { recursive: true, force: true });
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
catch (rmError) {
|
|
85
|
-
throw new Error(`Failed to remove directory ${path}: ${rmError.message} ${rmError.stack}`);
|
|
86
|
-
}
|
|
87
|
-
};
|
|
88
|
-
const readFile = async (path, encoding) => {
|
|
89
|
-
return await fs.promises.readFile(path, { encoding: encoding });
|
|
90
|
-
};
|
|
91
|
-
const writeFile = async (path, data, encoding) => {
|
|
92
|
-
await fs.promises.writeFile(path, data, { encoding: encoding });
|
|
93
|
-
};
|
|
94
|
-
const rename = async (oldPath, newPath) => {
|
|
95
|
-
await fs.promises.rename(oldPath, newPath);
|
|
96
|
-
};
|
|
97
|
-
const deleteFile = async (path) => {
|
|
98
|
-
try {
|
|
99
|
-
if (await exists(path)) {
|
|
100
|
-
await fs.promises.unlink(path);
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
|
-
catch (deleteError) {
|
|
104
|
-
throw new Error(`Failed to delete file ${path}: ${deleteError.message} ${deleteError.stack}`);
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
const forEachFileIn = async (directory, callback, options = { pattern: '*.*' }) => {
|
|
108
|
-
try {
|
|
109
|
-
const files = await glob(options.pattern, { cwd: directory, nodir: true });
|
|
110
|
-
for (const file of files) {
|
|
111
|
-
await callback(path.join(directory, file));
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
catch (err) {
|
|
115
|
-
throw new Error(`Failed to glob pattern ${options.pattern} in ${directory}: ${err.message}`);
|
|
116
|
-
}
|
|
117
|
-
};
|
|
118
|
-
const readStream = async (path) => {
|
|
119
|
-
return fs.createReadStream(path);
|
|
120
|
-
};
|
|
121
|
-
const hashFile = async (path, length) => {
|
|
122
|
-
const file = await readFile(path, 'utf8');
|
|
123
|
-
return crypto.createHash('sha256').update(file).digest('hex').slice(0, length);
|
|
124
|
-
};
|
|
125
|
-
const listFiles = async (directory) => {
|
|
126
|
-
return await fs.promises.readdir(directory);
|
|
127
|
-
};
|
|
128
|
-
return {
|
|
129
|
-
exists,
|
|
130
|
-
isDirectory,
|
|
131
|
-
isFile,
|
|
132
|
-
isReadable,
|
|
133
|
-
isWritable,
|
|
134
|
-
isFileReadable,
|
|
135
|
-
isDirectoryWritable,
|
|
136
|
-
isDirectoryReadable,
|
|
137
|
-
createDirectory,
|
|
138
|
-
ensureDirectory,
|
|
139
|
-
readFile,
|
|
140
|
-
readStream,
|
|
141
|
-
writeFile,
|
|
142
|
-
rename,
|
|
143
|
-
deleteFile,
|
|
144
|
-
forEachFileIn,
|
|
145
|
-
hashFile,
|
|
146
|
-
listFiles,
|
|
147
|
-
removeDirectory,
|
|
148
|
-
};
|
|
149
|
-
};
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Runtime validation utilities for safe type handling
|
|
3
|
-
*/
|
|
4
|
-
/**
|
|
5
|
-
* Validates and safely casts data to ReleaseSummary type
|
|
6
|
-
*/
|
|
7
|
-
export const validateReleaseSummary = (data) => {
|
|
8
|
-
if (!data || typeof data !== 'object') {
|
|
9
|
-
throw new Error('Invalid release summary: not an object');
|
|
10
|
-
}
|
|
11
|
-
if (typeof data.title !== 'string') {
|
|
12
|
-
throw new Error('Invalid release summary: title must be a string');
|
|
13
|
-
}
|
|
14
|
-
if (typeof data.body !== 'string') {
|
|
15
|
-
throw new Error('Invalid release summary: body must be a string');
|
|
16
|
-
}
|
|
17
|
-
return data;
|
|
18
|
-
};
|
|
19
|
-
/**
|
|
20
|
-
* Validates and safely casts data to LinkBackup type
|
|
21
|
-
*/
|
|
22
|
-
export const validateLinkBackup = (data) => {
|
|
23
|
-
if (!data || typeof data !== 'object') {
|
|
24
|
-
throw new Error('Invalid link backup: not an object');
|
|
25
|
-
}
|
|
26
|
-
// Validate each backup entry
|
|
27
|
-
for (const [key, value] of Object.entries(data)) {
|
|
28
|
-
if (!value || typeof value !== 'object') {
|
|
29
|
-
throw new Error(`Invalid link backup entry for ${key}: not an object`);
|
|
30
|
-
}
|
|
31
|
-
const entry = value;
|
|
32
|
-
if (typeof entry.originalVersion !== 'string') {
|
|
33
|
-
throw new Error(`Invalid link backup entry for ${key}: originalVersion must be a string`);
|
|
34
|
-
}
|
|
35
|
-
if (typeof entry.dependencyType !== 'string') {
|
|
36
|
-
throw new Error(`Invalid link backup entry for ${key}: dependencyType must be a string`);
|
|
37
|
-
}
|
|
38
|
-
if (typeof entry.relativePath !== 'string') {
|
|
39
|
-
throw new Error(`Invalid link backup entry for ${key}: relativePath must be a string`);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return data;
|
|
43
|
-
};
|
|
44
|
-
/**
|
|
45
|
-
* Validates transcription result has required text property
|
|
46
|
-
*/
|
|
47
|
-
export const validateTranscriptionResult = (data) => {
|
|
48
|
-
if (!data || typeof data !== 'object') {
|
|
49
|
-
throw new Error('Invalid transcription result: not an object');
|
|
50
|
-
}
|
|
51
|
-
if (typeof data.text !== 'string') {
|
|
52
|
-
throw new Error('Invalid transcription result: text property must be a string');
|
|
53
|
-
}
|
|
54
|
-
return data;
|
|
55
|
-
};
|
|
56
|
-
/**
|
|
57
|
-
* Safely parses JSON with error handling
|
|
58
|
-
*/
|
|
59
|
-
export const safeJsonParse = (jsonString, context) => {
|
|
60
|
-
try {
|
|
61
|
-
const parsed = JSON.parse(jsonString);
|
|
62
|
-
if (parsed === null || parsed === undefined) {
|
|
63
|
-
throw new Error('Parsed JSON is null or undefined');
|
|
64
|
-
}
|
|
65
|
-
return parsed;
|
|
66
|
-
}
|
|
67
|
-
catch (error) {
|
|
68
|
-
const contextStr = context ? ` (${context})` : '';
|
|
69
|
-
throw new Error(`Failed to parse JSON${contextStr}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
70
|
-
}
|
|
71
|
-
};
|
|
72
|
-
/**
|
|
73
|
-
* Validates that a value is a non-empty string
|
|
74
|
-
*/
|
|
75
|
-
export const validateString = (value, fieldName) => {
|
|
76
|
-
if (typeof value !== 'string') {
|
|
77
|
-
throw new Error(`${fieldName} must be a string, got ${typeof value}`);
|
|
78
|
-
}
|
|
79
|
-
if (value.trim() === '') {
|
|
80
|
-
throw new Error(`${fieldName} cannot be empty`);
|
|
81
|
-
}
|
|
82
|
-
return value;
|
|
83
|
-
};
|
|
84
|
-
/**
|
|
85
|
-
* Validates that a value exists and has a specific property
|
|
86
|
-
*/
|
|
87
|
-
export const validateHasProperty = (obj, property, context) => {
|
|
88
|
-
if (!obj || typeof obj !== 'object') {
|
|
89
|
-
const contextStr = context ? ` in ${context}` : '';
|
|
90
|
-
throw new Error(`Object is null or not an object${contextStr}`);
|
|
91
|
-
}
|
|
92
|
-
if (!(property in obj)) {
|
|
93
|
-
const contextStr = context ? ` in ${context}` : '';
|
|
94
|
-
throw new Error(`Missing required property '${property}'${contextStr}`);
|
|
95
|
-
}
|
|
96
|
-
};
|
|
97
|
-
/**
|
|
98
|
-
* Validates package.json structure has basic required fields
|
|
99
|
-
*/
|
|
100
|
-
export const validatePackageJson = (data, context, requireName = true) => {
|
|
101
|
-
if (!data || typeof data !== 'object') {
|
|
102
|
-
const contextStr = context ? ` (${context})` : '';
|
|
103
|
-
throw new Error(`Invalid package.json${contextStr}: not an object`);
|
|
104
|
-
}
|
|
105
|
-
if (requireName && typeof data.name !== 'string') {
|
|
106
|
-
const contextStr = context ? ` (${context})` : '';
|
|
107
|
-
throw new Error(`Invalid package.json${contextStr}: name must be a string`);
|
|
108
|
-
}
|
|
109
|
-
return data;
|
|
110
|
-
};
|