@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,537 +0,0 @@
1
- import { getLogger, getDryRunLogger } from '../logging.js';
2
- import { createStorage } from '@eldrforge/shared';
3
- import { safeJsonParse, validatePackageJson, run, runSecure } from '@eldrforge/git-tools';
4
- import { findAllPackageJsonFiles } from '../util/performance.js';
5
- import fs from 'fs/promises';
6
- import path__default from 'path';
7
-
8
- // Helper function to check if a dependency matches any external unlink patterns
9
- const matchesExternalUnlinkPattern = (dependencyName, externalUnlinkPatterns)=>{
10
- if (!externalUnlinkPatterns || externalUnlinkPatterns.length === 0) {
11
- return false;
12
- }
13
- return externalUnlinkPatterns.some((pattern)=>{
14
- // Simple string matching - could be enhanced with glob patterns later
15
- return dependencyName === pattern || dependencyName.startsWith(pattern);
16
- });
17
- };
18
- // Helper function to check if a path is a symbolic link
19
- const isSymbolicLink = async (filePath)=>{
20
- try {
21
- const stats = await fs.lstat(filePath);
22
- return stats.isSymbolicLink();
23
- } catch {
24
- return false;
25
- }
26
- };
27
- // Helper function to get the target of a symbolic link
28
- const getSymbolicLinkTarget = async (filePath)=>{
29
- try {
30
- const target = await fs.readlink(filePath);
31
- return target;
32
- } catch {
33
- return null;
34
- }
35
- };
36
- // Helper function to find all linked dependencies in a package
37
- const findLinkedDependencies = async (packagePath, packageName, storage, logger)=>{
38
- const linkedDependencies = [];
39
- try {
40
- const packageJsonPath = path__default.join(packagePath, 'package.json');
41
- const packageJsonContent = await storage.readFile(packageJsonPath, 'utf-8');
42
- const parsed = safeJsonParse(packageJsonContent, packageJsonPath);
43
- const packageJson = validatePackageJson(parsed, packageJsonPath);
44
- const allDependencies = {
45
- ...packageJson.dependencies,
46
- ...packageJson.devDependencies
47
- };
48
- const nodeModulesPath = path__default.join(packagePath, 'node_modules');
49
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
50
- for (const [dependencyName, version] of Object.entries(allDependencies)){
51
- let dependencyPath;
52
- if (dependencyName.startsWith('@')) {
53
- // Scoped package
54
- const [scope, name] = dependencyName.split('/');
55
- dependencyPath = path__default.join(nodeModulesPath, scope, name);
56
- } else {
57
- // Unscoped package
58
- dependencyPath = path__default.join(nodeModulesPath, dependencyName);
59
- }
60
- if (await isSymbolicLink(dependencyPath)) {
61
- const target = await getSymbolicLinkTarget(dependencyPath);
62
- if (target) {
63
- // Determine if this is an external dependency (not in the same workspace)
64
- const isExternal = !target.includes('node_modules') || target.startsWith('..');
65
- linkedDependencies.push({
66
- dependencyName,
67
- targetPath: target,
68
- isExternal
69
- });
70
- }
71
- }
72
- }
73
- } catch (error) {
74
- logger.warn(`UNLINK_CHECK_FAILED: Unable to check linked dependencies | Package: ${packageName} | Error: ${error.message}`);
75
- }
76
- return linkedDependencies;
77
- };
78
- // Helper function to remove symbolic links manually
79
- const removeSymbolicLink = async (packageName, targetDir, logger, isDryRun = false)=>{
80
- try {
81
- // Parse package name to get scope and name parts
82
- const [scope, name] = packageName.startsWith('@') ? packageName.split('/') : [
83
- null,
84
- packageName
85
- ];
86
- // Create the target path structure
87
- const nodeModulesPath = path__default.join(targetDir, 'node_modules');
88
- let targetPath;
89
- if (scope) {
90
- // Scoped package: node_modules/@scope/name
91
- targetPath = path__default.join(nodeModulesPath, scope, name);
92
- } else {
93
- // Unscoped package: node_modules/name
94
- targetPath = path__default.join(nodeModulesPath, name);
95
- }
96
- if (isDryRun) {
97
- logger.verbose(`DRY RUN: Would check and remove symlink: ${targetPath}`);
98
- return true;
99
- }
100
- // Check if something exists at the target path
101
- try {
102
- const stats = await fs.lstat(targetPath); // Use lstat to not follow symlinks
103
- if (stats.isSymbolicLink()) {
104
- // It's a symlink, remove it
105
- await fs.unlink(targetPath);
106
- logger.verbose(`Removed symlink: ${targetPath}`);
107
- return true;
108
- } else {
109
- logger.verbose(`Target exists but is not a symlink: ${targetPath}`);
110
- return false;
111
- }
112
- } catch (error) {
113
- if (error.code === 'ENOENT') {
114
- // Nothing exists at target path, nothing to remove
115
- logger.verbose(`No symlink found at: ${targetPath}`);
116
- return true;
117
- } else {
118
- throw error; // Re-throw unexpected errors
119
- }
120
- }
121
- } catch (error) {
122
- logger.warn(`UNLINK_SYMLINK_REMOVE_FAILED: Unable to remove symlink | Package: ${packageName} | Error: ${error.message}`);
123
- return false;
124
- }
125
- };
126
- // Helper function to parse package names and scopes (same as link command)
127
- const parsePackageArgument = (packageArg)=>{
128
- if (packageArg.startsWith('@')) {
129
- const parts = packageArg.split('/');
130
- if (parts.length === 1) {
131
- // Just a scope like "@fjell"
132
- return {
133
- scope: parts[0]
134
- };
135
- } else {
136
- // Full package name like "@fjell/core"
137
- return {
138
- scope: parts[0],
139
- packageName: packageArg
140
- };
141
- }
142
- } else {
143
- throw new Error(`Package argument must start with @ (scope): ${packageArg}`);
144
- }
145
- };
146
- // Find packages in the workspace that match the given scope or package name
147
- const findMatchingPackages = async (targetDirectories, scope, storage, logger, packageName)=>{
148
- const matchingPackages = [];
149
- // Find all package.json files in target directories
150
- let allPackageJsonFiles = [];
151
- for (const targetDirectory of targetDirectories){
152
- const packageJsonFiles = await findAllPackageJsonFiles(targetDirectory, storage);
153
- allPackageJsonFiles = allPackageJsonFiles.concat(packageJsonFiles);
154
- }
155
- for (const packageJsonLocation of allPackageJsonFiles){
156
- const packageDir = packageJsonLocation.path.replace('/package.json', '');
157
- try {
158
- const packageJsonContent = await storage.readFile(packageJsonLocation.path, 'utf-8');
159
- const parsed = safeJsonParse(packageJsonContent, packageJsonLocation.path);
160
- const packageJson = validatePackageJson(parsed, packageJsonLocation.path);
161
- if (!packageJson.name) continue;
162
- const isInScope = packageJson.name.startsWith(scope + '/');
163
- const isExactMatch = packageName && packageJson.name === packageName;
164
- if (isInScope || isExactMatch) {
165
- matchingPackages.push({
166
- name: packageJson.name,
167
- path: packageDir,
168
- isSource: packageName ? packageJson.name === packageName : isInScope
169
- });
170
- }
171
- } catch (error) {
172
- logger.warn(`Failed to parse ${packageJsonLocation.path}: ${error.message}`);
173
- }
174
- }
175
- return matchingPackages;
176
- };
177
- // Find packages that depend on the target package
178
- const findConsumingPackages = async (targetDirectories, targetPackageName, storage, logger)=>{
179
- const consumingPackages = [];
180
- // Find all package.json files in target directories
181
- let allPackageJsonFiles = [];
182
- for (const targetDirectory of targetDirectories){
183
- const packageJsonFiles = await findAllPackageJsonFiles(targetDirectory, storage);
184
- allPackageJsonFiles = allPackageJsonFiles.concat(packageJsonFiles);
185
- }
186
- for (const packageJsonLocation of allPackageJsonFiles){
187
- const packageDir = packageJsonLocation.path.replace('/package.json', '');
188
- try {
189
- const packageJsonContent = await storage.readFile(packageJsonLocation.path, 'utf-8');
190
- const parsed = safeJsonParse(packageJsonContent, packageJsonLocation.path);
191
- const packageJson = validatePackageJson(parsed, packageJsonLocation.path);
192
- if (!packageJson.name) continue;
193
- // Check if this package depends on the target package
194
- const dependencyTypes = [
195
- 'dependencies',
196
- 'devDependencies',
197
- 'peerDependencies',
198
- 'optionalDependencies'
199
- ];
200
- const hasDependency = dependencyTypes.some((depType)=>packageJson[depType] && packageJson[depType][targetPackageName]);
201
- if (hasDependency && packageJson.name !== targetPackageName) {
202
- consumingPackages.push({
203
- name: packageJson.name,
204
- path: packageDir
205
- });
206
- }
207
- } catch (error) {
208
- logger.warn(`Failed to parse ${packageJsonLocation.path}: ${error.message}`);
209
- }
210
- }
211
- return consumingPackages;
212
- };
213
- const executeInternal = async (runConfig, packageArgument)=>{
214
- var _runConfig_unlink, _runConfig_tree;
215
- const isDryRun = runConfig.dryRun || ((_runConfig_unlink = runConfig.unlink) === null || _runConfig_unlink === void 0 ? void 0 : _runConfig_unlink.dryRun) || false;
216
- const logger = getDryRunLogger(isDryRun);
217
- const storage = createStorage();
218
- // Check if this is a status command
219
- if (packageArgument === 'status') {
220
- return await executeStatus(runConfig);
221
- }
222
- // Get target directories from config, default to current directory
223
- const targetDirectories = ((_runConfig_tree = runConfig.tree) === null || _runConfig_tree === void 0 ? void 0 : _runConfig_tree.directories) || [
224
- process.cwd()
225
- ];
226
- if (targetDirectories.length === 1) {
227
- logger.info(`UNLINK_WORKSPACE_ANALYSIS: Analyzing single workspace directory | Path: ${targetDirectories[0]} | Purpose: Find packages to unlink`);
228
- } else {
229
- logger.info(`UNLINK_WORKSPACE_ANALYSIS: Analyzing multiple workspace directories | Paths: ${targetDirectories.join(', ')} | Count: ${targetDirectories.length} | Purpose: Find packages to unlink`);
230
- }
231
- // If no package argument provided, implement new behavior for current project
232
- if (!packageArgument) {
233
- var _runConfig_unlink1, _runConfig_unlink2;
234
- logger.info('UNLINK_SMART_MODE: Smart unlinking mode activated for current project | Mode: smart | Target: current directory | Purpose: Auto-unlink based on scope');
235
- const currentDir = process.cwd();
236
- const packageJsonPath = `${currentDir}/package.json`;
237
- // Check if we're in a directory with package.json
238
- if (!await storage.exists(packageJsonPath)) {
239
- const message = `No package.json found in current directory: ${currentDir}`;
240
- logger.warn('UNLINK_NO_PACKAGE_JSON: No package.json found in current directory | Directory: ' + currentDir + ' | Action: Cannot unlink without package.json');
241
- return message;
242
- }
243
- // Parse package.json to get package name
244
- let packageName;
245
- try {
246
- const packageJsonContent = await storage.readFile(packageJsonPath, 'utf-8');
247
- const parsed = safeJsonParse(packageJsonContent, packageJsonPath);
248
- const packageJson = validatePackageJson(parsed, packageJsonPath);
249
- if (!packageJson.name) {
250
- throw new Error('package.json has no name field');
251
- }
252
- packageName = packageJson.name;
253
- } catch (error) {
254
- const message = `Failed to parse package.json: ${error.message}`;
255
- logger.error('UNLINK_PACKAGE_NAME_MISSING: package.json must have a name field | Field: name | Requirement: Required for unlinking | Action: Add name field to package.json');
256
- return message;
257
- }
258
- logger.info(`UNLINK_PACKAGE_PROCESSING: Processing package for unlinking | Package: ${packageName} | Action: Remove symlinks and restore registry versions`);
259
- const cleanNodeModules = ((_runConfig_unlink1 = runConfig.unlink) === null || _runConfig_unlink1 === void 0 ? void 0 : _runConfig_unlink1.cleanNodeModules) || false;
260
- const externalUnlinkPatterns = ((_runConfig_unlink2 = runConfig.unlink) === null || _runConfig_unlink2 === void 0 ? void 0 : _runConfig_unlink2.externals) || [];
261
- // Step 0: Handle external dependencies if patterns are specified
262
- if (externalUnlinkPatterns.length > 0) {
263
- logger.info(`UNLINK_EXTERNAL_DEPS: Processing external dependencies | Patterns: ${externalUnlinkPatterns.join(', ')} | Purpose: Unlink external packages before main package`);
264
- // Read package.json to get dependencies
265
- const packageJsonContent = await storage.readFile(packageJsonPath, 'utf-8');
266
- const parsed = safeJsonParse(packageJsonContent, packageJsonPath);
267
- const packageJson = validatePackageJson(parsed, packageJsonPath);
268
- const allDependencies = {
269
- ...packageJson.dependencies,
270
- ...packageJson.devDependencies
271
- };
272
- const externalDependencies = Object.keys(allDependencies).filter((depName)=>matchesExternalUnlinkPattern(depName, externalUnlinkPatterns));
273
- if (externalDependencies.length > 0) {
274
- logger.info(`UNLINK_EXTERNAL_FOUND: Found external dependencies to unlink | Count: ${externalDependencies.length} | Dependencies: ${externalDependencies.join(', ')}`);
275
- for (const depName of externalDependencies){
276
- try {
277
- const success = await removeSymbolicLink(depName, currentDir, logger, isDryRun);
278
- if (success) {
279
- logger.info(`UNLINK_EXTERNAL_SUCCESS: External dependency unlinked successfully | Dependency: ${depName} | Status: unlinked`);
280
- } else {
281
- logger.warn(`UNLINK_EXTERNAL_FAILED: Failed to unlink external dependency | Dependency: ${depName} | Status: failed`);
282
- }
283
- } catch (error) {
284
- logger.warn(`UNLINK_EXTERNAL_ERROR: Error during external dependency unlink | Dependency: ${depName} | Error: ${error.message}`);
285
- }
286
- }
287
- } else {
288
- logger.info('UNLINK_EXTERNAL_NONE: No external dependencies found matching patterns | Patterns: ' + externalUnlinkPatterns.join(', ') + ' | Action: Skipping external unlink');
289
- }
290
- }
291
- if (isDryRun) {
292
- let dryRunMessage = `DRY RUN: Would execute unlink steps for ${packageName}:\n`;
293
- if (externalUnlinkPatterns.length > 0) {
294
- dryRunMessage += ` 0. Unlink external dependencies matching patterns: ${externalUnlinkPatterns.join(', ')}\n`;
295
- }
296
- dryRunMessage += ` 1. npm unlink -g\n`;
297
- if (cleanNodeModules) {
298
- dryRunMessage += ` 2. rm -rf node_modules package-lock.json\n`;
299
- dryRunMessage += ` 3. npm install\n`;
300
- dryRunMessage += ` 4. Check for remaining links with npm ls --link`;
301
- } else {
302
- dryRunMessage += ` 2. Check for remaining links with npm ls --link\n`;
303
- dryRunMessage += ` Note: Use --clean-node-modules flag to also clean and reinstall dependencies`;
304
- }
305
- logger.info(dryRunMessage);
306
- return dryRunMessage;
307
- }
308
- // Step 1: Remove global link
309
- logger.info('UNLINK_GLOBAL_REMOVING: Removing global npm link | Step: 1 | Command: npm unlink -g | Purpose: Remove package from global npm');
310
- try {
311
- await run('npm unlink -g');
312
- logger.info('UNLINK_GLOBAL_SUCCESS: Global link removed successfully | Status: unlinked | Location: global npm');
313
- } catch (error) {
314
- // This might fail if the package wasn't globally linked, which is OK
315
- logger.warn(`UNLINK_GLOBAL_SKIP: Failed to remove global link | Error: ${error.message} | Impact: OK if package wasn't linked | Status: continuing`);
316
- }
317
- if (cleanNodeModules) {
318
- // Step 2: Clean node_modules and package-lock.json
319
- logger.info('UNLINK_CLEANING: Cleaning node_modules and package-lock.json | Command: rm -rf | Purpose: Remove symlinked dependencies');
320
- try {
321
- await run('rm -rf node_modules package-lock.json');
322
- logger.info('UNLINK_CLEAN_SUCCESS: Successfully cleaned node_modules and package-lock.json | Status: removed | Next: Fresh install');
323
- } catch (error) {
324
- logger.warn(`UNLINK_CLEAN_FAILED: Failed to clean directories | Error: ${error.message} | Impact: May need manual cleanup`);
325
- }
326
- // Step 3: Install dependencies
327
- logger.info('UNLINK_INSTALLING: Installing dependencies from registry | Command: npm install | Purpose: Restore registry versions');
328
- try {
329
- await run('npm install');
330
- logger.info('UNLINK_INSTALL_SUCCESS: Dependencies installed successfully | Source: npm registry | Status: completed');
331
- } catch (error) {
332
- logger.error(`UNLINK_INSTALL_FAILED: Failed to install dependencies | Error: ${error.message} | Impact: Package may be in inconsistent state`);
333
- throw error;
334
- }
335
- // Step 4: Check for remaining links (suppress output and errors)
336
- logger.info('UNLINK_CHECK_REMAINING: Checking for remaining symlinks | Purpose: Verify clean unlink | Action: Scan node_modules');
337
- } else {
338
- // Step 2: Check for remaining links (suppress output and errors)
339
- logger.info('UNLINK_CHECK_REMAINING: Checking for remaining symlinks | Mode: skip-reinstall | Purpose: Verify unlink | Action: Scan node_modules');
340
- logger.info('Note: Use --clean-node-modules flag to also clean and reinstall dependencies');
341
- }
342
- try {
343
- // Use child_process directly to suppress logging and get JSON output
344
- const util = await import('util');
345
- const child_process = await import('child_process');
346
- const execPromise = util.promisify(child_process.exec);
347
- const result = await execPromise('npm ls --link --json');
348
- // Parse JSON output to check for links to packages in the same scope
349
- const packageScope = packageName.includes('/') ? packageName.split('/')[0] : null;
350
- if (packageScope && result.stdout.trim()) {
351
- try {
352
- const linksData = safeJsonParse(result.stdout, 'npm ls output after unlink');
353
- const linkedPackages = Object.keys(linksData.dependencies || {});
354
- const scopeLinkedPackages = linkedPackages.filter((pkg)=>pkg.startsWith(packageScope + '/'));
355
- if (scopeLinkedPackages.length > 0) {
356
- logger.warn(`UNLINK_REMAINING_LINKS: Found remaining links to packages in scope | Scope: ${packageScope} | Packages: ${scopeLinkedPackages.join(', ')} | Note: May be expected if workspace packages linked`);
357
- logger.verbose('UNLINK_REMAINING_NOTE: Remaining links may be expected | Reason: Other workspace packages still linked | Status: normal');
358
- } else {
359
- logger.info('UNLINK_VERIFY_CLEAN: No problematic links found | Status: clean | Verification: passed');
360
- }
361
- } catch {
362
- // If JSON parsing fails, fall back to basic check
363
- logger.verbose('Failed to parse npm ls --link --json output, using basic check');
364
- if (result.stdout.includes(packageScope)) {
365
- logger.warn(`UNLINK_REMAINING_LINKS_BASIC: Found remaining links to scope | Scope: ${packageScope} | Check: basic | Note: May be expected`);
366
- logger.verbose('UNLINK_REMAINING_NOTE: Remaining links may be expected | Reason: Other workspace packages still linked | Status: normal');
367
- } else {
368
- logger.info('UNLINK_VERIFY_CLEAN: No problematic links found | Status: clean | Verification: passed');
369
- }
370
- }
371
- } else {
372
- logger.info('UNLINK_VERIFY_CLEAN: No problematic links found | Status: clean | Verification: passed');
373
- }
374
- } catch {
375
- // npm ls --link returns non-zero when there are no links, which is what we want
376
- // So we only log this at verbose level
377
- logger.verbose('npm ls --link check completed (non-zero exit is expected when no links exist)');
378
- }
379
- const summary = `Successfully unlinked ${packageName}`;
380
- logger.info(summary);
381
- return summary;
382
- }
383
- // New scope-based unlinking behavior
384
- logger.info(`UNLINK_EXPLICIT_MODE: Unlinking specific scope/package | Target: ${packageArgument} | Mode: explicit | Purpose: Remove symlinks for package`);
385
- const { scope, packageName } = parsePackageArgument(packageArgument);
386
- logger.verbose(`Parsed scope: ${scope}, package: ${packageName || 'all packages in scope'}`);
387
- // Find matching packages in the workspace
388
- const matchingPackages = await findMatchingPackages(targetDirectories, scope, storage, logger, packageName);
389
- if (matchingPackages.length === 0) {
390
- const message = packageName ? `No package found matching: ${packageName}` : `No packages found in scope: ${scope}`;
391
- logger.warn(message);
392
- return message;
393
- }
394
- logger.info(`Found ${matchingPackages.length} matching package(s)`);
395
- const unlinkedPackages = [];
396
- // If specific package name provided, use that; otherwise unlink all packages in scope
397
- const packagesToUnlink = packageName ? matchingPackages.filter((pkg)=>pkg.name === packageName) : matchingPackages;
398
- for (const pkg of packagesToUnlink){
399
- logger.info(`Processing package: ${pkg.name}`);
400
- // Step A: Find all packages that depend on this package and unlink them first
401
- const consumingPackages = await findConsumingPackages(targetDirectories, pkg.name, storage, logger);
402
- if (consumingPackages.length === 0) {
403
- logger.info(`No consuming packages found for: ${pkg.name}`);
404
- } else {
405
- logger.info(`Found ${consumingPackages.length} consuming package(s) for: ${pkg.name}`);
406
- for (const consumer of consumingPackages){
407
- try {
408
- const consumerOriginalCwd = process.cwd();
409
- process.chdir(consumer.path);
410
- try {
411
- if (isDryRun) {
412
- logger.info(`DRY RUN: Would run 'npm unlink ${pkg.name}' in: ${consumer.path}`);
413
- } else {
414
- logger.verbose(`Running 'npm unlink ${pkg.name}' in consumer: ${consumer.path}`);
415
- await runSecure('npm', [
416
- 'unlink',
417
- pkg.name
418
- ]);
419
- logger.info(`UNLINK_CONSUMER_SUCCESS: Consumer unlinked from package | Consumer: ${consumer.name} | Package: ${pkg.name} | Status: unlinked`);
420
- }
421
- } finally{
422
- process.chdir(consumerOriginalCwd);
423
- }
424
- } catch (error) {
425
- // npm unlink can fail if package wasn't linked, but that's OK
426
- logger.warn(`UNLINK_CONSUMER_FAILED: Failed to unlink consumer | Consumer: ${consumer.name} | Package: ${pkg.name} | Error: ${error.message}`);
427
- }
428
- }
429
- }
430
- // Step B: Run 'npm unlink' in the source package directory
431
- try {
432
- const originalCwd = process.cwd();
433
- process.chdir(pkg.path);
434
- try {
435
- if (isDryRun) {
436
- logger.info(`DRY RUN: Would run 'npm unlink' in: ${pkg.path}`);
437
- } else {
438
- logger.verbose(`Running 'npm unlink' in source: ${pkg.path}`);
439
- await run('npm unlink');
440
- logger.info(`UNLINK_SOURCE_SUCCESS: Source package unlinked | Package: ${pkg.name} | Status: unlinked`);
441
- }
442
- } finally{
443
- process.chdir(originalCwd);
444
- }
445
- unlinkedPackages.push(pkg.name);
446
- } catch (error) {
447
- // npm unlink can fail if package wasn't linked, but that's OK
448
- logger.warn(`UNLINK_SOURCE_FAILED: Failed to unlink source package | Package: ${pkg.name} | Error: ${error.message}`);
449
- unlinkedPackages.push(pkg.name); // Still count as success
450
- }
451
- }
452
- const summary = `Successfully unlinked ${unlinkedPackages.length} package(s): ${unlinkedPackages.join(', ')}`;
453
- logger.info(summary);
454
- return summary;
455
- };
456
- // Status function to show what's currently linked (same as link command)
457
- const executeStatus = async (runConfig)=>{
458
- var _runConfig_tree;
459
- const logger = getLogger();
460
- const storage = createStorage();
461
- // Get target directories from config, default to current directory
462
- const targetDirectories = ((_runConfig_tree = runConfig.tree) === null || _runConfig_tree === void 0 ? void 0 : _runConfig_tree.directories) || [
463
- process.cwd()
464
- ];
465
- if (targetDirectories.length === 1) {
466
- logger.info(`UNLINK_STATUS_CHECK: Checking link status in directory | Directory: ${targetDirectories[0]} | Purpose: Show current symlinks`);
467
- } else {
468
- logger.info(`UNLINK_STATUS_CHECK: Checking link status in directories | Directories: ${targetDirectories.join(', ')} | Count: ${targetDirectories.length} | Purpose: Show current symlinks`);
469
- }
470
- // Find all packages in the workspace
471
- let allPackageJsonFiles = [];
472
- for (const targetDirectory of targetDirectories){
473
- const packageJsonFiles = await findAllPackageJsonFiles(targetDirectory, storage);
474
- allPackageJsonFiles = allPackageJsonFiles.concat(packageJsonFiles);
475
- }
476
- const packageStatuses = [];
477
- for (const packageJsonLocation of allPackageJsonFiles){
478
- const packageDir = packageJsonLocation.path.replace('/package.json', '');
479
- try {
480
- const packageJsonContent = await storage.readFile(packageJsonLocation.path, 'utf-8');
481
- const parsed = safeJsonParse(packageJsonContent, packageJsonLocation.path);
482
- const packageJson = validatePackageJson(parsed, packageJsonLocation.path);
483
- if (!packageJson.name) continue;
484
- const linkedDependencies = await findLinkedDependencies(packageDir, packageJson.name, storage, logger);
485
- if (linkedDependencies.length > 0) {
486
- packageStatuses.push({
487
- name: packageJson.name,
488
- path: packageDir,
489
- linkedDependencies
490
- });
491
- }
492
- } catch (error) {
493
- logger.warn(`Failed to parse ${packageJsonLocation.path}: ${error.message}`);
494
- }
495
- }
496
- if (packageStatuses.length === 0) {
497
- return 'No linked dependencies found in workspace.';
498
- }
499
- // Format the output
500
- let output = `Found ${packageStatuses.length} package(s) with linked dependencies:\n\n`;
501
- for (const packageStatus of packageStatuses){
502
- output += `📦 ${packageStatus.name}\n`;
503
- output += ` Path: ${packageStatus.path}\n`;
504
- if (packageStatus.linkedDependencies.length > 0) {
505
- output += ` Linked dependencies:\n`;
506
- for (const dep of packageStatus.linkedDependencies){
507
- const type = dep.isExternal ? '🔗 External' : '🔗 Internal';
508
- output += ` ${type} ${dep.dependencyName} -> ${dep.targetPath}\n`;
509
- }
510
- }
511
- output += '\n';
512
- }
513
- return output;
514
- };
515
- const execute = async (runConfig, packageArgument)=>{
516
- try {
517
- var _runConfig_unlink;
518
- // Check if this is a status command from direct parameter
519
- if (packageArgument === 'status') {
520
- return await executeStatus(runConfig);
521
- }
522
- // Use packageArgument from runConfig if not provided as parameter
523
- const finalPackageArgument = packageArgument || ((_runConfig_unlink = runConfig.unlink) === null || _runConfig_unlink === void 0 ? void 0 : _runConfig_unlink.packageArgument);
524
- // Check if this is a status command from config
525
- if (finalPackageArgument === 'status') {
526
- return await executeStatus(runConfig);
527
- }
528
- return await executeInternal(runConfig, finalPackageArgument);
529
- } catch (error) {
530
- const logger = getLogger();
531
- logger.error(`unlink failed: ${error.message}`);
532
- throw error;
533
- }
534
- };
535
-
536
- export { execute, findLinkedDependencies, getSymbolicLinkTarget, isSymbolicLink, matchesExternalUnlinkPattern, parsePackageArgument, removeSymbolicLink };
537
- //# sourceMappingURL=unlink.js.map