@eldrforge/kodrdriv 1.2.133 → 1.2.135

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 (96) hide show
  1. package/.cursor/rules/no-local-dependencies.md +6 -0
  2. package/AI-GUIDE.md +4 -1
  3. package/LICENSE +1 -1
  4. package/dist/application.js +34 -42
  5. package/dist/application.js.map +1 -1
  6. package/dist/arguments.js +3 -3
  7. package/dist/arguments.js.map +1 -1
  8. package/dist/constants.js +5 -7
  9. package/dist/constants.js.map +1 -1
  10. package/dist/logging.js +4 -32
  11. package/dist/logging.js.map +1 -1
  12. package/dist/types.js +283 -0
  13. package/dist/types.js.map +1 -0
  14. package/guide/ai-system.md +3 -0
  15. package/guide/architecture.md +3 -0
  16. package/guide/commands.md +3 -0
  17. package/guide/configuration.md +3 -0
  18. package/guide/debugging.md +4 -1
  19. package/guide/development.md +3 -0
  20. package/guide/index.md +4 -1
  21. package/guide/integration.md +3 -0
  22. package/guide/monorepo.md +3 -0
  23. package/guide/quickstart.md +3 -0
  24. package/guide/testing.md +3 -0
  25. package/guide/tree-operations.md +3 -0
  26. package/guide/usage.md +3 -0
  27. package/package.json +15 -10
  28. package/DUPLICATION-CLEANUP.md +0 -104
  29. package/agentic-reflection-commit-2025-12-27T22-56-06-143Z.md +0 -50
  30. package/agentic-reflection-commit-2025-12-27T23-01-57-294Z.md +0 -50
  31. package/agentic-reflection-commit-2025-12-27T23-11-57-811Z.md +0 -50
  32. package/agentic-reflection-commit-2025-12-27T23-12-50-645Z.md +0 -50
  33. package/agentic-reflection-commit-2025-12-27T23-13-59-347Z.md +0 -52
  34. package/agentic-reflection-commit-2025-12-27T23-14-36-001Z.md +0 -50
  35. package/agentic-reflection-commit-2025-12-27T23-18-59-832Z.md +0 -50
  36. package/agentic-reflection-commit-2025-12-27T23-25-20-667Z.md +0 -62
  37. package/dist/commands/audio-commit.js +0 -152
  38. package/dist/commands/audio-commit.js.map +0 -1
  39. package/dist/commands/audio-review.js +0 -274
  40. package/dist/commands/audio-review.js.map +0 -1
  41. package/dist/commands/clean.js +0 -49
  42. package/dist/commands/clean.js.map +0 -1
  43. package/dist/commands/commit.js +0 -680
  44. package/dist/commands/commit.js.map +0 -1
  45. package/dist/commands/development.js +0 -467
  46. package/dist/commands/development.js.map +0 -1
  47. package/dist/commands/link.js +0 -646
  48. package/dist/commands/link.js.map +0 -1
  49. package/dist/commands/precommit.js +0 -99
  50. package/dist/commands/precommit.js.map +0 -1
  51. package/dist/commands/publish.js +0 -1432
  52. package/dist/commands/publish.js.map +0 -1
  53. package/dist/commands/release.js +0 -376
  54. package/dist/commands/release.js.map +0 -1
  55. package/dist/commands/review.js +0 -733
  56. package/dist/commands/review.js.map +0 -1
  57. package/dist/commands/select-audio.js +0 -46
  58. package/dist/commands/select-audio.js.map +0 -1
  59. package/dist/commands/tree.js +0 -2363
  60. package/dist/commands/tree.js.map +0 -1
  61. package/dist/commands/unlink.js +0 -537
  62. package/dist/commands/unlink.js.map +0 -1
  63. package/dist/commands/updates.js +0 -211
  64. package/dist/commands/updates.js.map +0 -1
  65. package/dist/commands/versions.js +0 -221
  66. package/dist/commands/versions.js.map +0 -1
  67. package/dist/content/diff.js +0 -346
  68. package/dist/content/diff.js.map +0 -1
  69. package/dist/content/files.js +0 -190
  70. package/dist/content/files.js.map +0 -1
  71. package/dist/content/log.js +0 -72
  72. package/dist/content/log.js.map +0 -1
  73. package/dist/util/aiAdapter.js +0 -28
  74. package/dist/util/aiAdapter.js.map +0 -1
  75. package/dist/util/fileLock.js +0 -241
  76. package/dist/util/fileLock.js.map +0 -1
  77. package/dist/util/general.js +0 -379
  78. package/dist/util/general.js.map +0 -1
  79. package/dist/util/gitMutex.js +0 -161
  80. package/dist/util/gitMutex.js.map +0 -1
  81. package/dist/util/interactive.js +0 -32
  82. package/dist/util/interactive.js.map +0 -1
  83. package/dist/util/loggerAdapter.js +0 -41
  84. package/dist/util/loggerAdapter.js.map +0 -1
  85. package/dist/util/performance.js +0 -134
  86. package/dist/util/performance.js.map +0 -1
  87. package/dist/util/precommitOptimizations.js +0 -310
  88. package/dist/util/precommitOptimizations.js.map +0 -1
  89. package/dist/util/stopContext.js +0 -146
  90. package/dist/util/stopContext.js.map +0 -1
  91. package/dist/util/storageAdapter.js +0 -31
  92. package/dist/util/storageAdapter.js.map +0 -1
  93. package/dist/util/validation.js +0 -45
  94. package/dist/util/validation.js.map +0 -1
  95. package/dist/utils/branchState.js +0 -700
  96. package/dist/utils/branchState.js.map +0 -1
@@ -1,241 +0,0 @@
1
- import * as fs from 'fs';
2
- import * as path from 'path';
3
- import * as os from 'os';
4
- import { getLogger } from '../logging.js';
5
-
6
- // eslint-disable-next-line no-restricted-imports
7
- function _define_property(obj, key, value) {
8
- if (key in obj) {
9
- Object.defineProperty(obj, key, {
10
- value: value,
11
- enumerable: true,
12
- configurable: true,
13
- writable: true
14
- });
15
- } else {
16
- obj[key] = value;
17
- }
18
- return obj;
19
- }
20
- /**
21
- * File-based lock for cross-process synchronization
22
- * Uses atomic file operations to coordinate across multiple Node processes
23
- */ class FileLock {
24
- /**
25
- * Acquire the file lock with exponential backoff retry
26
- */ async lock() {
27
- let attempts = 0;
28
- let currentDelay = this.retryDelay;
29
- while(attempts < this.maxRetries){
30
- try {
31
- // Try to create lock file atomically with 'wx' flag (fails if exists)
32
- const lockData = {
33
- pid: process.pid,
34
- timestamp: Date.now(),
35
- hostname: os.hostname()
36
- };
37
- // Check if lock file exists and is stale
38
- if (fs.existsSync(this.lockPath)) {
39
- const lockContent = fs.readFileSync(this.lockPath, 'utf-8');
40
- try {
41
- const existingLock = JSON.parse(lockContent);
42
- const lockAge = Date.now() - existingLock.timestamp;
43
- // If lock is stale, try to remove it
44
- if (lockAge > this.lockTimeout) {
45
- this.logger.debug(`Removing stale lock file (age: ${lockAge}ms, pid: ${existingLock.pid})`);
46
- try {
47
- fs.unlinkSync(this.lockPath);
48
- } catch {
49
- // Lock might have been removed by another process, continue
50
- }
51
- }
52
- } catch {
53
- // Invalid lock file, try to remove it
54
- try {
55
- fs.unlinkSync(this.lockPath);
56
- } catch {
57
- // Ignore errors
58
- }
59
- }
60
- }
61
- // Try to acquire lock
62
- fs.writeFileSync(this.lockPath, JSON.stringify(lockData, null, 2), {
63
- flag: 'wx'
64
- });
65
- this.lockAcquired = true;
66
- if (attempts > 0) {
67
- this.logger.debug(`Acquired file lock after ${attempts} attempts: ${this.lockPath}`);
68
- }
69
- return;
70
- } catch (error) {
71
- if (error.code === 'EEXIST') {
72
- // Lock file exists, retry with backoff
73
- attempts++;
74
- if (attempts === 1 || attempts % 10 === 0) {
75
- this.logger.verbose(`Waiting for file lock (attempt ${attempts}/${this.maxRetries}): ${this.lockPath}`);
76
- }
77
- await new Promise((resolve)=>setTimeout(resolve, currentDelay));
78
- // Exponential backoff
79
- currentDelay = Math.min(currentDelay * 1.5, this.maxRetryDelay);
80
- } else {
81
- // Unexpected error
82
- throw new Error(`Failed to acquire file lock ${this.lockPath}: ${error.message}`);
83
- }
84
- }
85
- }
86
- throw new Error(`Failed to acquire file lock after ${this.maxRetries} attempts: ${this.lockPath}`);
87
- }
88
- /**
89
- * Release the file lock
90
- */ unlock() {
91
- if (!this.lockAcquired) {
92
- return;
93
- }
94
- try {
95
- if (fs.existsSync(this.lockPath)) {
96
- fs.unlinkSync(this.lockPath);
97
- }
98
- this.lockAcquired = false;
99
- this.logger.silly(`Released file lock: ${this.lockPath}`);
100
- } catch (error) {
101
- // Lock file might have been removed by another process or stale lock cleanup
102
- this.logger.debug(`Error releasing file lock ${this.lockPath}: ${error.message}`);
103
- this.lockAcquired = false;
104
- }
105
- }
106
- /**
107
- * Check if this instance currently holds the lock
108
- */ isLocked() {
109
- return this.lockAcquired;
110
- }
111
- constructor(lockPath){
112
- _define_property(this, "lockPath", void 0);
113
- _define_property(this, "lockAcquired", false);
114
- _define_property(this, "maxRetries", 100); // Maximum number of lock attempts
115
- _define_property(this, "retryDelay", 100); // Initial retry delay in ms
116
- _define_property(this, "maxRetryDelay", 2000); // Maximum retry delay in ms
117
- _define_property(this, "lockTimeout", 30000); // Consider lock stale after 30 seconds
118
- _define_property(this, "logger", getLogger());
119
- this.lockPath = lockPath;
120
- }
121
- }
122
- /**
123
- * Manages file-based locks for git repositories (cross-process safe)
124
- */ class RepositoryFileLockManager {
125
- /**
126
- * Get or create a file lock for a specific git repository
127
- * @param repoPath Path to the git repository root
128
- * @returns FileLock for this repository
129
- */ getRepositoryLock(repoPath) {
130
- const normalizedPath = path.resolve(repoPath);
131
- if (!this.locks.has(normalizedPath)) {
132
- // Resolve the actual .git directory (handles both regular repos and submodules)
133
- const gitDirPath = this.resolveGitDirectory(normalizedPath);
134
- const lockPath = path.join(gitDirPath, 'kodrdriv.lock');
135
- this.logger.debug(`Creating file lock for repository: ${normalizedPath} at ${lockPath}`);
136
- this.locks.set(normalizedPath, new FileLock(lockPath));
137
- // Register cleanup handler on first lock creation
138
- if (!this.cleanupRegistered) {
139
- this.registerCleanupHandlers();
140
- this.cleanupRegistered = true;
141
- }
142
- }
143
- return this.locks.get(normalizedPath);
144
- }
145
- /**
146
- * Resolve the actual .git directory path, handling both regular repos and submodules
147
- * @param repoPath Path to the repository root
148
- * @returns Path to the actual .git directory
149
- */ resolveGitDirectory(repoPath) {
150
- const gitPath = path.join(repoPath, '.git');
151
- try {
152
- const stat = fs.statSync(gitPath);
153
- if (stat.isDirectory()) {
154
- // Regular git repository
155
- return gitPath;
156
- } else if (stat.isFile()) {
157
- // Git submodule - .git is a file with format: gitdir: <path>
158
- const gitFileContent = fs.readFileSync(gitPath, 'utf-8').trim();
159
- const match = gitFileContent.match(/^gitdir:\s*(.+)$/);
160
- if (match && match[1]) {
161
- // Resolve the gitdir path (it's relative to the repo path)
162
- const gitDirPath = path.resolve(repoPath, match[1]);
163
- this.logger.debug(`Resolved submodule gitdir: ${gitDirPath}`);
164
- // Ensure the git directory exists
165
- if (!fs.existsSync(gitDirPath)) {
166
- throw new Error(`Submodule git directory does not exist: ${gitDirPath}`);
167
- }
168
- return gitDirPath;
169
- }
170
- throw new Error(`Invalid .git file format in ${gitPath}: ${gitFileContent}`);
171
- }
172
- } catch (error) {
173
- // Check if error is from statSync (file doesn't exist)
174
- if (error.code === 'ENOENT') {
175
- throw new Error(`No .git directory or file found in ${repoPath}`);
176
- }
177
- throw new Error(`Failed to resolve git directory for ${repoPath}: ${error.message}`);
178
- }
179
- throw new Error(`No .git directory or file found in ${repoPath}`);
180
- }
181
- /**
182
- * Register cleanup handlers to release locks on process exit
183
- */ registerCleanupHandlers() {
184
- const cleanup = ()=>{
185
- this.destroy();
186
- };
187
- // Handle various exit scenarios
188
- process.on('exit', cleanup);
189
- process.on('SIGINT', ()=>{
190
- cleanup();
191
- process.exit(130); // Standard exit code for SIGINT
192
- });
193
- process.on('SIGTERM', ()=>{
194
- cleanup();
195
- process.exit(143); // Standard exit code for SIGTERM
196
- });
197
- process.on('uncaughtException', (error)=>{
198
- this.logger.error('FILELOCK_UNCAUGHT_EXCEPTION: Uncaught exception detected, cleaning up locks | Error: ' + error + ' | Action: Release all locks');
199
- cleanup();
200
- process.exit(1);
201
- });
202
- }
203
- /**
204
- * Execute a git operation with repository-level file locking
205
- * @param repoPath Path to the git repository root
206
- * @param operation The async operation to execute under lock
207
- * @param operationName Optional name for logging
208
- * @returns Result of the operation
209
- */ async withGitLock(repoPath, operation, operationName) {
210
- const lock = this.getRepositoryLock(repoPath);
211
- const startWait = Date.now();
212
- this.logger.silly(`Acquiring file lock for ${repoPath}${operationName ? ` for: ${operationName}` : ''}`);
213
- await lock.lock();
214
- const waitTime = Date.now() - startWait;
215
- if (waitTime > 100) {
216
- this.logger.debug(`Acquired file lock for ${repoPath} after ${waitTime}ms${operationName ? ` for: ${operationName}` : ''}`);
217
- }
218
- try {
219
- return await operation();
220
- } finally{
221
- lock.unlock();
222
- }
223
- }
224
- /**
225
- * Clean up all locks
226
- */ destroy() {
227
- this.logger.debug(`Cleaning up ${this.locks.size} file lock(s)`);
228
- for (const lock of this.locks.values()){
229
- lock.unlock();
230
- }
231
- this.locks.clear();
232
- }
233
- constructor(){
234
- _define_property(this, "locks", new Map());
235
- _define_property(this, "logger", getLogger());
236
- _define_property(this, "cleanupRegistered", false);
237
- }
238
- }
239
-
240
- export { FileLock, RepositoryFileLockManager };
241
- //# sourceMappingURL=fileLock.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"fileLock.js","sources":["../../src/util/fileLock.ts"],"sourcesContent":["// eslint-disable-next-line no-restricted-imports\nimport * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport { getLogger } from '../logging';\n\n/**\n * File-based lock for cross-process synchronization\n * Uses atomic file operations to coordinate across multiple Node processes\n */\nexport class FileLock {\n private lockPath: string;\n private lockAcquired = false;\n private maxRetries = 100; // Maximum number of lock attempts\n private retryDelay = 100; // Initial retry delay in ms\n private maxRetryDelay = 2000; // Maximum retry delay in ms\n private lockTimeout = 30000; // Consider lock stale after 30 seconds\n private logger = getLogger();\n\n constructor(lockPath: string) {\n this.lockPath = lockPath;\n }\n\n /**\n * Acquire the file lock with exponential backoff retry\n */\n async lock(): Promise<void> {\n let attempts = 0;\n let currentDelay = this.retryDelay;\n\n while (attempts < this.maxRetries) {\n try {\n // Try to create lock file atomically with 'wx' flag (fails if exists)\n const lockData = {\n pid: process.pid,\n timestamp: Date.now(),\n hostname: os.hostname()\n };\n\n // Check if lock file exists and is stale\n if (fs.existsSync(this.lockPath)) {\n const lockContent = fs.readFileSync(this.lockPath, 'utf-8');\n try {\n const existingLock = JSON.parse(lockContent);\n const lockAge = Date.now() - existingLock.timestamp;\n\n // If lock is stale, try to remove it\n if (lockAge > this.lockTimeout) {\n this.logger.debug(`Removing stale lock file (age: ${lockAge}ms, pid: ${existingLock.pid})`);\n try {\n fs.unlinkSync(this.lockPath);\n } catch {\n // Lock might have been removed by another process, continue\n }\n }\n } catch {\n // Invalid lock file, try to remove it\n try {\n fs.unlinkSync(this.lockPath);\n } catch {\n // Ignore errors\n }\n }\n }\n\n // Try to acquire lock\n fs.writeFileSync(this.lockPath, JSON.stringify(lockData, null, 2), { flag: 'wx' });\n this.lockAcquired = true;\n\n if (attempts > 0) {\n this.logger.debug(`Acquired file lock after ${attempts} attempts: ${this.lockPath}`);\n }\n\n return;\n } catch (error: any) {\n if (error.code === 'EEXIST') {\n // Lock file exists, retry with backoff\n attempts++;\n\n if (attempts === 1 || attempts % 10 === 0) {\n this.logger.verbose(`Waiting for file lock (attempt ${attempts}/${this.maxRetries}): ${this.lockPath}`);\n }\n\n await new Promise(resolve => setTimeout(resolve, currentDelay));\n\n // Exponential backoff\n currentDelay = Math.min(currentDelay * 1.5, this.maxRetryDelay);\n } else {\n // Unexpected error\n throw new Error(`Failed to acquire file lock ${this.lockPath}: ${error.message}`);\n }\n }\n }\n\n throw new Error(`Failed to acquire file lock after ${this.maxRetries} attempts: ${this.lockPath}`);\n }\n\n /**\n * Release the file lock\n */\n unlock(): void {\n if (!this.lockAcquired) {\n return;\n }\n\n try {\n if (fs.existsSync(this.lockPath)) {\n fs.unlinkSync(this.lockPath);\n }\n this.lockAcquired = false;\n this.logger.silly(`Released file lock: ${this.lockPath}`);\n } catch (error: any) {\n // Lock file might have been removed by another process or stale lock cleanup\n this.logger.debug(`Error releasing file lock ${this.lockPath}: ${error.message}`);\n this.lockAcquired = false;\n }\n }\n\n /**\n * Check if this instance currently holds the lock\n */\n isLocked(): boolean {\n return this.lockAcquired;\n }\n}\n\n/**\n * Manages file-based locks for git repositories (cross-process safe)\n */\nexport class RepositoryFileLockManager {\n private locks: Map<string, FileLock> = new Map();\n private logger = getLogger();\n private cleanupRegistered = false;\n\n /**\n * Get or create a file lock for a specific git repository\n * @param repoPath Path to the git repository root\n * @returns FileLock for this repository\n */\n getRepositoryLock(repoPath: string): FileLock {\n const normalizedPath = path.resolve(repoPath);\n\n if (!this.locks.has(normalizedPath)) {\n // Resolve the actual .git directory (handles both regular repos and submodules)\n const gitDirPath = this.resolveGitDirectory(normalizedPath);\n const lockPath = path.join(gitDirPath, 'kodrdriv.lock');\n this.logger.debug(`Creating file lock for repository: ${normalizedPath} at ${lockPath}`);\n this.locks.set(normalizedPath, new FileLock(lockPath));\n\n // Register cleanup handler on first lock creation\n if (!this.cleanupRegistered) {\n this.registerCleanupHandlers();\n this.cleanupRegistered = true;\n }\n }\n\n return this.locks.get(normalizedPath)!;\n }\n\n /**\n * Resolve the actual .git directory path, handling both regular repos and submodules\n * @param repoPath Path to the repository root\n * @returns Path to the actual .git directory\n */\n private resolveGitDirectory(repoPath: string): string {\n const gitPath = path.join(repoPath, '.git');\n\n try {\n const stat = fs.statSync(gitPath);\n\n if (stat.isDirectory()) {\n // Regular git repository\n return gitPath;\n } else if (stat.isFile()) {\n // Git submodule - .git is a file with format: gitdir: <path>\n const gitFileContent = fs.readFileSync(gitPath, 'utf-8').trim();\n const match = gitFileContent.match(/^gitdir:\\s*(.+)$/);\n\n if (match && match[1]) {\n // Resolve the gitdir path (it's relative to the repo path)\n const gitDirPath = path.resolve(repoPath, match[1]);\n this.logger.debug(`Resolved submodule gitdir: ${gitDirPath}`);\n\n // Ensure the git directory exists\n if (!fs.existsSync(gitDirPath)) {\n throw new Error(`Submodule git directory does not exist: ${gitDirPath}`);\n }\n\n return gitDirPath;\n }\n\n throw new Error(`Invalid .git file format in ${gitPath}: ${gitFileContent}`);\n }\n } catch (error: any) {\n // Check if error is from statSync (file doesn't exist)\n if (error.code === 'ENOENT') {\n throw new Error(`No .git directory or file found in ${repoPath}`);\n }\n throw new Error(`Failed to resolve git directory for ${repoPath}: ${error.message}`);\n }\n\n throw new Error(`No .git directory or file found in ${repoPath}`);\n }\n\n /**\n * Register cleanup handlers to release locks on process exit\n */\n private registerCleanupHandlers(): void {\n const cleanup = () => {\n this.destroy();\n };\n\n // Handle various exit scenarios\n process.on('exit', cleanup);\n process.on('SIGINT', () => {\n cleanup();\n process.exit(130); // Standard exit code for SIGINT\n });\n process.on('SIGTERM', () => {\n cleanup();\n process.exit(143); // Standard exit code for SIGTERM\n });\n process.on('uncaughtException', (error) => {\n this.logger.error('FILELOCK_UNCAUGHT_EXCEPTION: Uncaught exception detected, cleaning up locks | Error: ' + error + ' | Action: Release all locks');\n cleanup();\n process.exit(1);\n });\n }\n\n /**\n * Execute a git operation with repository-level file locking\n * @param repoPath Path to the git repository root\n * @param operation The async operation to execute under lock\n * @param operationName Optional name for logging\n * @returns Result of the operation\n */\n async withGitLock<T>(\n repoPath: string,\n operation: () => Promise<T>,\n operationName?: string\n ): Promise<T> {\n const lock = this.getRepositoryLock(repoPath);\n const startWait = Date.now();\n\n this.logger.silly(\n `Acquiring file lock for ${repoPath}${operationName ? ` for: ${operationName}` : ''}`\n );\n\n await lock.lock();\n\n const waitTime = Date.now() - startWait;\n if (waitTime > 100) {\n this.logger.debug(\n `Acquired file lock for ${repoPath} after ${waitTime}ms${operationName ? ` for: ${operationName}` : ''}`\n );\n }\n\n try {\n return await operation();\n } finally {\n lock.unlock();\n }\n }\n\n /**\n * Clean up all locks\n */\n destroy(): void {\n this.logger.debug(`Cleaning up ${this.locks.size} file lock(s)`);\n for (const lock of this.locks.values()) {\n lock.unlock();\n }\n this.locks.clear();\n }\n}\n"],"names":["FileLock","lock","attempts","currentDelay","retryDelay","maxRetries","lockData","pid","process","timestamp","Date","now","hostname","os","fs","existsSync","lockPath","lockContent","readFileSync","existingLock","JSON","parse","lockAge","lockTimeout","logger","debug","unlinkSync","writeFileSync","stringify","flag","lockAcquired","error","code","verbose","Promise","resolve","setTimeout","Math","min","maxRetryDelay","Error","message","unlock","silly","isLocked","getLogger","RepositoryFileLockManager","getRepositoryLock","repoPath","normalizedPath","path","locks","has","gitDirPath","resolveGitDirectory","join","set","cleanupRegistered","registerCleanupHandlers","get","gitPath","stat","statSync","isDirectory","isFile","gitFileContent","trim","match","cleanup","destroy","on","exit","withGitLock","operation","operationName","startWait","waitTime","size","values","clear","Map"],"mappings":"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"}
@@ -1,379 +0,0 @@
1
- import path__default from 'path';
2
- import { incrementPatchVersion, validateVersionString, convertToReleaseVersion, incrementPrereleaseVersion } from '@eldrforge/shared';
3
- export { calculateTargetVersion, convertToReleaseVersion, deepMerge, incrementMajorVersion, incrementMinorVersion, incrementPatchVersion, incrementPrereleaseVersion, stringifyJSON, validateVersionString } from '@eldrforge/shared';
4
- import { getLogger } from '../logging.js';
5
-
6
- /**
7
- * Get version from a specific branch's package.json
8
- */ const getVersionFromBranch = async (branchName)=>{
9
- const { runSecure, validateGitRef, safeJsonParse, validatePackageJson } = await import('@eldrforge/git-tools');
10
- try {
11
- // Validate branch name to prevent injection
12
- if (!validateGitRef(branchName)) {
13
- throw new Error(`Invalid branch name: ${branchName}`);
14
- }
15
- // Cast to any to avoid type mismatch with node_modules version
16
- const { stdout } = await runSecure('git', [
17
- 'show',
18
- `${branchName}:package.json`
19
- ], {
20
- suppressErrorLogging: true
21
- });
22
- const packageJson = safeJsonParse(stdout, 'package.json');
23
- const validated = validatePackageJson(packageJson, 'package.json');
24
- return validated.version;
25
- } catch {
26
- // Return null if we can't get the version (branch may not exist or no package.json)
27
- return null;
28
- }
29
- };
30
- /**
31
- * Calculate target version based on branch configuration
32
- * SEMANTICS: The version config specifies what version should be ON the target branch
33
- */ const calculateBranchDependentVersion = async (currentVersion, currentBranch, branchesConfig, targetBranch)=>{
34
- const { getLogger } = await import('../logging.js');
35
- const logger = getLogger();
36
- // Look up the source branch to find the target branch
37
- if (!branchesConfig || !branchesConfig[currentBranch]) {
38
- // Use default configuration from constants
39
- const { KODRDRIV_DEFAULTS } = await import('../constants.js');
40
- const defaultConfig = KODRDRIV_DEFAULTS.branches;
41
- if (defaultConfig && defaultConfig[currentBranch]) {
42
- const sourceConfig = defaultConfig[currentBranch];
43
- const finalTargetBranch = sourceConfig.targetBranch || targetBranch || 'main';
44
- // Look at target branch's version config to determine what version it should have
45
- const targetConfig = defaultConfig[finalTargetBranch];
46
- logger.info(`VERSION_BRANCH_DEFAULT: Using default branch configuration | Source Branch: ${currentBranch} | Target Branch: ${finalTargetBranch} | Source: default config`);
47
- if (!(targetConfig === null || targetConfig === void 0 ? void 0 : targetConfig.version)) {
48
- const defaultVersion = incrementPatchVersion(currentVersion);
49
- logger.debug(`No version config for target branch '${finalTargetBranch}', using default increment`);
50
- return {
51
- version: defaultVersion,
52
- targetBranch: finalTargetBranch
53
- };
54
- }
55
- return calculateVersionFromTargetConfig(currentVersion, finalTargetBranch, targetConfig.version, logger);
56
- }
57
- // No config at all, use traditional defaults
58
- const defaultTargetBranch = targetBranch || 'main';
59
- const defaultVersion = incrementPatchVersion(currentVersion);
60
- logger.debug(`No branch-specific config found for '${currentBranch}', using defaults`);
61
- return {
62
- version: defaultVersion,
63
- targetBranch: defaultTargetBranch
64
- };
65
- }
66
- const sourceConfig = branchesConfig[currentBranch];
67
- const finalTargetBranch = sourceConfig.targetBranch || targetBranch || 'main';
68
- // Look at target branch's version config to determine what version it should have
69
- const targetConfig = branchesConfig[finalTargetBranch];
70
- logger.info(`VERSION_BRANCH_DEPENDENT: Using branch-dependent targeting | Source Branch: ${currentBranch} | Target Branch: ${finalTargetBranch} | Source: branch config`);
71
- if (!(targetConfig === null || targetConfig === void 0 ? void 0 : targetConfig.version)) {
72
- // No version config for target, use default increment
73
- const defaultVersion = incrementPatchVersion(currentVersion);
74
- logger.debug(`No version config for target branch '${finalTargetBranch}', using default increment`);
75
- return {
76
- version: defaultVersion,
77
- targetBranch: finalTargetBranch
78
- };
79
- }
80
- return calculateVersionFromTargetConfig(currentVersion, finalTargetBranch, targetConfig.version, logger);
81
- };
82
- /**
83
- * Calculate version based on target branch configuration
84
- */ const calculateVersionFromTargetConfig = async (currentVersion, targetBranch, versionConfig, logger)=>{
85
- if (versionConfig.type === 'release') {
86
- // Convert to release version (remove prerelease tags)
87
- const releaseVersion = convertToReleaseVersion(currentVersion);
88
- logger.info(`VERSION_RELEASE_CONVERSION: Converting prerelease to release version | Current: ${currentVersion} | Release: ${releaseVersion} | Action: Remove prerelease tag`);
89
- return {
90
- version: releaseVersion,
91
- targetBranch
92
- };
93
- } else if (versionConfig.type === 'prerelease') {
94
- if (!versionConfig.tag) {
95
- throw new Error(`Prerelease version type requires a tag in branch configuration`);
96
- }
97
- const tag = versionConfig.tag;
98
- if (versionConfig.increment) {
99
- // Check if there's already a version with this tag in the target branch
100
- const targetBranchVersion = await getVersionFromBranch(targetBranch);
101
- if (targetBranchVersion) {
102
- // Use the target branch version as the base and increment
103
- const newVersion = incrementPrereleaseVersion(targetBranchVersion, tag);
104
- logger.info(`VERSION_PRERELEASE_INCREMENT: Incrementing prerelease version | Current: ${targetBranchVersion} | New: ${newVersion} | Action: Increment prerelease number`);
105
- return {
106
- version: newVersion,
107
- targetBranch
108
- };
109
- } else {
110
- // No version in target branch, use current version as base
111
- const newVersion = incrementPrereleaseVersion(currentVersion, tag);
112
- logger.info(`VERSION_PRERELEASE_CREATE: Creating new prerelease version | Current: ${currentVersion} | New: ${newVersion} | Action: Add prerelease tag`);
113
- return {
114
- version: newVersion,
115
- targetBranch
116
- };
117
- }
118
- } else {
119
- // Just add/change the prerelease tag without incrementing
120
- const baseVersion = convertToReleaseVersion(currentVersion);
121
- const newVersion = `${baseVersion}-${tag}.0`;
122
- logger.info(`VERSION_PRERELEASE_TAG: Setting prerelease tag | Current: ${currentVersion} | New: ${newVersion} | Tag: ${versionConfig.tag}`);
123
- return {
124
- version: newVersion,
125
- targetBranch
126
- };
127
- }
128
- }
129
- throw new Error(`Invalid version type: ${versionConfig.type}`);
130
- };
131
- /**
132
- * Find the development branch from branches configuration
133
- * Returns the branch marked with developmentBranch: true
134
- */ const findDevelopmentBranch = (branchesConfig)=>{
135
- if (!branchesConfig || typeof branchesConfig !== 'object') {
136
- return null;
137
- }
138
- for (const [branchName, branchConfig] of Object.entries(branchesConfig)){
139
- if (branchConfig && typeof branchConfig === 'object' && branchConfig.developmentBranch === true) {
140
- return branchName;
141
- }
142
- }
143
- return null;
144
- };
145
- const checkIfTagExists = async (tagName)=>{
146
- const { runSecure, validateGitRef } = await import('@eldrforge/git-tools');
147
- try {
148
- // Validate tag name to prevent injection
149
- if (!validateGitRef(tagName)) {
150
- throw new Error(`Invalid tag name: ${tagName}`);
151
- }
152
- const { stdout } = await runSecure('git', [
153
- 'tag',
154
- '-l',
155
- tagName
156
- ]);
157
- return stdout.trim() === tagName;
158
- } catch {
159
- // If git command fails, assume tag doesn't exist
160
- return false;
161
- }
162
- };
163
- const confirmVersionInteractively = async (currentVersion, proposedVersion, targetVersionInput)=>{
164
- const { getUserChoice, getUserTextInput, requireTTY } = await import('./interactive.js');
165
- const { getLogger } = await import('../logging.js');
166
- requireTTY('Interactive version confirmation requires a terminal.');
167
- const logger = getLogger();
168
- logger.info(`\nVERSION_CONFIRMATION: Version confirmation required | Current: ${currentVersion} | Proposed: ${proposedVersion}`);
169
- logger.info(`VERSION_CURRENT: Current package version | Version: ${currentVersion}`);
170
- logger.info(`VERSION_PROPOSED: Proposed new version | Version: ${proposedVersion}`);
171
- if (targetVersionInput) {
172
- logger.info(`VERSION_TARGET_INPUT: Target version provided | Input: ${targetVersionInput}`);
173
- }
174
- const choices = [
175
- {
176
- key: 'c',
177
- label: `Confirm ${proposedVersion}`
178
- },
179
- {
180
- key: 'e',
181
- label: 'Enter custom version'
182
- },
183
- {
184
- key: 'a',
185
- label: 'Abort publish'
186
- }
187
- ];
188
- const choice = await getUserChoice('\n🤔 Confirm the version for this release:', choices);
189
- switch(choice){
190
- case 'c':
191
- return proposedVersion;
192
- case 'e':
193
- {
194
- const customVersion = await getUserTextInput('\n📝 Enter the version number (e.g., "4.30.0"):');
195
- if (!validateVersionString(customVersion)) {
196
- throw new Error(`Invalid version format: ${customVersion}. Expected format: "x.y.z"`);
197
- }
198
- const cleanCustomVersion = customVersion.startsWith('v') ? customVersion.slice(1) : customVersion;
199
- logger.info(`VERSION_CUSTOM_SELECTED: Using custom version from user input | Version: ${cleanCustomVersion} | Source: interactive input`);
200
- return cleanCustomVersion;
201
- }
202
- case 'a':
203
- throw new Error('Release aborted by user');
204
- default:
205
- throw new Error(`Unexpected choice: ${choice}`);
206
- }
207
- };
208
- const getOutputPath = (outputDirectory, filename)=>{
209
- return path__default.join(outputDirectory, filename);
210
- };
211
- const getTimestampedFilename = (baseName, extension = '.json')=>{
212
- const now = new Date();
213
- // Format as YYMMdd-HHmm (e.g., 250701-1030)
214
- const yy = now.getFullYear().toString().slice(-2);
215
- const mm = (now.getMonth() + 1).toString().padStart(2, '0');
216
- const dd = now.getDate().toString().padStart(2, '0');
217
- const hh = now.getHours().toString().padStart(2, '0');
218
- const min = now.getMinutes().toString().padStart(2, '0');
219
- const timestamp = `${yy}${mm}${dd}-${hh}${min}`;
220
- return `${timestamp}-${baseName}${extension}`;
221
- };
222
- const getTimestampedRequestFilename = (baseName)=>{
223
- return getTimestampedFilename(baseName, '.request.json');
224
- };
225
- const getTimestampedResponseFilename = (baseName)=>{
226
- return getTimestampedFilename(baseName, '.response.json');
227
- };
228
- const getTimestampedCommitFilename = ()=>{
229
- return getTimestampedFilename('commit-message', '.md');
230
- };
231
- const getTimestampedReleaseNotesFilename = ()=>{
232
- return getTimestampedFilename('release-notes', '.md');
233
- };
234
- const getTimestampedAudioFilename = ()=>{
235
- return getTimestampedFilename('audio-recording', '.wav');
236
- };
237
- const getTimestampedReviewFilename = ()=>{
238
- return getTimestampedFilename('review-analysis', '.md');
239
- };
240
- const getTimestampedReviewNotesFilename = ()=>{
241
- return getTimestampedFilename('review-notes', '.md');
242
- };
243
- // archiveAudio function moved to @eldrforge/audio-tools
244
- /**
245
- * Query npm registry for published version of a package
246
- * Returns null if package is not published or on error
247
- */ const getNpmPublishedVersion = async (packageName)=>{
248
- const logger = getLogger();
249
- try {
250
- const { runSecure } = await import('@eldrforge/git-tools');
251
- // Use npm view to get the latest published version
252
- // --json flag ensures parseable output
253
- const { stdout } = await runSecure('npm', [
254
- 'view',
255
- packageName,
256
- 'version',
257
- '--json'
258
- ]);
259
- if (!stdout || stdout.trim() === '') {
260
- logger.verbose(`Package ${packageName} not found on npm registry`);
261
- return null;
262
- }
263
- // npm view returns just the version string for a single version
264
- const version = stdout.trim().replace(/^["']|["']$/g, ''); // Remove quotes if present
265
- logger.verbose(`Found ${packageName}@${version} on npm registry`);
266
- return version;
267
- } catch (error) {
268
- // Package not found or network error
269
- logger.verbose(`Could not query npm for ${packageName}: ${error.message}`);
270
- return null;
271
- }
272
- };
273
- /**
274
- * Get detailed info about a tag including the version it points to
275
- */ const getTagInfo = async (tagName)=>{
276
- try {
277
- const { runSecure, validateGitRef } = await import('@eldrforge/git-tools');
278
- if (!validateGitRef(tagName)) {
279
- throw new Error(`Invalid tag name: ${tagName}`);
280
- }
281
- // Check if tag exists
282
- const { stdout: tagList } = await runSecure('git', [
283
- 'tag',
284
- '-l',
285
- tagName
286
- ]);
287
- if (tagList.trim() !== tagName) {
288
- return {
289
- exists: false
290
- };
291
- }
292
- // Get the commit the tag points to
293
- const { stdout: commit } = await runSecure('git', [
294
- 'rev-list',
295
- '-n',
296
- '1',
297
- tagName
298
- ]);
299
- // Extract version from tag name (assumes format like v1.2.3 or working/v1.2.3)
300
- const versionMatch = tagName.match(/v?(\d+\.\d+\.\d+(?:-[a-zA-Z0-9.-]+)?)/);
301
- const version = versionMatch ? versionMatch[1] : undefined;
302
- return {
303
- exists: true,
304
- commit: commit.trim(),
305
- version
306
- };
307
- } catch {
308
- return null;
309
- }
310
- };
311
- /**
312
- * Check if a version is a development/prerelease version (has prerelease tag)
313
- */ const isDevelopmentVersion = (version)=>{
314
- // Development versions have prerelease tags: 1.2.3-dev.0, 1.2.3-alpha.1, etc.
315
- return version.includes('-');
316
- };
317
- /**
318
- * Get expected version pattern for a branch
319
- */ const getExpectedVersionPattern = (branchName)=>{
320
- // Development/working branches should have prerelease versions
321
- const devBranchPatterns = /^(working|development|dev|feature\/|wip\/)/i;
322
- if (devBranchPatterns.test(branchName)) {
323
- return {
324
- pattern: /^\d+\.\d+\.\d+-[a-zA-Z0-9.-]+$/,
325
- description: 'X.Y.Z-<tag> (e.g., 1.2.3-dev.0)',
326
- isDevelopment: true
327
- };
328
- }
329
- // Main/master/production branches should have release versions
330
- const releaseBranchPatterns = /^(main|master|production|release\/)/i;
331
- if (releaseBranchPatterns.test(branchName)) {
332
- return {
333
- pattern: /^\d+\.\d+\.\d+$/,
334
- description: 'X.Y.Z (e.g., 1.2.3)',
335
- isDevelopment: false
336
- };
337
- }
338
- // For other branches, allow both but prefer release versions
339
- return {
340
- pattern: /^\d+\.\d+\.\d+(-[a-zA-Z0-9.-]+)?$/,
341
- description: 'X.Y.Z or X.Y.Z-<tag>',
342
- isDevelopment: false
343
- };
344
- };
345
- /**
346
- * Validate version against branch expectations
347
- */ const validateVersionForBranch = (version, branchName)=>{
348
- const expected = getExpectedVersionPattern(branchName);
349
- if (!expected.pattern.test(version)) {
350
- return {
351
- valid: false,
352
- issue: `Invalid version format for branch '${branchName}'`,
353
- fix: `Version should match ${expected.description}`
354
- };
355
- }
356
- const isDevVersion = isDevelopmentVersion(version);
357
- // Development branches should have development versions
358
- if (expected.isDevelopment && !isDevVersion) {
359
- return {
360
- valid: false,
361
- issue: `Release version on development branch '${branchName}'`,
362
- fix: 'Run kodrdriv development to update to development version'
363
- };
364
- }
365
- // Release branches should NOT have development versions
366
- if (!expected.isDevelopment && branchName.match(/^(main|master|production|release\/)/) && isDevVersion) {
367
- return {
368
- valid: false,
369
- issue: `Development version on release branch '${branchName}'`,
370
- fix: 'Do not commit development versions to release branches'
371
- };
372
- }
373
- return {
374
- valid: true
375
- };
376
- };
377
-
378
- export { calculateBranchDependentVersion, checkIfTagExists, confirmVersionInteractively, findDevelopmentBranch, getExpectedVersionPattern, getNpmPublishedVersion, getOutputPath, getTagInfo, getTimestampedAudioFilename, getTimestampedCommitFilename, getTimestampedFilename, getTimestampedReleaseNotesFilename, getTimestampedRequestFilename, getTimestampedResponseFilename, getTimestampedReviewFilename, getTimestampedReviewNotesFilename, getVersionFromBranch, isDevelopmentVersion, validateVersionForBranch };
379
- //# sourceMappingURL=general.js.map