feat: add runtime provider/model switching via /model <tier> <provider/model>

- ModelRouter: add setClient(), labels map, getLabel(), getAllLabels()
- TUI commands: parse /model <tier> <provider/model> syntax with autocompletion
- TUI minimal: handle provider switching via createClientFromConfig factory
- Daemon: wire initial labels into router config
- Fix /model alias mappings (opus=complex, sonnet=default, haiku=fast)
- Add design doc and update state.json with feature status
This commit is contained in:
William Valentin
2026-02-06 23:42:14 -08:00
parent e92ce69067
commit d4530a7034
8 changed files with 527 additions and 37 deletions
+26
View File
@@ -43,6 +43,32 @@ describe('parseCommand', () => {
expect(parseCommand('/model opus')).toEqual({ type: 'model', name: 'opus' });
});
it('parses /model with provider/model', () => {
expect(parseCommand('/model default anthropic/claude-sonnet-4')).toEqual({
type: 'model',
name: 'default',
providerModel: 'anthropic/claude-sonnet-4',
});
expect(parseCommand('/model fast github-copilot/gpt-4o-mini')).toEqual({
type: 'model',
name: 'fast',
providerModel: 'github-copilot/gpt-4o-mini',
});
expect(parseCommand('/model complex openai/o3')).toEqual({
type: 'model',
name: 'complex',
providerModel: 'openai/o3',
});
});
it('still parses /model fast as tier switch (no providerModel)', () => {
expect(parseCommand('/model fast')).toEqual({ type: 'model', name: 'fast' });
});
it('still parses /model as info (no args)', () => {
expect(parseCommand('/model')).toEqual({ type: 'model' });
});
it('parses /backend command without argument', () => {
expect(parseCommand('/backend')).toEqual({ type: 'backend' });
});