ucn 3.8.14 → 3.8.15

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 (2) hide show
  1. package/core/imports.js +50 -16
  2. package/package.json +1 -1
package/core/imports.js CHANGED
@@ -194,7 +194,26 @@ function findGoModule(startDir) {
194
194
  // Parse module line: module github.com/user/project
195
195
  const match = content.match(/^module\s+(\S+)/m);
196
196
  if (match) {
197
- const result = { modulePath: match[1], root: dir };
197
+ // Parse replace directives for local module redirects
198
+ // e.g., k8s.io/api => ./staging/src/k8s.io/api
199
+ const replaces = [];
200
+ // Block form: replace ( ... )
201
+ const replaceBlock = content.match(/^replace\s*\(([\s\S]*?)\)/m);
202
+ if (replaceBlock) {
203
+ for (const line of replaceBlock[1].split('\n')) {
204
+ const rm = line.match(/^\s*(\S+)\s.*?=>\s*(\S+)/);
205
+ if (rm && rm[2].startsWith('.')) {
206
+ replaces.push({ from: rm[1], to: path.resolve(dir, rm[2]) });
207
+ }
208
+ }
209
+ }
210
+ // Single-line form: replace k8s.io/foo => ./bar
211
+ for (const rm of content.matchAll(/^replace\s+(\S+)\s.*?=>\s*(\S+)/gm)) {
212
+ if (rm[2].startsWith('.')) {
213
+ replaces.push({ from: rm[1], to: path.resolve(dir, rm[2]) });
214
+ }
215
+ }
216
+ const result = { modulePath: match[1], root: dir, replaces };
198
217
  goModuleCache.set(startDir, result);
199
218
  return result;
200
219
  }
@@ -209,6 +228,25 @@ function findGoModule(startDir) {
209
228
  return null;
210
229
  }
211
230
 
231
+ /**
232
+ * Find the first non-test .go file in a directory (Go packages are directories).
233
+ * @param {string} pkgDir - Absolute path to the package directory
234
+ * @returns {string|null}
235
+ */
236
+ function findFirstGoFile(pkgDir) {
237
+ try {
238
+ if (fs.existsSync(pkgDir) && fs.statSync(pkgDir).isDirectory()) {
239
+ const files = fs.readdirSync(pkgDir).sort();
240
+ for (const file of files) {
241
+ if (file.endsWith('.go') && !file.endsWith('_test.go')) {
242
+ return path.join(pkgDir, file);
243
+ }
244
+ }
245
+ }
246
+ } catch (e) { /* ignore */ }
247
+ return null;
248
+ }
249
+
212
250
  /**
213
251
  * Resolve Go package import to local files
214
252
  * @param {string} importPath - Go import path (e.g., "github.com/user/proj/pkg/util")
@@ -224,23 +262,19 @@ function resolveGoImport(importPath, fromFile, projectRoot) {
224
262
 
225
263
  // Check if the import is within this module
226
264
  if (importPath === modulePath || importPath.startsWith(modulePath + '/')) {
227
- // Convert module path to relative path
228
- // e.g., "github.com/user/proj/pkg/util" -> "pkg/util"
229
265
  const relativePath = importPath.slice(modulePath.length).replace(/^\//, '');
230
- const pkgDir = path.join(root, relativePath);
266
+ const resolved = findFirstGoFile(path.join(root, relativePath));
267
+ if (resolved) return resolved;
268
+ }
231
269
 
232
- // Go imports are directories, find a .go file in the directory
233
- if (fs.existsSync(pkgDir) && fs.statSync(pkgDir).isDirectory()) {
234
- // Return the first .go file in the directory (not _test.go)
235
- try {
236
- const files = fs.readdirSync(pkgDir).sort();
237
- for (const file of files) {
238
- if (file.endsWith('.go') && !file.endsWith('_test.go')) {
239
- return path.join(pkgDir, file);
240
- }
241
- }
242
- } catch (e) {
243
- // Ignore read errors
270
+ // Check replace directives (e.g., k8s.io/api => ./staging/src/k8s.io/api)
271
+ if (goMod.replaces) {
272
+ for (const { from, to } of goMod.replaces) {
273
+ if (importPath === from || importPath.startsWith(from + '/')) {
274
+ const relativePath = importPath.slice(from.length).replace(/^\//, '');
275
+ const resolved = findFirstGoFile(path.join(to, relativePath));
276
+ if (resolved) return resolved;
277
+ break;
244
278
  }
245
279
  }
246
280
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ucn",
3
- "version": "3.8.14",
3
+ "version": "3.8.15",
4
4
  "mcpName": "io.github.mleoca/ucn",
5
5
  "description": "Code intelligence toolkit for AI agents — extract functions, trace call chains, find callers, detect dead code without reading entire files. Works as MCP server, CLI, or agent skill. Supports JS/TS, Python, Go, Rust, Java.",
6
6
  "main": "index.js",