mirror of
https://github.com/jarrodwatts/claude-hud.git
synced 2026-04-19 17:02:42 +00:00
feat: add display.modelOverride for custom model display names
Users can now set a fully custom model name in their config:
{ "display": { "modelOverride": "zane's intelligent opus 4.6" } }
When set, the override completely replaces the auto-detected model
name (while preserving the provider qualifier like "| Bedrock").
Follows the same pattern as customLine: string type, max 80 chars,
empty string means disabled (falls through to modelFormat).
This commit is contained in:
@@ -90,6 +90,7 @@ export interface HudConfig {
|
||||
sevenDayThreshold: number;
|
||||
environmentThreshold: number;
|
||||
modelFormat: ModelFormatMode;
|
||||
modelOverride: string;
|
||||
customLine: string;
|
||||
};
|
||||
colors: HudColorOverrides;
|
||||
@@ -128,6 +129,7 @@ export const DEFAULT_CONFIG: HudConfig = {
|
||||
sevenDayThreshold: 80,
|
||||
environmentThreshold: 0,
|
||||
modelFormat: 'full',
|
||||
modelOverride: '',
|
||||
customLine: '',
|
||||
},
|
||||
colors: {
|
||||
@@ -340,6 +342,9 @@ export function mergeConfig(userConfig: Partial<HudConfig>): HudConfig {
|
||||
modelFormat: validateModelFormat(migrated.display?.modelFormat)
|
||||
? migrated.display.modelFormat
|
||||
: DEFAULT_CONFIG.display.modelFormat,
|
||||
modelOverride: typeof migrated.display?.modelOverride === 'string'
|
||||
? migrated.display.modelOverride.slice(0, 80)
|
||||
: DEFAULT_CONFIG.display.modelOverride,
|
||||
customLine: typeof migrated.display?.customLine === 'string'
|
||||
? migrated.display.customLine.slice(0, 80)
|
||||
: DEFAULT_CONFIG.display.customLine,
|
||||
|
||||
@@ -9,7 +9,7 @@ export function renderProjectLine(ctx: RenderContext): string | null {
|
||||
const parts: string[] = [];
|
||||
|
||||
if (display?.showModel !== false) {
|
||||
const model = formatModelName(getModelName(ctx.stdin), ctx.config?.display?.modelFormat);
|
||||
const model = formatModelName(getModelName(ctx.stdin), ctx.config?.display?.modelFormat, ctx.config?.display?.modelOverride);
|
||||
const providerLabel = getProviderLabel(ctx.stdin);
|
||||
const showUsage = display?.showUsage !== false;
|
||||
const hasApiKey = !!process.env.ANTHROPIC_API_KEY;
|
||||
|
||||
@@ -12,7 +12,7 @@ const DEBUG = process.env.DEBUG?.includes('claude-hud') || process.env.DEBUG ===
|
||||
* Used for compact layout mode.
|
||||
*/
|
||||
export function renderSessionLine(ctx: RenderContext): string {
|
||||
const model = formatModelName(getModelName(ctx.stdin), ctx.config?.display?.modelFormat);
|
||||
const model = formatModelName(getModelName(ctx.stdin), ctx.config?.display?.modelFormat, ctx.config?.display?.modelOverride);
|
||||
|
||||
const rawPercent = getContextPercent(ctx.stdin);
|
||||
const bufferedPercent = getBufferedPercent(ctx.stdin);
|
||||
|
||||
11
src/stdin.ts
11
src/stdin.ts
@@ -167,13 +167,20 @@ export function stripContextSuffix(name: string): string {
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats a model name according to the user's chosen display format.
|
||||
* Formats a model name according to the user's chosen display settings.
|
||||
*
|
||||
* When `override` is set, it replaces the model name entirely.
|
||||
* Otherwise, `format` controls how the raw name is abbreviated:
|
||||
*
|
||||
* full: Return raw name unchanged (e.g. "Opus 4.6 (1M context)")
|
||||
* compact: Strip context-window suffix (e.g. "Opus 4.6")
|
||||
* short: Strip context suffix AND leading "Claude " prefix (e.g. "Opus 4.6")
|
||||
*/
|
||||
export function formatModelName(name: string, format?: ModelFormatMode): string {
|
||||
export function formatModelName(name: string, format?: ModelFormatMode, override?: string): string {
|
||||
if (override) {
|
||||
return override;
|
||||
}
|
||||
|
||||
if (!format || format === 'full') {
|
||||
return name;
|
||||
}
|
||||
|
||||
@@ -135,6 +135,24 @@ test('mergeConfig falls back to full for invalid modelFormat', () => {
|
||||
assert.equal(mergeConfig({ display: { modelFormat: null } }).display.modelFormat, 'full');
|
||||
});
|
||||
|
||||
test('mergeConfig defaults modelOverride to empty string', () => {
|
||||
const config = mergeConfig({});
|
||||
assert.equal(config.display.modelOverride, '');
|
||||
});
|
||||
|
||||
test('mergeConfig preserves modelOverride and truncates long values', () => {
|
||||
const override = 'x'.repeat(120);
|
||||
const config = mergeConfig({ display: { modelOverride: override } });
|
||||
assert.equal(config.display.modelOverride.length, 80);
|
||||
assert.equal(config.display.modelOverride, override.slice(0, 80));
|
||||
});
|
||||
|
||||
test('mergeConfig falls back to empty for non-string modelOverride', () => {
|
||||
assert.equal(mergeConfig({ display: { modelOverride: 123 } }).display.modelOverride, '');
|
||||
assert.equal(mergeConfig({ display: { modelOverride: null } }).display.modelOverride, '');
|
||||
assert.equal(mergeConfig({ display: { modelOverride: true } }).display.modelOverride, '');
|
||||
});
|
||||
|
||||
test('getConfigPath respects CLAUDE_CONFIG_DIR', async () => {
|
||||
const originalConfigDir = process.env.CLAUDE_CONFIG_DIR;
|
||||
const customConfigDir = await mkdtemp(path.join(tmpdir(), 'claude-hud-config-dir-'));
|
||||
|
||||
@@ -319,6 +319,17 @@ test('formatModelName short mode strips context suffix and Claude prefix', () =>
|
||||
assert.equal(formatModelName('claude Opus 4.5', 'short'), 'Opus 4.5');
|
||||
});
|
||||
|
||||
test('formatModelName override replaces model name entirely', () => {
|
||||
// Override takes precedence over format
|
||||
assert.equal(formatModelName('Claude Opus 4.5', 'full', "zane's intelligent opus"), "zane's intelligent opus");
|
||||
assert.equal(formatModelName('Claude Opus 4.5', 'compact', 'My Model'), 'My Model');
|
||||
assert.equal(formatModelName('Claude Opus 4.5', 'short', 'Custom'), 'Custom');
|
||||
assert.equal(formatModelName('Claude Opus 4.5', undefined, 'Override'), 'Override');
|
||||
// Empty override is treated as unset (falls through to format)
|
||||
assert.equal(formatModelName('Claude Opus 4.5 (1M context)', 'compact', ''), 'Claude Opus 4.5');
|
||||
assert.equal(formatModelName('Opus 4.6', 'full', ''), 'Opus 4.6');
|
||||
});
|
||||
|
||||
test('bedrock model detection recognizes bedrock ids', () => {
|
||||
assert.ok(isBedrockModelId('anthropic.claude-3-5-sonnet-20240620-v1:0'));
|
||||
assert.ok(isBedrockModelId('eu.anthropic.claude-opus-4-5-20251101-v1:0'));
|
||||
|
||||
Reference in New Issue
Block a user