Skip to content

Fix case-sensitive client-name matching in hyp attach/detach (#300)#301

Merged
philcunliffe merged 1 commit into
masterfrom
fix/issue-300
Jul 14, 2026
Merged

Fix case-sensitive client-name matching in hyp attach/detach (#300)#301
philcunliffe merged 1 commit into
masterfrom
fix/issue-300

Conversation

@philcunliffe

Copy link
Copy Markdown
Contributor

Problem

hyp attach Claude failed with error: unknown client 'Claude'. Registered clients: claude, codex. Client-name matching was case-sensitive end to end. The products are branded "Claude"/"Codex" with capitals, so the mixed-case token is a very plausible user mistake. The issue (#300) also calls out hyp detach Claude (a different resolution path) and the hyp attach ALL sentinel.

Root cause (verified)

parseClientArgs in src/core/commands/clients.js stored the positional / --client token verbatim (default 'claude'). Downstream:

  • attach resolves against the live gateway registry getClient(name) (state.clients.get(name)); the adapter registers lowercase (CLIENT_NAME = 'claude').
  • detach resolves against the client-descriptor map, keyed by lowercase manifest names - a different path, as the issue notes.
  • the all sentinel is an exact === 'all' compare in both expandClientName and expandDetachClientNames.

Both runAttach and runDetach route through runClientLifecycle, which parses the token once via parseClientArgs/setClient. So lowercasing the user-supplied token in setClient normalizes all three paths in one spot. Confirmed the shared code path by reading the source: detach's descriptor map is built by buildClientDescriptorMap, which returns keys ['claude','codex'], so map.get('Claude') missed pre-fix.

Fix

Lowercase the user token once in setClient (inside parseClientArgs). Adapter registration is unchanged. The multiple-specified guard and error text now compare the normalized token.

Tests (fail -> pass)

New test/core/client-name-case-insensitive.test.js:

  1. attach Claude resolves to the lowercase claude adapter (attach runs, exit 0).
  2. attach ALL expands to every registered client (claude, codex).
  3. detach Claude resolves via the client-descriptor map (no-op success on a temp HOME, not unknown client).
  4. Control: attach claude (already lowercase) is unchanged.

On unmodified code, tests 1-3 fail with unknown client / exit 1; the control passes. After the fix all four pass. Full npm test is green (2156 pass, 0 fail, 1 pre-existing skip).

Covers attach, detach, and the ALL sentinel as required.

🤖 Generated with Claude Code

Fixes #300

`hyp attach Claude` (and `hyp detach Claude`, `hyp attach ALL`) failed with
`unknown client 'Claude'` because the client token was matched case-sensitively
end to end. The products are branded "Claude"/"Codex" with capitals, so the
mixed-case token is a very plausible user mistake.

Both `runAttach` and `runDetach` route through `runClientLifecycle`, which
parses the token once via `parseClientArgs`/`setClient`. Lowercasing the
user-supplied token there normalizes all three resolution paths in one spot:
attach's live gateway registry (`getClient`), detach's client-descriptor map
(keyed by lowercase manifest names), and the lowercase `all` sentinel. Adapter
registration (`CLIENT_NAME = 'claude'`) is unchanged.

Adds a regression test that fails on the prior code (attach Claude, attach ALL,
detach Claude all exit 1 `unknown client`) and passes after the fix, plus an
already-lowercase control to prove behavior is preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@philcunliffe

Copy link
Copy Markdown
Contributor Author

neutral PR-review round 1 — PR #301 — clean

Path: dual-review (Codex + Claude, both ran, converged). Verdict: approve. Risk class: low (16-line fix in the CLI arg parser + a 131-line regression test; no daemon/gateway/persistence surface).

Verified (not trusting self-report)

  • Single lowercase spot covers all three cases. Both runAttach and runDetach route through runClientLifecycleparseClientArgs and consume the normalized parsed.client: attach via expandClientNamegateway.getClient (adapters register lowercase CLIENT_NAME); detach via expandDetachClientNames → descriptor map keyed by lowercase manifest names (buildClientDescriptorMap); the all/ALL sentinel via requested === 'all'. Nothing downstream re-reads a raw un-normalized value.
  • No over-normalization. setClient only ever receives the positional/--client client token; --dry-run/--json are handled separately and no path/profile/non-client arg passes through it. Lowercasing is safe.
  • Other callers unaffected (parseSkillsArgs is separate; walkthrough/integration.js pass lowercase).
  • Tests genuinely gate the fix: reverting the diff, 3 of 4 new tests fail on old code (attach Claude, attach ALL, detach Claude) and the attach claude control passes on both; all 4 pass on the new code. The detach test exercises the real descriptor-map path. Full suite green (2160 pass, 1 skip).
  • Conventions clean (no semicolons, no em dashes).

Findings

None (no blocker/major/minor).

Non-blocking, not actionable for this PR

  • Trivial extra-coverage: detach Codex/CoDeX not directly tested, but the normalization is one shared point already exercised by the claude/ALL cases.
  • Out of scope (pre-existing): hyp skills install --client Claude remains case-sensitive (parseSkillsArgs) — unrelated to hyp attach/detach client names are case-sensitive: 'hyp attach Claude' fails #300, not introduced here.
  • Forward note (theoretical): a future adapter with a non-lowercase CLIENT_NAME would be CLI-unreachable; all current adapters are lowercase.

Marker = clean → reviewed rung satisfied at the current head; PR is terminal (mergeable ∧ green ∧ reviewed) and holds for a human.

@philcunliffe philcunliffe added the neutral:approved neutral reviewed this and holds it for a maintainer merge (own or adopted PR; LLP 0025/0030) label Jul 14, 2026
@philcunliffe philcunliffe merged commit 3766f50 into master Jul 14, 2026
4 checks passed
@philcunliffe philcunliffe deleted the fix/issue-300 branch July 14, 2026 19:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

neutral:approved neutral reviewed this and holds it for a maintainer merge (own or adopted PR; LLP 0025/0030)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

hyp attach/detach client names are case-sensitive: 'hyp attach Claude' fails

1 participant