feat: Address review comments, add stage/unstage functionality, conflict resolution improvements, support for Sonnet 4.6

This commit is contained in:
gsxdsm
2026-02-18 18:58:33 -08:00
parent df9a6314da
commit 983eb21faa
66 changed files with 2317 additions and 823 deletions

View File

@@ -50,15 +50,15 @@ describe('sdk-options.ts', () => {
describe('getModelForUseCase', () => {
it('should return explicit model when provided', async () => {
const { getModelForUseCase } = await import('@/lib/sdk-options.js');
const result = getModelForUseCase('spec', 'claude-sonnet-4-20250514');
expect(result).toBe('claude-sonnet-4-20250514');
const result = getModelForUseCase('spec', 'claude-sonnet-4-6');
expect(result).toBe('claude-sonnet-4-6');
});
it('should use environment variable for spec model', async () => {
process.env.AUTOMAKER_MODEL_SPEC = 'claude-sonnet-4-20250514';
process.env.AUTOMAKER_MODEL_SPEC = 'claude-sonnet-4-6';
const { getModelForUseCase } = await import('@/lib/sdk-options.js');
const result = getModelForUseCase('spec');
expect(result).toBe('claude-sonnet-4-20250514');
expect(result).toBe('claude-sonnet-4-6');
});
it('should use default model for spec when no override', async () => {
@@ -71,10 +71,10 @@ describe('sdk-options.ts', () => {
it('should fall back to AUTOMAKER_MODEL_DEFAULT', async () => {
delete process.env.AUTOMAKER_MODEL_SPEC;
process.env.AUTOMAKER_MODEL_DEFAULT = 'claude-sonnet-4-20250514';
process.env.AUTOMAKER_MODEL_DEFAULT = 'claude-sonnet-4-6';
const { getModelForUseCase } = await import('@/lib/sdk-options.js');
const result = getModelForUseCase('spec');
expect(result).toBe('claude-sonnet-4-20250514');
expect(result).toBe('claude-sonnet-4-6');
});
});
@@ -203,10 +203,10 @@ describe('sdk-options.ts', () => {
const options = createChatOptions({
cwd: '/test/path',
sessionModel: 'claude-sonnet-4-20250514',
sessionModel: 'claude-sonnet-4-6',
});
expect(options.model).toBe('claude-sonnet-4-20250514');
expect(options.model).toBe('claude-sonnet-4-6');
});
});

View File

@@ -360,10 +360,10 @@ describe('claude-provider.ts', () => {
});
describe('getAvailableModels', () => {
it('should return 4 Claude models', () => {
it('should return 5 Claude models', () => {
const models = provider.getAvailableModels();
expect(models).toHaveLength(4);
expect(models).toHaveLength(5);
});
it('should include Claude Opus 4.6', () => {
@@ -375,12 +375,12 @@ describe('claude-provider.ts', () => {
expect(opus?.provider).toBe('anthropic');
});
it('should include Claude Sonnet 4', () => {
it('should include Claude Sonnet 4.6', () => {
const models = provider.getAvailableModels();
const sonnet = models.find((m) => m.id === 'claude-sonnet-4-20250514');
const sonnet = models.find((m) => m.id === 'claude-sonnet-4-6');
expect(sonnet).toBeDefined();
expect(sonnet?.name).toBe('Claude Sonnet 4');
expect(sonnet?.name).toBe('Claude Sonnet 4.6');
});
it('should include Claude 3.5 Sonnet', () => {

View File

@@ -69,19 +69,19 @@ describe('opencode-provider.ts', () => {
it('should include free tier GLM model', () => {
const models = provider.getAvailableModels();
const glm = models.find((m) => m.id === 'opencode/glm-4.7-free');
const glm = models.find((m) => m.id === 'opencode/glm-5-free');
expect(glm).toBeDefined();
expect(glm?.name).toBe('GLM 4.7 Free');
expect(glm?.name).toBe('GLM 5 Free');
expect(glm?.tier).toBe('basic');
});
it('should include free tier MiniMax model', () => {
const models = provider.getAvailableModels();
const minimax = models.find((m) => m.id === 'opencode/minimax-m2.1-free');
const minimax = models.find((m) => m.id === 'opencode/minimax-m2.5-free');
expect(minimax).toBeDefined();
expect(minimax?.name).toBe('MiniMax M2.1 Free');
expect(minimax?.name).toBe('MiniMax M2.5 Free');
expect(minimax?.tier).toBe('basic');
});

View File

@@ -59,8 +59,8 @@ describe('provider-factory.ts', () => {
expect(provider).toBeInstanceOf(ClaudeProvider);
});
it('should return ClaudeProvider for claude-sonnet-4-20250514', () => {
const provider = ProviderFactory.getProviderForModel('claude-sonnet-4-20250514');
it('should return ClaudeProvider for claude-sonnet-4-6', () => {
const provider = ProviderFactory.getProviderForModel('claude-sonnet-4-6');
expect(provider).toBeInstanceOf(ClaudeProvider);
});

View File

@@ -129,7 +129,7 @@ describe('AgentExecutor', () => {
projectPath: '/project',
abortController: new AbortController(),
provider: {} as BaseProvider,
effectiveBareModel: 'claude-sonnet-4-20250514',
effectiveBareModel: 'claude-sonnet-4-6',
};
expect(options.featureId).toBe('test-feature');
});
@@ -166,7 +166,7 @@ describe('AgentExecutor', () => {
projectPath: '/test/project',
abortController: new AbortController(),
provider: {} as BaseProvider,
effectiveBareModel: 'claude-sonnet-4-20250514',
effectiveBareModel: 'claude-sonnet-4-6',
};
expect(options.workDir).toBe('/test/workdir');
@@ -174,7 +174,7 @@ describe('AgentExecutor', () => {
expect(options.prompt).toBe('Test prompt');
expect(options.projectPath).toBe('/test/project');
expect(options.abortController).toBeInstanceOf(AbortController);
expect(options.effectiveBareModel).toBe('claude-sonnet-4-20250514');
expect(options.effectiveBareModel).toBe('claude-sonnet-4-6');
});
it('should accept optional options', () => {
@@ -185,10 +185,10 @@ describe('AgentExecutor', () => {
projectPath: '/test/project',
abortController: new AbortController(),
provider: {} as BaseProvider,
effectiveBareModel: 'claude-sonnet-4-20250514',
effectiveBareModel: 'claude-sonnet-4-6',
// Optional fields
imagePaths: ['/image1.png', '/image2.png'],
model: 'claude-sonnet-4-20250514',
model: 'claude-sonnet-4-6',
planningMode: 'spec',
requirePlanApproval: true,
previousContent: 'Previous content',
@@ -419,7 +419,7 @@ describe('AgentExecutor', () => {
projectPath: '/project',
abortController,
provider: mockProvider,
effectiveBareModel: 'claude-sonnet-4-20250514',
effectiveBareModel: 'claude-sonnet-4-6',
planningMode: 'skip',
};
@@ -461,7 +461,7 @@ describe('AgentExecutor', () => {
projectPath: '/project',
abortController: new AbortController(),
provider: mockProvider,
effectiveBareModel: 'claude-sonnet-4-20250514',
effectiveBareModel: 'claude-sonnet-4-6',
previousContent: 'Previous context from earlier session',
};
@@ -507,7 +507,7 @@ describe('AgentExecutor', () => {
projectPath: '/project',
abortController: new AbortController(),
provider: mockProvider,
effectiveBareModel: 'claude-sonnet-4-20250514',
effectiveBareModel: 'claude-sonnet-4-6',
planningMode: 'skip', // No spec detection in skip mode
};
@@ -558,7 +558,7 @@ describe('AgentExecutor', () => {
projectPath: '/project',
abortController: new AbortController(),
provider: mockProvider,
effectiveBareModel: 'claude-sonnet-4-20250514',
effectiveBareModel: 'claude-sonnet-4-6',
planningMode: 'skip',
};
@@ -618,7 +618,7 @@ describe('AgentExecutor', () => {
projectPath: '/project',
abortController: new AbortController(),
provider: mockProvider,
effectiveBareModel: 'claude-sonnet-4-20250514',
effectiveBareModel: 'claude-sonnet-4-6',
planningMode: 'skip',
};
@@ -671,7 +671,7 @@ describe('AgentExecutor', () => {
projectPath: '/project',
abortController: new AbortController(),
provider: mockProvider,
effectiveBareModel: 'claude-sonnet-4-20250514',
effectiveBareModel: 'claude-sonnet-4-6',
planningMode: 'skip',
};
@@ -712,7 +712,7 @@ describe('AgentExecutor', () => {
projectPath: '/project',
abortController: new AbortController(),
provider: mockProvider,
effectiveBareModel: 'claude-sonnet-4-20250514',
effectiveBareModel: 'claude-sonnet-4-6',
planningMode: 'skip',
};
@@ -763,7 +763,7 @@ describe('AgentExecutor', () => {
projectPath: '/project',
abortController: new AbortController(),
provider: mockProvider,
effectiveBareModel: 'claude-sonnet-4-20250514',
effectiveBareModel: 'claude-sonnet-4-6',
planningMode: 'skip',
};
@@ -810,7 +810,7 @@ describe('AgentExecutor', () => {
projectPath: '/project',
abortController: new AbortController(),
provider: mockProvider,
effectiveBareModel: 'claude-sonnet-4-20250514',
effectiveBareModel: 'claude-sonnet-4-6',
planningMode: 'skip',
};
@@ -855,7 +855,7 @@ describe('AgentExecutor', () => {
projectPath: '/project',
abortController: new AbortController(),
provider: mockProvider,
effectiveBareModel: 'claude-sonnet-4-20250514',
effectiveBareModel: 'claude-sonnet-4-6',
planningMode: 'skip',
branchName: 'feature/my-feature',
};
@@ -906,7 +906,7 @@ describe('AgentExecutor', () => {
projectPath: '/project',
abortController: new AbortController(),
provider: mockProvider,
effectiveBareModel: 'claude-sonnet-4-20250514',
effectiveBareModel: 'claude-sonnet-4-6',
planningMode: 'skip',
};

View File

@@ -272,10 +272,10 @@ describe('agent-service.ts', () => {
await service.sendMessage({
sessionId: 'session-1',
message: 'Hello',
model: 'claude-sonnet-4-20250514',
model: 'claude-sonnet-4-6',
});
expect(ProviderFactory.getProviderForModel).toHaveBeenCalledWith('claude-sonnet-4-20250514');
expect(ProviderFactory.getProviderForModel).toHaveBeenCalledWith('claude-sonnet-4-6');
});
it('should save session messages', async () => {
@@ -339,7 +339,10 @@ describe('agent-service.ts', () => {
it('should handle non-existent session', async () => {
const history = await service.getHistory('nonexistent');
expect(history).toBeDefined(); // Returns error object
expect(history).toBeDefined();
expect(history.success).toBe(false);
expect(history.error).toBeDefined();
expect(typeof history.error).toBe('string');
});
});
@@ -530,13 +533,13 @@ describe('agent-service.ts', () => {
it('should set model for existing session', async () => {
vi.mocked(fs.readFile).mockResolvedValue('{"session-1": {}}');
const result = await service.setSessionModel('session-1', 'claude-sonnet-4-20250514');
const result = await service.setSessionModel('session-1', 'claude-sonnet-4-6');
expect(result).toBe(true);
});
it('should return false for non-existent session', async () => {
const result = await service.setSessionModel('nonexistent', 'claude-sonnet-4-20250514');
const result = await service.setSessionModel('nonexistent', 'claude-sonnet-4-6');
expect(result).toBe(false);
});
@@ -719,7 +722,7 @@ describe('agent-service.ts', () => {
const result = await service.addToQueue('session-1', {
message: 'Test prompt',
imagePaths: ['/test/image.png'],
model: 'claude-sonnet-4-20250514',
model: 'claude-sonnet-4-6',
});
expect(result.success).toBe(true);

View File

@@ -25,7 +25,7 @@ const mockLogger = vi.hoisted(() => ({
const mockCreateChatOptions = vi.hoisted(() =>
vi.fn(() => ({
model: 'claude-sonnet-4-20250514',
model: 'claude-sonnet-4-6',
systemPrompt: 'test prompt',
}))
);