mirror of
https://github.com/stablyai/orca.git
synced 2026-10-19 16:03:26 +00:00
* WIP: Changes before auto-review fixes
* Customize Source Control AI action recipes
- Add per-action CLI arguments for generation and launch flows so saved
recipes can select model flags without putting prompts in argv.
- Share the text-generation dialog between commit messages and hosted-review
details, with first-run defaults and repo/global recipe support.
- Launch fix-check agents directly from saved recipes and harden prompt/template
handling for invalid args, blank prompts, and inherited variables.
* Allow custom commands for source control action recipes
- Let text action recipes save and resolve the custom-command sentinel
- Add settings UI for custom command recipes and preserve per-action defaults
- Split large source-control dialogs and direct launch helpers into focused modules
- Keep launch actions from treating custom text agents as runnable TUI agents
* Add per-repo Source Control AI enablement, custom command, and save-targ
- Repo overrides now support `enabled` and `customAgentCommand`, letting
repositories opt in/out of Source Control AI independently and supply a
repo-scoped custom command that takes precedence over the global one.
- Recipe-save dialogs gained a save-target selector ("Don't save / Save for
this repo / Save as global default") replacing the old boolean checkbox,
routing saves through the new `saveSourceControlActionRecipe` helper in
`source-control-ai-recipe-save.ts`.
- `normalizeRepoSourceControlAiOverrides` now returns `undefined` for empty
objects and passes the `null` sentinel through the IPC/RPC layer so the
persistence layer can clear repo overrides cleanly.
- `resolveSourceControlLaunchPlatform` resolves the correct shell platform
for SSH and WSL worktrees so agent launch commands are built correctly.
- Settings UI gained `RepositorySourceControlAiEnablement` and
`RepositorySourceControlAiCustomCommand` rows; draft/label logic was
extracted into focused modules to stay within lint line limits.
* Extract action recipe defaults into own component and use id-prefixed wo
- Move action recipe draft state and UI out of CommitMessageAiPane into SourceControlAiActionRecipeDefaults and source-control-ai-action-recipe-draft.ts to respect the max-lines lint rule
- Use toRuntimeWorktreeSelector() across all runtime git RPC calls so the runtime can resolve worktrees by ID rather than path
- Fix SSH launch platform resolution to use the repo's connection when the newly created worktree isn't hydrated yet
- Add edit and delete handlers for PR conversation comments with confirmation dialog
- Use text-status-success design token instead of hardcoded text-emerald-500
* add more search keyword
* Rename "Enable Source Control AI defaults" to "Show Source Control AI ac
* fix test
* Remove unused imports and variable assignment in launch-work-item-direct
* Fix test mocks to use `mocks.store` instead of `storeState.value` for di
* Extract Source Control AI logic into focused modules with fix-checks dia
---------
Co-authored-by: Orca <help@stably.ai>
230 lines
6.3 KiB
TypeScript
230 lines
6.3 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import { getDefaultSettings } from './constants'
|
|
import {
|
|
normalizeWritableRepoSourceControlAiOverrides,
|
|
saveSourceControlActionRecipe,
|
|
toSourceControlAiRepoUpdate
|
|
} from './source-control-ai-recipe-save'
|
|
import type { GlobalSettings, Repo } from './types'
|
|
|
|
function settings(): GlobalSettings {
|
|
const base = getDefaultSettings('/tmp')
|
|
return {
|
|
...base,
|
|
defaultTuiAgent: 'codex',
|
|
sourceControlAi: {
|
|
...base.sourceControlAi!,
|
|
enabled: true,
|
|
agentId: 'codex',
|
|
selectedModelByAgent: { codex: 'gpt-5.5' },
|
|
selectedThinkingByModel: {}
|
|
}
|
|
}
|
|
}
|
|
|
|
describe('source-control AI recipe saves', () => {
|
|
it('writes complete repo recipes and removes same-action legacy instructions', () => {
|
|
const result = saveSourceControlActionRecipe({
|
|
target: { type: 'repo', repoId: 'repo-1' },
|
|
settings: settings(),
|
|
repo: {
|
|
sourceControlAi: {
|
|
enabled: false,
|
|
instructionsByOperation: {
|
|
commitMessage: 'legacy commit',
|
|
pullRequest: 'legacy review'
|
|
},
|
|
prCreationDefaults: {
|
|
draft: true,
|
|
useTemplate: null
|
|
}
|
|
}
|
|
} satisfies Pick<Repo, 'sourceControlAi'>,
|
|
actionId: 'commitMessage',
|
|
recipe: {
|
|
agentId: 'claude',
|
|
commandInputTemplate: '{basePrompt}\n\nrepo',
|
|
agentArgs: ' --model sonnet '
|
|
}
|
|
})
|
|
|
|
expect(result).toEqual({
|
|
target: { type: 'repo', repoId: 'repo-1' },
|
|
update: {
|
|
sourceControlAi: {
|
|
enabled: false,
|
|
instructionsByOperation: {
|
|
pullRequest: 'legacy review'
|
|
},
|
|
actionOverrides: {
|
|
commitMessage: {
|
|
agentId: 'claude',
|
|
commandInputTemplate: '{basePrompt}\n\nrepo',
|
|
agentArgs: '--model sonnet'
|
|
},
|
|
pullRequest: {
|
|
agentId: null,
|
|
commandInputTemplate: '{basePrompt}\n\nlegacy review'
|
|
}
|
|
},
|
|
prCreationDefaults: {
|
|
draft: true
|
|
}
|
|
}
|
|
}
|
|
})
|
|
})
|
|
|
|
it('clears empty repo source-control AI writes with the JSON boundary sentinel', () => {
|
|
expect(toSourceControlAiRepoUpdate({ prCreationDefaults: { draft: null } })).toEqual({
|
|
sourceControlAi: null
|
|
})
|
|
})
|
|
|
|
it('preserves boolean-only hosted-review defaults and strips legacy null defaults on write', () => {
|
|
expect(
|
|
normalizeWritableRepoSourceControlAiOverrides({
|
|
prCreationDefaults: {
|
|
draft: true,
|
|
useTemplate: null,
|
|
generateDetailsOnOpen: false
|
|
}
|
|
})
|
|
).toEqual({
|
|
prCreationDefaults: {
|
|
draft: true,
|
|
generateDetailsOnOpen: false
|
|
}
|
|
})
|
|
})
|
|
|
|
it('stores repo custom commands when saving a custom-command recipe', () => {
|
|
const result = saveSourceControlActionRecipe({
|
|
target: { type: 'repo', repoId: 'repo-1' },
|
|
settings: settings(),
|
|
repo: null,
|
|
actionId: 'commitMessage',
|
|
recipe: {
|
|
agentId: 'custom',
|
|
commandInputTemplate: '{basePrompt}'
|
|
},
|
|
customAgentCommand: 'repo-agent {prompt}'
|
|
})
|
|
|
|
expect(result).toMatchObject({
|
|
update: {
|
|
sourceControlAi: {
|
|
customAgentCommand: 'repo-agent {prompt}',
|
|
actionOverrides: {
|
|
commitMessage: {
|
|
agentId: 'custom',
|
|
commandInputTemplate: '{basePrompt}'
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
})
|
|
|
|
it('writes global recipes through a complete normalized source-control AI value', () => {
|
|
const result = saveSourceControlActionRecipe({
|
|
target: { type: 'global' },
|
|
settings: settings(),
|
|
actionId: 'pullRequest',
|
|
recipe: {
|
|
agentId: 'custom',
|
|
commandInputTemplate: '{basePrompt}\n\nreview',
|
|
agentArgs: '--verbose'
|
|
},
|
|
customAgentCommand: 'review-agent {prompt}'
|
|
})
|
|
|
|
expect(result.target).toEqual({ type: 'global' })
|
|
expect('sourceControlAi' in result).toBe(true)
|
|
if (!('sourceControlAi' in result)) {
|
|
throw new Error('Expected a global save result')
|
|
}
|
|
expect(result.sourceControlAi.actions?.pullRequest).toEqual({
|
|
agentId: 'custom',
|
|
commandInputTemplate: '{basePrompt}\n\nreview',
|
|
agentArgs: '--verbose'
|
|
})
|
|
expect(result.sourceControlAi.customAgentCommand).toBe('review-agent {prompt}')
|
|
expect(result.sourceControlAi.enabled).toBe(true)
|
|
})
|
|
|
|
it('replaces global recipes so cleared CLI args do not survive', () => {
|
|
const currentSettings = settings()
|
|
currentSettings.sourceControlAi = {
|
|
...currentSettings.sourceControlAi!,
|
|
actions: {
|
|
pullRequest: {
|
|
agentId: 'codex',
|
|
commandInputTemplate: '{basePrompt}',
|
|
agentArgs: '--old-model'
|
|
}
|
|
}
|
|
}
|
|
|
|
const result = saveSourceControlActionRecipe({
|
|
target: { type: 'global' },
|
|
settings: currentSettings,
|
|
actionId: 'pullRequest',
|
|
recipe: {
|
|
agentId: 'claude',
|
|
commandInputTemplate: '{basePrompt}',
|
|
agentArgs: ''
|
|
}
|
|
})
|
|
|
|
if (!('sourceControlAi' in result)) {
|
|
throw new Error('Expected a global save result')
|
|
}
|
|
expect(result.sourceControlAi.actions?.pullRequest).toEqual({
|
|
agentId: 'claude',
|
|
commandInputTemplate: '{basePrompt}',
|
|
agentArgs: ''
|
|
})
|
|
})
|
|
|
|
it('writes empty repo CLI args so repository recipes can clear inherited args', () => {
|
|
const currentSettings = settings()
|
|
currentSettings.sourceControlAi = {
|
|
...currentSettings.sourceControlAi!,
|
|
actions: {
|
|
pullRequest: {
|
|
agentId: 'codex',
|
|
commandInputTemplate: '{basePrompt}',
|
|
agentArgs: '--global-model'
|
|
}
|
|
}
|
|
}
|
|
|
|
const result = saveSourceControlActionRecipe({
|
|
target: { type: 'repo', repoId: 'repo-1' },
|
|
settings: currentSettings,
|
|
repo: null,
|
|
actionId: 'pullRequest',
|
|
recipe: {
|
|
agentId: 'claude',
|
|
commandInputTemplate: '{basePrompt}',
|
|
agentArgs: ''
|
|
}
|
|
})
|
|
|
|
expect(result).toMatchObject({
|
|
update: {
|
|
sourceControlAi: {
|
|
actionOverrides: {
|
|
pullRequest: {
|
|
agentId: 'claude',
|
|
commandInputTemplate: '{basePrompt}',
|
|
agentArgs: ''
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
})
|
|
})
|