Two repos both named app (~/work/app/.mcp.json, ~/personal/app/.mcp.json) both rendered as "Project: app" in the picker — the paths dedupe fine, but the labels were indistinguishable, so picking the wrong one meant editing/backing-up/writing the wrong repo's config. api/web/app/server/client make this common.
The fix (pure + tested):
disambiguate_project_labels(dirs) — labels stay Project: <name> until a basename collides, then only the colliding ones widen toward the root a component at a time (Project: work/app vs Project: personal/app), widening further if the parent also collides. No-collision case unchanged.
Full path in the combo item's ToolTipRole so a profile is always verifiable on hover.
Tightened discovery: the docstring claimed a top-level object but the code only checked is_file(), so a .mcp.json that was a JSON array or garbage still became a profile and only failed on open. It now must strict-parse to a dict, else it's skipped.
Tests: +6 (basename / one-level widen / deeper widen, plus discover_project_configs disambiguation and skipping array/garbage/missing). 463 passed, ruff clean. GUI change is one setItemData tooltip line.
Closes #74.
Two repos both named `app` (`~/work/app/.mcp.json`, `~/personal/app/.mcp.json`) both rendered as **"Project: app"** in the picker — the paths dedupe fine, but the labels were indistinguishable, so picking the wrong one meant editing/backing-up/writing the wrong repo's config. `api`/`web`/`app`/`server`/`client` make this common.
**The fix (pure + tested):**
- `disambiguate_project_labels(dirs)` — labels stay `Project: <name>` until a basename collides, then **only the colliding ones** widen toward the root a component at a time (`Project: work/app` vs `Project: personal/app`), widening further if the parent also collides. No-collision case unchanged.
- Full path in the combo item's `ToolTipRole` so a profile is always verifiable on hover.
- Tightened discovery: the docstring claimed a top-level object but the code only checked `is_file()`, so a `.mcp.json` that was a JSON array or garbage still became a profile and only failed on open. It now must strict-parse to a dict, else it's skipped.
**Tests:** +6 (basename / one-level widen / deeper widen, plus `discover_project_configs` disambiguation and skipping array/garbage/missing). 463 passed, ruff clean. GUI change is one `setItemData` tooltip line.
discover_project_configs labelled every project by basename alone, so
~/work/app/.mcp.json and ~/personal/app/.mcp.json both read as
"Project: app". Paths dedupe correctly, so both profiles existed -- they
were just indistinguishable in the picker, and picking the wrong one meant
editing, backing up, and writing the wrong repo's config. api/web/app/
server/client as repo names make this common.
- disambiguate_project_labels(dirs): pure, testable. Labels stay
"Project: <name>" until a basename collides, then only the colliding
ones widen toward the root one component at a time
("Project: work/app" vs "Project: personal/app"), widening further if the
parent also collides. The common no-collision case is unchanged.
- Full path goes in the combo item's ToolTipRole, so a profile is always
verifiable by hover regardless of label.
- Tightened the "is this a project config?" check: the docstring claimed a
top-level object but the code only checked is_file(), so a .mcp.json that
was a JSON array or garbage still became a profile and only failed on
open. It now must parse (strict) to a dict, else it's skipped.
Tests: +6 (no-collision basename, colliding widen, deeper widen, and
discover_project_configs disambiguation + skipping array/garbage/missing).
463 passed, ruff clean.
Closes#74
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EKwBecy6N83jnqQmw8ezwE
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #74.
Two repos both named
app(~/work/app/.mcp.json,~/personal/app/.mcp.json) both rendered as "Project: app" in the picker — the paths dedupe fine, but the labels were indistinguishable, so picking the wrong one meant editing/backing-up/writing the wrong repo's config.api/web/app/server/clientmake this common.The fix (pure + tested):
disambiguate_project_labels(dirs)— labels stayProject: <name>until a basename collides, then only the colliding ones widen toward the root a component at a time (Project: work/appvsProject: personal/app), widening further if the parent also collides. No-collision case unchanged.ToolTipRoleso a profile is always verifiable on hover.is_file(), so a.mcp.jsonthat was a JSON array or garbage still became a profile and only failed on open. It now must strict-parse to a dict, else it's skipped.Tests: +6 (basename / one-level widen / deeper widen, plus
discover_project_configsdisambiguation and skipping array/garbage/missing). 463 passed, ruff clean. GUI change is onesetItemDatatooltip line.discover_project_configs labelled every project by basename alone, so ~/work/app/.mcp.json and ~/personal/app/.mcp.json both read as "Project: app". Paths dedupe correctly, so both profiles existed -- they were just indistinguishable in the picker, and picking the wrong one meant editing, backing up, and writing the wrong repo's config. api/web/app/ server/client as repo names make this common. - disambiguate_project_labels(dirs): pure, testable. Labels stay "Project: <name>" until a basename collides, then only the colliding ones widen toward the root one component at a time ("Project: work/app" vs "Project: personal/app"), widening further if the parent also collides. The common no-collision case is unchanged. - Full path goes in the combo item's ToolTipRole, so a profile is always verifiable by hover regardless of label. - Tightened the "is this a project config?" check: the docstring claimed a top-level object but the code only checked is_file(), so a .mcp.json that was a JSON array or garbage still became a profile and only failed on open. It now must parse (strict) to a dict, else it's skipped. Tests: +6 (no-collision basename, colliding widen, deeper widen, and discover_project_configs disambiguation + skipping array/garbage/missing). 463 passed, ruff clean. Closes #74 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EKwBecy6N83jnqQmw8ezwEthe_og referenced this pull request2026-08-03 23:39:29 -04:00