mirror of
https://github.com/AutoMaker-Org/automaker.git
synced 2026-02-03 21:03:08 +00:00
feat(worktree): add AI commit message generation feature
- Implemented a new endpoint to generate commit messages based on git diffs. - Updated worktree routes to include the AI commit message generation functionality. - Enhanced the UI to support automatic generation of commit messages when the commit dialog opens, based on user settings. - Added settings for enabling/disabling AI-generated commit messages and configuring the model used for generation.
This commit is contained in:
@@ -217,7 +217,7 @@ app.use('/api/sessions', createSessionsRoutes(agentService));
|
|||||||
app.use('/api/features', createFeaturesRoutes(featureLoader));
|
app.use('/api/features', createFeaturesRoutes(featureLoader));
|
||||||
app.use('/api/auto-mode', createAutoModeRoutes(autoModeService));
|
app.use('/api/auto-mode', createAutoModeRoutes(autoModeService));
|
||||||
app.use('/api/enhance-prompt', createEnhancePromptRoutes(settingsService));
|
app.use('/api/enhance-prompt', createEnhancePromptRoutes(settingsService));
|
||||||
app.use('/api/worktree', createWorktreeRoutes(events));
|
app.use('/api/worktree', createWorktreeRoutes(events, settingsService));
|
||||||
app.use('/api/git', createGitRoutes());
|
app.use('/api/git', createGitRoutes());
|
||||||
app.use('/api/suggestions', createSuggestionsRoutes(events, settingsService));
|
app.use('/api/suggestions', createSuggestionsRoutes(events, settingsService));
|
||||||
app.use('/api/models', createModelsRoutes());
|
app.use('/api/models', createModelsRoutes());
|
||||||
|
|||||||
@@ -40,8 +40,12 @@ import {
|
|||||||
createDeleteInitScriptHandler,
|
createDeleteInitScriptHandler,
|
||||||
createRunInitScriptHandler,
|
createRunInitScriptHandler,
|
||||||
} from './routes/init-script.js';
|
} from './routes/init-script.js';
|
||||||
|
import type { SettingsService } from '../../services/settings-service.js';
|
||||||
|
|
||||||
export function createWorktreeRoutes(events: EventEmitter): Router {
|
export function createWorktreeRoutes(
|
||||||
|
events: EventEmitter,
|
||||||
|
settingsService?: SettingsService
|
||||||
|
): Router {
|
||||||
const router = Router();
|
const router = Router();
|
||||||
|
|
||||||
router.post('/info', validatePathParams('projectPath'), createInfoHandler());
|
router.post('/info', validatePathParams('projectPath'), createInfoHandler());
|
||||||
@@ -69,7 +73,7 @@ export function createWorktreeRoutes(events: EventEmitter): Router {
|
|||||||
'/generate-commit-message',
|
'/generate-commit-message',
|
||||||
validatePathParams('worktreePath'),
|
validatePathParams('worktreePath'),
|
||||||
requireGitRepoOnly,
|
requireGitRepoOnly,
|
||||||
createGenerateCommitMessageHandler()
|
createGenerateCommitMessageHandler(settingsService)
|
||||||
);
|
);
|
||||||
router.post(
|
router.post(
|
||||||
'/push',
|
'/push',
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
/**
|
/**
|
||||||
* POST /worktree/generate-commit-message endpoint - Generate an AI commit message from git diff
|
* POST /worktree/generate-commit-message endpoint - Generate an AI commit message from git diff
|
||||||
*
|
*
|
||||||
* Uses Claude Haiku to generate a concise, conventional commit message from git changes.
|
* Uses the configured model (via phaseModels.commitMessageModel) to generate a concise,
|
||||||
|
* conventional commit message from git changes. Defaults to Claude Haiku for speed.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Request, Response } from 'express';
|
import type { Request, Response } from 'express';
|
||||||
@@ -9,12 +10,26 @@ import { exec } from 'child_process';
|
|||||||
import { promisify } from 'util';
|
import { promisify } from 'util';
|
||||||
import { query } from '@anthropic-ai/claude-agent-sdk';
|
import { query } from '@anthropic-ai/claude-agent-sdk';
|
||||||
import { createLogger } from '@automaker/utils';
|
import { createLogger } from '@automaker/utils';
|
||||||
import { CLAUDE_MODEL_MAP } from '@automaker/model-resolver';
|
import { DEFAULT_PHASE_MODELS, isCursorModel, stripProviderPrefix } from '@automaker/types';
|
||||||
|
import { resolvePhaseModel } from '@automaker/model-resolver';
|
||||||
|
import { mergeCommitMessagePrompts } from '@automaker/prompts';
|
||||||
|
import { ProviderFactory } from '../../../providers/provider-factory.js';
|
||||||
|
import type { SettingsService } from '../../../services/settings-service.js';
|
||||||
import { getErrorMessage, logError } from '../common.js';
|
import { getErrorMessage, logError } from '../common.js';
|
||||||
|
|
||||||
const logger = createLogger('GenerateCommitMessage');
|
const logger = createLogger('GenerateCommitMessage');
|
||||||
const execAsync = promisify(exec);
|
const execAsync = promisify(exec);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the effective system prompt for commit message generation.
|
||||||
|
* Uses custom prompt from settings if enabled, otherwise falls back to default.
|
||||||
|
*/
|
||||||
|
async function getSystemPrompt(settingsService?: SettingsService): Promise<string> {
|
||||||
|
const settings = await settingsService?.getGlobalSettings();
|
||||||
|
const prompts = mergeCommitMessagePrompts(settings?.promptCustomization?.commitMessage);
|
||||||
|
return prompts.systemPrompt;
|
||||||
|
}
|
||||||
|
|
||||||
interface GenerateCommitMessageRequestBody {
|
interface GenerateCommitMessageRequestBody {
|
||||||
worktreePath: string;
|
worktreePath: string;
|
||||||
}
|
}
|
||||||
@@ -29,23 +44,6 @@ interface GenerateCommitMessageErrorResponse {
|
|||||||
error: string;
|
error: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SYSTEM_PROMPT = `You are a git commit message generator. Your task is to create a clear, concise commit message based on the git diff provided.
|
|
||||||
|
|
||||||
Rules:
|
|
||||||
- Output ONLY the commit message, nothing else
|
|
||||||
- First line should be a short summary (50 chars or less) in imperative mood
|
|
||||||
- Start with a conventional commit type if appropriate (feat:, fix:, refactor:, docs:, etc.)
|
|
||||||
- Keep it concise and descriptive
|
|
||||||
- Focus on WHAT changed and WHY (if clear from the diff), not HOW
|
|
||||||
- No quotes, backticks, or extra formatting
|
|
||||||
- If there are multiple changes, provide a brief summary on the first line
|
|
||||||
|
|
||||||
Examples:
|
|
||||||
- feat: Add dark mode toggle to settings
|
|
||||||
- fix: Resolve login validation edge case
|
|
||||||
- refactor: Extract user authentication logic
|
|
||||||
- docs: Update installation instructions`;
|
|
||||||
|
|
||||||
async function extractTextFromStream(
|
async function extractTextFromStream(
|
||||||
stream: AsyncIterable<{
|
stream: AsyncIterable<{
|
||||||
type: string;
|
type: string;
|
||||||
@@ -73,10 +71,9 @@ async function extractTextFromStream(
|
|||||||
return responseText;
|
return responseText;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createGenerateCommitMessageHandler(): (
|
export function createGenerateCommitMessageHandler(
|
||||||
req: Request,
|
settingsService?: SettingsService
|
||||||
res: Response
|
): (req: Request, res: Response) => Promise<void> {
|
||||||
) => Promise<void> {
|
|
||||||
return async (req: Request, res: Response): Promise<void> => {
|
return async (req: Request, res: Response): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
const { worktreePath } = req.body as GenerateCommitMessageRequestBody;
|
const { worktreePath } = req.body as GenerateCommitMessageRequestBody;
|
||||||
@@ -136,21 +133,66 @@ export function createGenerateCommitMessageHandler(): (
|
|||||||
|
|
||||||
const userPrompt = `Generate a commit message for these changes:\n\n\`\`\`diff\n${truncatedDiff}\n\`\`\``;
|
const userPrompt = `Generate a commit message for these changes:\n\n\`\`\`diff\n${truncatedDiff}\n\`\`\``;
|
||||||
|
|
||||||
const stream = query({
|
// Get model from phase settings
|
||||||
prompt: userPrompt,
|
const settings = await settingsService?.getGlobalSettings();
|
||||||
options: {
|
const phaseModelEntry =
|
||||||
model: CLAUDE_MODEL_MAP.haiku,
|
settings?.phaseModels?.commitMessageModel || DEFAULT_PHASE_MODELS.commitMessageModel;
|
||||||
systemPrompt: SYSTEM_PROMPT,
|
const { model } = resolvePhaseModel(phaseModelEntry);
|
||||||
|
|
||||||
|
logger.info(`Using model for commit message: ${model}`);
|
||||||
|
|
||||||
|
// Get the effective system prompt (custom or default)
|
||||||
|
const systemPrompt = await getSystemPrompt(settingsService);
|
||||||
|
|
||||||
|
let message: string;
|
||||||
|
|
||||||
|
// Route to appropriate provider based on model type
|
||||||
|
if (isCursorModel(model)) {
|
||||||
|
// Use Cursor provider for Cursor models
|
||||||
|
logger.info(`Using Cursor provider for model: ${model}`);
|
||||||
|
|
||||||
|
const provider = ProviderFactory.getProviderForModel(model);
|
||||||
|
const bareModel = stripProviderPrefix(model);
|
||||||
|
|
||||||
|
const cursorPrompt = `${systemPrompt}\n\n${userPrompt}`;
|
||||||
|
|
||||||
|
let responseText = '';
|
||||||
|
for await (const msg of provider.executeQuery({
|
||||||
|
prompt: cursorPrompt,
|
||||||
|
model: bareModel,
|
||||||
|
cwd: worktreePath,
|
||||||
maxTurns: 1,
|
maxTurns: 1,
|
||||||
allowedTools: [],
|
allowedTools: [],
|
||||||
permissionMode: 'default',
|
readOnly: true,
|
||||||
},
|
})) {
|
||||||
});
|
if (msg.type === 'assistant' && msg.message?.content) {
|
||||||
|
for (const block of msg.message.content) {
|
||||||
|
if (block.type === 'text' && block.text) {
|
||||||
|
responseText += block.text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const message = await extractTextFromStream(stream);
|
message = responseText.trim();
|
||||||
|
} else {
|
||||||
|
// Use Claude SDK for Claude models
|
||||||
|
const stream = query({
|
||||||
|
prompt: userPrompt,
|
||||||
|
options: {
|
||||||
|
model,
|
||||||
|
systemPrompt,
|
||||||
|
maxTurns: 1,
|
||||||
|
allowedTools: [],
|
||||||
|
permissionMode: 'default',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
message = await extractTextFromStream(stream);
|
||||||
|
}
|
||||||
|
|
||||||
if (!message || message.trim().length === 0) {
|
if (!message || message.trim().length === 0) {
|
||||||
logger.warn('Received empty response from Claude');
|
logger.warn('Received empty response from model');
|
||||||
const response: GenerateCommitMessageErrorResponse = {
|
const response: GenerateCommitMessageErrorResponse = {
|
||||||
success: false,
|
success: false,
|
||||||
error: 'Failed to generate commit message - empty response',
|
error: 'Failed to generate commit message - empty response',
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ import { Label } from '@/components/ui/label';
|
|||||||
import { GitCommit, Loader2, Sparkles } from 'lucide-react';
|
import { GitCommit, Loader2, Sparkles } from 'lucide-react';
|
||||||
import { getElectronAPI } from '@/lib/electron';
|
import { getElectronAPI } from '@/lib/electron';
|
||||||
import { toast } from 'sonner';
|
import { toast } from 'sonner';
|
||||||
|
import { useAppStore } from '@/store/app-store';
|
||||||
|
|
||||||
interface WorktreeInfo {
|
interface WorktreeInfo {
|
||||||
path: string;
|
path: string;
|
||||||
@@ -39,6 +40,7 @@ export function CommitWorktreeDialog({
|
|||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
const [isGenerating, setIsGenerating] = useState(false);
|
const [isGenerating, setIsGenerating] = useState(false);
|
||||||
const [error, setError] = useState<string | null>(null);
|
const [error, setError] = useState<string | null>(null);
|
||||||
|
const enableAiCommitMessages = useAppStore((state) => state.enableAiCommitMessages);
|
||||||
|
|
||||||
const handleCommit = async () => {
|
const handleCommit = async () => {
|
||||||
if (!worktree || !message.trim()) return;
|
if (!worktree || !message.trim()) return;
|
||||||
@@ -83,19 +85,24 @@ export function CommitWorktreeDialog({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
// Generate AI commit message when dialog opens
|
// Generate AI commit message when dialog opens (if enabled)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (open && worktree) {
|
if (open && worktree) {
|
||||||
// Reset state
|
// Reset state
|
||||||
setMessage('');
|
setMessage('');
|
||||||
setError(null);
|
setError(null);
|
||||||
|
|
||||||
|
// Only generate AI commit message if enabled
|
||||||
|
if (!enableAiCommitMessages) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setIsGenerating(true);
|
setIsGenerating(true);
|
||||||
|
|
||||||
const generateMessage = async () => {
|
const generateMessage = async () => {
|
||||||
try {
|
try {
|
||||||
const api = getElectronAPI();
|
const api = getElectronAPI();
|
||||||
if (!api?.worktree?.generateCommitMessage) {
|
if (!api?.worktree?.generateCommitMessage) {
|
||||||
setError('AI commit message generation not available');
|
|
||||||
setIsGenerating(false);
|
setIsGenerating(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -120,7 +127,7 @@ export function CommitWorktreeDialog({
|
|||||||
|
|
||||||
generateMessage();
|
generateMessage();
|
||||||
}
|
}
|
||||||
}, [open, worktree]);
|
}, [open, worktree, enableAiCommitMessages]);
|
||||||
|
|
||||||
if (!worktree) return null;
|
if (!worktree) return null;
|
||||||
|
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ export function SettingsView() {
|
|||||||
setEnableDependencyBlocking,
|
setEnableDependencyBlocking,
|
||||||
skipVerificationInAutoMode,
|
skipVerificationInAutoMode,
|
||||||
setSkipVerificationInAutoMode,
|
setSkipVerificationInAutoMode,
|
||||||
|
enableAiCommitMessages,
|
||||||
|
setEnableAiCommitMessages,
|
||||||
useWorktrees,
|
useWorktrees,
|
||||||
setUseWorktrees,
|
setUseWorktrees,
|
||||||
muteDoneSound,
|
muteDoneSound,
|
||||||
@@ -182,11 +184,13 @@ export function SettingsView() {
|
|||||||
skipVerificationInAutoMode={skipVerificationInAutoMode}
|
skipVerificationInAutoMode={skipVerificationInAutoMode}
|
||||||
defaultPlanningMode={defaultPlanningMode}
|
defaultPlanningMode={defaultPlanningMode}
|
||||||
defaultRequirePlanApproval={defaultRequirePlanApproval}
|
defaultRequirePlanApproval={defaultRequirePlanApproval}
|
||||||
|
enableAiCommitMessages={enableAiCommitMessages}
|
||||||
onDefaultSkipTestsChange={setDefaultSkipTests}
|
onDefaultSkipTestsChange={setDefaultSkipTests}
|
||||||
onEnableDependencyBlockingChange={setEnableDependencyBlocking}
|
onEnableDependencyBlockingChange={setEnableDependencyBlocking}
|
||||||
onSkipVerificationInAutoModeChange={setSkipVerificationInAutoMode}
|
onSkipVerificationInAutoModeChange={setSkipVerificationInAutoMode}
|
||||||
onDefaultPlanningModeChange={setDefaultPlanningMode}
|
onDefaultPlanningModeChange={setDefaultPlanningMode}
|
||||||
onDefaultRequirePlanApprovalChange={setDefaultRequirePlanApproval}
|
onDefaultRequirePlanApprovalChange={setDefaultRequirePlanApproval}
|
||||||
|
onEnableAiCommitMessagesChange={setEnableAiCommitMessages}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 'worktrees':
|
case 'worktrees':
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ import {
|
|||||||
ScrollText,
|
ScrollText,
|
||||||
ShieldCheck,
|
ShieldCheck,
|
||||||
FastForward,
|
FastForward,
|
||||||
|
Sparkles,
|
||||||
} from 'lucide-react';
|
} from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import {
|
import {
|
||||||
@@ -28,11 +29,13 @@ interface FeatureDefaultsSectionProps {
|
|||||||
skipVerificationInAutoMode: boolean;
|
skipVerificationInAutoMode: boolean;
|
||||||
defaultPlanningMode: PlanningMode;
|
defaultPlanningMode: PlanningMode;
|
||||||
defaultRequirePlanApproval: boolean;
|
defaultRequirePlanApproval: boolean;
|
||||||
|
enableAiCommitMessages: boolean;
|
||||||
onDefaultSkipTestsChange: (value: boolean) => void;
|
onDefaultSkipTestsChange: (value: boolean) => void;
|
||||||
onEnableDependencyBlockingChange: (value: boolean) => void;
|
onEnableDependencyBlockingChange: (value: boolean) => void;
|
||||||
onSkipVerificationInAutoModeChange: (value: boolean) => void;
|
onSkipVerificationInAutoModeChange: (value: boolean) => void;
|
||||||
onDefaultPlanningModeChange: (value: PlanningMode) => void;
|
onDefaultPlanningModeChange: (value: PlanningMode) => void;
|
||||||
onDefaultRequirePlanApprovalChange: (value: boolean) => void;
|
onDefaultRequirePlanApprovalChange: (value: boolean) => void;
|
||||||
|
onEnableAiCommitMessagesChange: (value: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function FeatureDefaultsSection({
|
export function FeatureDefaultsSection({
|
||||||
@@ -41,11 +44,13 @@ export function FeatureDefaultsSection({
|
|||||||
skipVerificationInAutoMode,
|
skipVerificationInAutoMode,
|
||||||
defaultPlanningMode,
|
defaultPlanningMode,
|
||||||
defaultRequirePlanApproval,
|
defaultRequirePlanApproval,
|
||||||
|
enableAiCommitMessages,
|
||||||
onDefaultSkipTestsChange,
|
onDefaultSkipTestsChange,
|
||||||
onEnableDependencyBlockingChange,
|
onEnableDependencyBlockingChange,
|
||||||
onSkipVerificationInAutoModeChange,
|
onSkipVerificationInAutoModeChange,
|
||||||
onDefaultPlanningModeChange,
|
onDefaultPlanningModeChange,
|
||||||
onDefaultRequirePlanApprovalChange,
|
onDefaultRequirePlanApprovalChange,
|
||||||
|
onEnableAiCommitMessagesChange,
|
||||||
}: FeatureDefaultsSectionProps) {
|
}: FeatureDefaultsSectionProps) {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@@ -251,6 +256,34 @@ export function FeatureDefaultsSection({
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Separator */}
|
||||||
|
<div className="border-t border-border/30" />
|
||||||
|
|
||||||
|
{/* AI Commit Messages Setting */}
|
||||||
|
<div className="group flex items-start space-x-3 p-3 rounded-xl hover:bg-accent/30 transition-colors duration-200 -mx-3">
|
||||||
|
<Checkbox
|
||||||
|
id="enable-ai-commit-messages"
|
||||||
|
checked={enableAiCommitMessages}
|
||||||
|
onCheckedChange={(checked) => onEnableAiCommitMessagesChange(checked === true)}
|
||||||
|
className="mt-1"
|
||||||
|
data-testid="enable-ai-commit-messages-checkbox"
|
||||||
|
/>
|
||||||
|
<div className="space-y-1.5">
|
||||||
|
<Label
|
||||||
|
htmlFor="enable-ai-commit-messages"
|
||||||
|
className="text-foreground cursor-pointer font-medium flex items-center gap-2"
|
||||||
|
>
|
||||||
|
<Sparkles className="w-4 h-4 text-brand-500" />
|
||||||
|
Generate AI commit messages
|
||||||
|
</Label>
|
||||||
|
<p className="text-xs text-muted-foreground/80 leading-relaxed">
|
||||||
|
When enabled, opening the commit dialog will automatically generate a commit message
|
||||||
|
using AI based on your staged or unstaged changes. You can configure the model used in
|
||||||
|
Model Defaults.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ const QUICK_TASKS: PhaseConfig[] = [
|
|||||||
label: 'Image Descriptions',
|
label: 'Image Descriptions',
|
||||||
description: 'Analyzes and describes context images',
|
description: 'Analyzes and describes context images',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: 'commitMessageModel',
|
||||||
|
label: 'Commit Messages',
|
||||||
|
description: 'Generates git commit messages from diffs',
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
const VALIDATION_TASKS: PhaseConfig[] = [
|
const VALIDATION_TASKS: PhaseConfig[] = [
|
||||||
|
|||||||
@@ -1538,11 +1538,10 @@ function createMockWorktreeAPI(): WorktreeAPI {
|
|||||||
},
|
},
|
||||||
|
|
||||||
generateCommitMessage: async (worktreePath: string) => {
|
generateCommitMessage: async (worktreePath: string) => {
|
||||||
console.log('[Mock] Generating commit message:', { worktreePath });
|
console.log('[Mock] Generating commit message for:', worktreePath);
|
||||||
return {
|
return {
|
||||||
success: true,
|
success: true,
|
||||||
message:
|
message: 'feat: Add mock commit message generation',
|
||||||
'feat: Add new feature implementation\n\nThis is a mock AI-generated commit message.',
|
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
@@ -536,6 +536,7 @@ export interface AppState {
|
|||||||
defaultSkipTests: boolean; // Default value for skip tests when creating new features
|
defaultSkipTests: boolean; // Default value for skip tests when creating new features
|
||||||
enableDependencyBlocking: boolean; // When true, show blocked badges and warnings for features with incomplete dependencies (default: true)
|
enableDependencyBlocking: boolean; // When true, show blocked badges and warnings for features with incomplete dependencies (default: true)
|
||||||
skipVerificationInAutoMode: boolean; // When true, auto-mode grabs features even if dependencies are not verified (only checks they're not running)
|
skipVerificationInAutoMode: boolean; // When true, auto-mode grabs features even if dependencies are not verified (only checks they're not running)
|
||||||
|
enableAiCommitMessages: boolean; // When true, auto-generate commit messages using AI when opening commit dialog
|
||||||
planUseSelectedWorktreeBranch: boolean; // When true, Plan dialog creates features on the currently selected worktree branch
|
planUseSelectedWorktreeBranch: boolean; // When true, Plan dialog creates features on the currently selected worktree branch
|
||||||
addFeatureUseSelectedWorktreeBranch: boolean; // When true, Add Feature dialog defaults to custom mode with selected worktree branch
|
addFeatureUseSelectedWorktreeBranch: boolean; // When true, Add Feature dialog defaults to custom mode with selected worktree branch
|
||||||
|
|
||||||
@@ -932,6 +933,7 @@ export interface AppActions {
|
|||||||
setDefaultSkipTests: (skip: boolean) => void;
|
setDefaultSkipTests: (skip: boolean) => void;
|
||||||
setEnableDependencyBlocking: (enabled: boolean) => void;
|
setEnableDependencyBlocking: (enabled: boolean) => void;
|
||||||
setSkipVerificationInAutoMode: (enabled: boolean) => Promise<void>;
|
setSkipVerificationInAutoMode: (enabled: boolean) => Promise<void>;
|
||||||
|
setEnableAiCommitMessages: (enabled: boolean) => Promise<void>;
|
||||||
setPlanUseSelectedWorktreeBranch: (enabled: boolean) => Promise<void>;
|
setPlanUseSelectedWorktreeBranch: (enabled: boolean) => Promise<void>;
|
||||||
setAddFeatureUseSelectedWorktreeBranch: (enabled: boolean) => Promise<void>;
|
setAddFeatureUseSelectedWorktreeBranch: (enabled: boolean) => Promise<void>;
|
||||||
|
|
||||||
@@ -1218,6 +1220,7 @@ const initialState: AppState = {
|
|||||||
defaultSkipTests: true, // Default to manual verification (tests disabled)
|
defaultSkipTests: true, // Default to manual verification (tests disabled)
|
||||||
enableDependencyBlocking: true, // Default to enabled (show dependency blocking UI)
|
enableDependencyBlocking: true, // Default to enabled (show dependency blocking UI)
|
||||||
skipVerificationInAutoMode: false, // Default to disabled (require dependencies to be verified)
|
skipVerificationInAutoMode: false, // Default to disabled (require dependencies to be verified)
|
||||||
|
enableAiCommitMessages: true, // Default to enabled (auto-generate commit messages)
|
||||||
planUseSelectedWorktreeBranch: true, // Default to enabled (Plan creates features on selected worktree branch)
|
planUseSelectedWorktreeBranch: true, // Default to enabled (Plan creates features on selected worktree branch)
|
||||||
addFeatureUseSelectedWorktreeBranch: false, // Default to disabled (Add Feature uses normal defaults)
|
addFeatureUseSelectedWorktreeBranch: false, // Default to disabled (Add Feature uses normal defaults)
|
||||||
useWorktrees: true, // Default to enabled (git worktree isolation)
|
useWorktrees: true, // Default to enabled (git worktree isolation)
|
||||||
@@ -1848,6 +1851,17 @@ export const useAppStore = create<AppState & AppActions>()((set, get) => ({
|
|||||||
const { syncSettingsToServer } = await import('@/hooks/use-settings-migration');
|
const { syncSettingsToServer } = await import('@/hooks/use-settings-migration');
|
||||||
await syncSettingsToServer();
|
await syncSettingsToServer();
|
||||||
},
|
},
|
||||||
|
setEnableAiCommitMessages: async (enabled) => {
|
||||||
|
const previous = get().enableAiCommitMessages;
|
||||||
|
set({ enableAiCommitMessages: enabled });
|
||||||
|
// Sync to server settings file
|
||||||
|
const { syncSettingsToServer } = await import('@/hooks/use-settings-migration');
|
||||||
|
const ok = await syncSettingsToServer();
|
||||||
|
if (!ok) {
|
||||||
|
logger.error('Failed to sync enableAiCommitMessages setting to server - reverting');
|
||||||
|
set({ enableAiCommitMessages: previous });
|
||||||
|
}
|
||||||
|
},
|
||||||
setPlanUseSelectedWorktreeBranch: async (enabled) => {
|
setPlanUseSelectedWorktreeBranch: async (enabled) => {
|
||||||
const previous = get().planUseSelectedWorktreeBranch;
|
const previous = get().planUseSelectedWorktreeBranch;
|
||||||
set({ planUseSelectedWorktreeBranch: enabled });
|
set({ planUseSelectedWorktreeBranch: enabled });
|
||||||
|
|||||||
@@ -157,6 +157,10 @@ export interface PhaseModelConfig {
|
|||||||
// Memory tasks - for learning extraction and memory operations
|
// Memory tasks - for learning extraction and memory operations
|
||||||
/** Model for extracting learnings from completed agent sessions */
|
/** Model for extracting learnings from completed agent sessions */
|
||||||
memoryExtractionModel: PhaseModelEntry;
|
memoryExtractionModel: PhaseModelEntry;
|
||||||
|
|
||||||
|
// Quick tasks - commit messages
|
||||||
|
/** Model for generating git commit messages from diffs */
|
||||||
|
commitMessageModel: PhaseModelEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Keys of PhaseModelConfig for type-safe access */
|
/** Keys of PhaseModelConfig for type-safe access */
|
||||||
@@ -398,6 +402,10 @@ export interface GlobalSettings {
|
|||||||
/** Priority for ntfy notifications (1-5, default: 3) */
|
/** Priority for ntfy notifications (1-5, default: 3) */
|
||||||
ntfyPriority: 1 | 2 | 3 | 4 | 5;
|
ntfyPriority: 1 | 2 | 3 | 4 | 5;
|
||||||
|
|
||||||
|
// AI Commit Message Generation
|
||||||
|
/** Enable AI-generated commit messages when opening commit dialog (default: true) */
|
||||||
|
enableAiCommitMessages: boolean;
|
||||||
|
|
||||||
// AI Model Selection (per-phase configuration)
|
// AI Model Selection (per-phase configuration)
|
||||||
/** Phase-specific AI model configuration */
|
/** Phase-specific AI model configuration */
|
||||||
phaseModels: PhaseModelConfig;
|
phaseModels: PhaseModelConfig;
|
||||||
@@ -669,6 +677,9 @@ export const DEFAULT_PHASE_MODELS: PhaseModelConfig = {
|
|||||||
|
|
||||||
// Memory - use fast model for learning extraction (cost-effective)
|
// Memory - use fast model for learning extraction (cost-effective)
|
||||||
memoryExtractionModel: { model: 'haiku' },
|
memoryExtractionModel: { model: 'haiku' },
|
||||||
|
|
||||||
|
// Commit messages - use fast model for speed
|
||||||
|
commitMessageModel: { model: 'haiku' },
|
||||||
};
|
};
|
||||||
|
|
||||||
/** Current version of the global settings schema */
|
/** Current version of the global settings schema */
|
||||||
@@ -724,6 +735,7 @@ export const DEFAULT_GLOBAL_SETTINGS: GlobalSettings = {
|
|||||||
ntfyTopic: '',
|
ntfyTopic: '',
|
||||||
ntfyAuthToken: undefined,
|
ntfyAuthToken: undefined,
|
||||||
ntfyPriority: 3,
|
ntfyPriority: 3,
|
||||||
|
enableAiCommitMessages: true,
|
||||||
phaseModels: DEFAULT_PHASE_MODELS,
|
phaseModels: DEFAULT_PHASE_MODELS,
|
||||||
enhancementModel: 'sonnet',
|
enhancementModel: 'sonnet',
|
||||||
validationModel: 'opus',
|
validationModel: 'opus',
|
||||||
|
|||||||
Reference in New Issue
Block a user