mirror of
https://github.com/stablyai/orca.git
synced 2026-10-18 08:03:22 +00:00
fix(naming): preserve branch prompt overrides
This commit is contained in:
@@ -2483,7 +2483,7 @@ describe('Store', () => {
|
||||
})
|
||||
expect(persisted.settings.sourceControlAi.actions.branchName).toEqual({
|
||||
agentId: 'claude',
|
||||
commandInputTemplate: '{basePrompt}\n\nRollback commit prompt'
|
||||
commandInputTemplate: 'Rollback commit prompt\n\n{basePrompt}'
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -1853,7 +1853,7 @@ describe('generateBranchNameFromContext', () => {
|
||||
})
|
||||
})
|
||||
|
||||
it('includes the branch-name custom prompt in the generated prompt', async () => {
|
||||
it('keeps branch-name guidance first without dropping the output contract', async () => {
|
||||
let prompt = ''
|
||||
await generateBranchNameFromContext(
|
||||
{ firstPrompt: 'Fix login flow' },
|
||||
@@ -1861,7 +1861,8 @@ describe('generateBranchNameFromContext', () => {
|
||||
agentId: 'custom',
|
||||
model: '',
|
||||
customAgentCommand: 'agent',
|
||||
customPrompt: 'Prefer auth terminology.'
|
||||
customPrompt: 'Prefer auth terminology.',
|
||||
commandInputTemplate: 'Prefer auth terminology.\n\n{basePrompt}'
|
||||
},
|
||||
{
|
||||
kind: 'remote',
|
||||
@@ -1882,6 +1883,8 @@ describe('generateBranchNameFromContext', () => {
|
||||
expect(prompt.startsWith('Prefer auth terminology.')).toBe(true)
|
||||
expect(prompt).toContain('Prefer auth terminology.')
|
||||
expect(prompt).not.toContain('Additional user prompt:')
|
||||
expect(prompt).toContain('Generate a short git branch name')
|
||||
expect(prompt).toContain('Output ONLY the branch name on a single line')
|
||||
})
|
||||
})
|
||||
|
||||
|
||||
@@ -127,6 +127,8 @@ describe('buildBranchNamePrompt', () => {
|
||||
expect(prompt.startsWith('Prefer product nouns.')).toBe(true)
|
||||
expect(prompt).not.toContain('Additional user prompt:')
|
||||
expect(prompt).not.toContain('Rules:')
|
||||
expect(prompt).toContain('Generate a git branch name')
|
||||
expect(prompt).toContain('Output ONLY the branch name on a single line')
|
||||
expect(prompt).toContain('Add a logout button')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -113,13 +113,14 @@ export function buildBranchNamePrompt(context: BranchNameWorkContext, customProm
|
||||
// override owns style rather than sitting under a prescriptive default.
|
||||
if (prompt) {
|
||||
sections.push(prompt, '')
|
||||
} else {
|
||||
sections.push(
|
||||
'Generate a short git branch name that summarizes the coding task described below.',
|
||||
'Output ONLY the branch name on a single line, nothing else.',
|
||||
''
|
||||
)
|
||||
}
|
||||
sections.push(
|
||||
prompt
|
||||
? 'Generate a git branch name that summarizes the coding task described below.'
|
||||
: 'Generate a short git branch name that summarizes the coding task described below.',
|
||||
'Output ONLY the branch name on a single line, nothing else.',
|
||||
''
|
||||
)
|
||||
sections.push('User request:', context.firstPrompt.trim())
|
||||
const assistant = context.assistantMessage?.trim()
|
||||
if (assistant) {
|
||||
|
||||
@@ -313,11 +313,19 @@ describe('source-control AI resolution', () => {
|
||||
}).params.customPrompt
|
||||
).toBe('Repo commit style')
|
||||
expect(resolve('branchName').params.customPrompt).toBe('Global branch style')
|
||||
expect(resolve('branchName').params.commandInputTemplate).toBe(
|
||||
'Global branch style\n\n{basePrompt}'
|
||||
)
|
||||
expect(
|
||||
resolve('branchName', {
|
||||
instructionsByOperation: { branchName: 'Repo branch style' }
|
||||
}).params.customPrompt
|
||||
).toBe('Repo branch style')
|
||||
expect(
|
||||
resolve('branchName', {
|
||||
instructionsByOperation: { branchName: 'Repo branch style' }
|
||||
}).params.commandInputTemplate
|
||||
).toBe('Repo branch style\n\n{basePrompt}')
|
||||
})
|
||||
|
||||
it('does not treat null repo instructions as configured overrides', () => {
|
||||
@@ -368,6 +376,12 @@ describe('source-control AI resolution', () => {
|
||||
expect(migrated.instructionsByOperation.commitMessage).toBe('Legacy commit prompt')
|
||||
expect(migrated.instructionsByOperation.pullRequest).toBe('')
|
||||
expect(migrated.instructionsByOperation.branchName).toBe('Legacy commit prompt')
|
||||
expect(migrated.actions?.commitMessage?.commandInputTemplate).toBe(
|
||||
'{basePrompt}\n\nLegacy commit prompt'
|
||||
)
|
||||
expect(migrated.actions?.branchName?.commandInputTemplate).toBe(
|
||||
'Legacy commit prompt\n\n{basePrompt}'
|
||||
)
|
||||
})
|
||||
|
||||
it('merges legacy commit-message updates without wiping PR-only settings', () => {
|
||||
@@ -702,7 +716,7 @@ describe('source-control AI resolution', () => {
|
||||
commandInputTemplate: '{basePrompt}'
|
||||
},
|
||||
branchName: {
|
||||
commandInputTemplate: '{basePrompt}\n\nbranch style'
|
||||
commandInputTemplate: 'branch style\n\n{basePrompt}'
|
||||
}
|
||||
},
|
||||
prCreationDefaults: {
|
||||
|
||||
@@ -296,13 +296,15 @@ export function normalizeRepoSourceControlAiOverrides(
|
||||
const migratedActionOverrides = { ...actionOverrides }
|
||||
for (const operation of SOURCE_CONTROL_TEXT_ACTION_IDS) {
|
||||
const instruction = instructionsByOperation?.[operation]
|
||||
const existingTemplate = migratedActionOverrides[operation]?.commandInputTemplate
|
||||
if (
|
||||
typeof instruction === 'string' &&
|
||||
migratedActionOverrides[operation]?.commandInputTemplate === undefined
|
||||
(existingTemplate === undefined ||
|
||||
isLegacyBranchInstructionTemplate(operation, instruction, existingTemplate))
|
||||
) {
|
||||
migratedActionOverrides[operation] = {
|
||||
...migratedActionOverrides[operation],
|
||||
commandInputTemplate: commandTemplateFromInstruction(instruction)
|
||||
commandInputTemplate: commandTemplateFromOperationInstruction(operation, instruction)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -324,6 +326,35 @@ function commandTemplateFromInstruction(instruction: string | null | undefined):
|
||||
return ['{basePrompt}', '', trimmed].join('\n')
|
||||
}
|
||||
|
||||
function commandTemplateFromOperationInstruction(
|
||||
operation: SourceControlAiOperation,
|
||||
instruction: string | null | undefined
|
||||
): string {
|
||||
const trimmed = instruction?.trim()
|
||||
if (!trimmed) {
|
||||
return '{basePrompt}'
|
||||
}
|
||||
// Why: branch naming instructions define naming style, so they must precede
|
||||
// the general built-in prompt. Other operations retain their released order.
|
||||
return operation === 'branchName'
|
||||
? [trimmed, '', '{basePrompt}'].join('\n')
|
||||
: commandTemplateFromInstruction(trimmed)
|
||||
}
|
||||
|
||||
function isLegacyBranchInstructionTemplate(
|
||||
operation: SourceControlAiOperation,
|
||||
instruction: string | null | undefined,
|
||||
template: string | null | undefined
|
||||
): boolean {
|
||||
// Why: reorder only the exact template older settings derived automatically;
|
||||
// a user-authored command template remains authoritative.
|
||||
return (
|
||||
operation === 'branchName' &&
|
||||
Boolean(instruction?.trim()) &&
|
||||
template === commandTemplateFromInstruction(instruction)
|
||||
)
|
||||
}
|
||||
|
||||
function actionRecipeFromLegacyCommitMessageAi(legacy: CommitMessageAiSettings): {
|
||||
agentId?: TuiAgent | CustomAgentId | null
|
||||
commandInputTemplate: string
|
||||
@@ -400,7 +431,10 @@ function shouldImportLegacyBranchPrompt(
|
||||
projectedLegacy: CommitMessageAiSettings
|
||||
): boolean {
|
||||
const branchRecipe = readSourceControlActionDefault(base.actions, 'branchName')
|
||||
const projectedTemplate = commandTemplateFromInstruction(projectedLegacy.customPrompt)
|
||||
const projectedTemplate = commandTemplateFromOperationInstruction(
|
||||
'branchName',
|
||||
projectedLegacy.customPrompt
|
||||
)
|
||||
return (
|
||||
branchRecipe.commandInputTemplate === undefined ||
|
||||
branchRecipe.commandInputTemplate ===
|
||||
@@ -473,7 +507,13 @@ export function sourceControlAiSettingsFromLegacy(
|
||||
actions: {
|
||||
...defaults.actions,
|
||||
commitMessage: legacyActionRecipe,
|
||||
branchName: legacyActionRecipe
|
||||
branchName: {
|
||||
...legacyActionRecipe,
|
||||
commandInputTemplate: commandTemplateFromOperationInstruction(
|
||||
'branchName',
|
||||
legacy.customPrompt
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -640,7 +680,12 @@ export function mergeLegacyCommitMessageAiIntoSourceControlAi(
|
||||
? applyLegacyAgentToActionRecipe(base.actions?.branchName, legacy.agentId)
|
||||
: base.actions?.branchName),
|
||||
...(shouldMergeBranchPrompt
|
||||
? { commandInputTemplate: legacyActionRecipe.commandInputTemplate }
|
||||
? {
|
||||
commandInputTemplate: commandTemplateFromOperationInstruction(
|
||||
'branchName',
|
||||
legacy.customPrompt
|
||||
)
|
||||
}
|
||||
: {})
|
||||
}
|
||||
}
|
||||
@@ -693,15 +738,21 @@ export function normalizeSourceControlAiSettings(
|
||||
const existing = readSourceControlActionDefault(normalizedActions, actionId)
|
||||
const instruction = base.instructionsByOperation?.[actionId]
|
||||
const legacyInstruction = actionId === 'commitMessage' ? legacy?.customPrompt : undefined
|
||||
const resolvedInstruction = instruction ?? legacyInstruction
|
||||
const instructionTemplate =
|
||||
instruction || legacyInstruction
|
||||
? commandTemplateFromInstruction(instruction ?? legacyInstruction)
|
||||
? commandTemplateFromOperationInstruction(actionId, resolvedInstruction)
|
||||
: undefined
|
||||
const shouldApplyInstructionTemplate =
|
||||
instructionTemplate !== undefined &&
|
||||
(existing.commandInputTemplate === undefined ||
|
||||
existing.commandInputTemplate ===
|
||||
DEFAULT_SOURCE_CONTROL_ACTION_COMMAND_TEMPLATES[actionId])
|
||||
DEFAULT_SOURCE_CONTROL_ACTION_COMMAND_TEMPLATES[actionId] ||
|
||||
isLegacyBranchInstructionTemplate(
|
||||
actionId,
|
||||
resolvedInstruction,
|
||||
existing.commandInputTemplate
|
||||
))
|
||||
return [
|
||||
actionId,
|
||||
{
|
||||
@@ -1048,7 +1099,7 @@ function resolveActionRecipeForTextOperation(
|
||||
)
|
||||
const fallbackTemplate =
|
||||
repoInstruction !== undefined
|
||||
? commandTemplateFromInstruction(repoInstruction)
|
||||
? commandTemplateFromOperationInstruction(operation, repoInstruction)
|
||||
: resolveSourceControlActionCommandTemplate(source.actions, operation)
|
||||
const repoTemplate =
|
||||
typeof repoRecipe?.commandInputTemplate === 'string'
|
||||
|
||||
Reference in New Issue
Block a user