#1025: agents naturally pass the repo FOLDER name ("codebase-memory-mcp"), * but indexed project names derive from the full path * (E:\project\graph\x -> "E-project-graph-x"), so the exact lookup fails * while list_projects clearly shows the project. When no .db exists, * scan cache-dir FILENAMES for a segment-aligned tail match ("- .db"): * exactly one match adopts the full
| 1439 | * the existing not-found error (which lists all candidates) fires. Filename- |
| 1440 | * level only — internal-name drift stays #704's fallback in resolve_store. */ |
| 1441 | static char *resolve_project_tail(char *project) { |
| 1442 | if (!project || !cbm_validate_project_name(project)) { |
| 1443 | return project; |
| 1444 | } |
| 1445 | char dir[CBM_SZ_1K]; |
| 1446 | cache_dir(dir, sizeof(dir)); |
| 1447 | char exact[CBM_SZ_2K]; |
| 1448 | snprintf(exact, sizeof(exact), "%s/%s.db", dir, project); |
| 1449 | if (cbm_file_exists(exact)) { |
| 1450 | return project; /* exact name — untouched fast path */ |
| 1451 | } |
| 1452 | size_t plen = strlen(project); |
| 1453 | char match[CBM_SZ_1K] = ""; |
| 1454 | int matches = 0; |
| 1455 | cbm_dir_t *d = cbm_opendir(dir); |
| 1456 | if (!d) { |
| 1457 | return project; |
| 1458 | } |
| 1459 | cbm_dirent_t *entry; |
| 1460 | while ((entry = cbm_readdir(d)) != NULL) { |
| 1461 | const char *n = entry->name; |
| 1462 | size_t len = strlen(n); |
| 1463 | if (!is_project_db_file(n, len)) { |
| 1464 | continue; |
| 1465 | } |
| 1466 | size_t stem_len = len - MCP_DB_EXT; /* strip ".db" */ |
| 1467 | if (stem_len <= plen + 1 || stem_len >= sizeof(match)) { |
| 1468 | continue; |
| 1469 | } |
| 1470 | if (n[stem_len - plen - 1] != '-' || strncmp(n + stem_len - plen, project, plen) != 0) { |
| 1471 | continue; |
| 1472 | } |
| 1473 | matches++; |
| 1474 | if (matches > 1) { |
| 1475 | break; /* ambiguous — keep the original name */ |
| 1476 | } |
| 1477 | memcpy(match, n, stem_len); |
| 1478 | match[stem_len] = '\0'; |
| 1479 | } |
| 1480 | cbm_closedir(d); |
| 1481 | if (matches == 1) { |
| 1482 | cbm_log_info("mcp.project_tail_resolved", "passed", project, "resolved", match); |
| 1483 | free(project); |
| 1484 | return heap_strdup(match); |
| 1485 | } |
| 1486 | return project; |
| 1487 | } |
| 1488 | |
| 1489 | /* Resolve the project argument, accepting the canonical "project" key plus the |
| 1490 | * aliases a caller naturally reaches for (#640): list_projects surfaces the |
no test coverage detected