mirror of
https://github.com/stablyai/orca.git
synced 2026-09-23 00:02:29 +00:00
* fix(naming): keep the built-in branch-name prompt general The shipped auto-rename prompt no longer hard-codes style rules (word count, kebab-case, no prefixes). Users can already override naming via Source Control AI instructions and the branch-name command template; a prescriptive default fought those overrides. Git-safe sanitization still runs after generation. * fix(naming): preserve branch prompt overrides * chore(skills): refresh rc.2 manifest version
135 lines
5.3 KiB
TypeScript
135 lines
5.3 KiB
TypeScript
import { describe, expect, it } from 'vitest'
|
|
import {
|
|
buildBranchNamePrompt,
|
|
humanizeBranchSlug,
|
|
isAutoGeneratedCreatureBranchName,
|
|
sanitizeBranchSlug,
|
|
stripConfiguredBranchPrefix
|
|
} from './branch-name-from-work'
|
|
|
|
describe('sanitizeBranchSlug', () => {
|
|
it('lowercases and kebab-cases free-form model output', () => {
|
|
expect(sanitizeBranchSlug('Fix the auth bug')).toBe('fix-the-auth-bug')
|
|
})
|
|
|
|
it('caps at four words', () => {
|
|
expect(sanitizeBranchSlug('add dark mode toggle to settings page')).toBe('add-dark-mode-toggle')
|
|
})
|
|
|
|
it('honors a custom word cap', () => {
|
|
expect(sanitizeBranchSlug('add dark mode toggle', 2)).toBe('add-dark')
|
|
})
|
|
|
|
it('collapses punctuation, quotes, and slashes into single hyphens', () => {
|
|
expect(sanitizeBranchSlug('"feat/login_flow!!"')).toBe('feat-login-flow')
|
|
})
|
|
|
|
it('returns empty string when nothing usable remains', () => {
|
|
expect(sanitizeBranchSlug(' !!! ___ ')).toBe('')
|
|
expect(sanitizeBranchSlug('')).toBe('')
|
|
})
|
|
})
|
|
|
|
describe('isAutoGeneratedCreatureBranchName', () => {
|
|
it('matches a creature name case-insensitively', () => {
|
|
expect(isAutoGeneratedCreatureBranchName('Nautilus')).toBe(true)
|
|
expect(isAutoGeneratedCreatureBranchName('octopus')).toBe(true)
|
|
})
|
|
|
|
it('matches numbered collision variants', () => {
|
|
expect(isAutoGeneratedCreatureBranchName('Nautilus-2')).toBe(true)
|
|
expect(isAutoGeneratedCreatureBranchName('Seahorse-17')).toBe(true)
|
|
})
|
|
|
|
it('rejects user-chosen and work-derived names', () => {
|
|
expect(isAutoGeneratedCreatureBranchName('fix-auth-bug')).toBe(false)
|
|
expect(isAutoGeneratedCreatureBranchName('my-feature')).toBe(false)
|
|
expect(isAutoGeneratedCreatureBranchName('')).toBe(false)
|
|
})
|
|
})
|
|
|
|
describe('stripConfiguredBranchPrefix', () => {
|
|
it('strips a leaked username prefix the model folded into the slug', () => {
|
|
expect(stripConfiguredBranchPrefix('tmchow-worktree-creation-spinner', 'tmchow')).toBe(
|
|
'worktree-creation-spinner'
|
|
)
|
|
})
|
|
|
|
it('strips a multi-token custom prefix', () => {
|
|
expect(stripConfiguredBranchPrefix('my-team-add-logout', 'my-team')).toBe('add-logout')
|
|
})
|
|
|
|
it('normalizes the prefix the same way the slug was normalized', () => {
|
|
expect(stripConfiguredBranchPrefix('jane-doe-fix-auth', 'Jane.Doe')).toBe('fix-auth')
|
|
})
|
|
|
|
it('leaves a work-derived name that merely starts with a real word', () => {
|
|
// The leading word matches no configured prefix, so it is content, not a prefix.
|
|
expect(stripConfiguredBranchPrefix('add-logout-button', 'tmchow')).toBe('add-logout-button')
|
|
})
|
|
|
|
it('does not strip when no prefix is configured', () => {
|
|
expect(stripConfiguredBranchPrefix('tmchow-fix-auth', null)).toBe('tmchow-fix-auth')
|
|
expect(stripConfiguredBranchPrefix('tmchow-fix-auth', undefined)).toBe('tmchow-fix-auth')
|
|
expect(stripConfiguredBranchPrefix('tmchow-fix-auth', '')).toBe('tmchow-fix-auth')
|
|
})
|
|
|
|
it('returns empty for prefix-only output so the caller skips the rename', () => {
|
|
// The model echoed just the prefix; renaming would double it (`tmchow/tmchow`).
|
|
expect(stripConfiguredBranchPrefix('tmchow', 'tmchow')).toBe('')
|
|
// Confirm a real work-derived name still strips correctly.
|
|
expect(stripConfiguredBranchPrefix('tmchow-fix-auth', 'tmchow')).toBe('fix-auth')
|
|
})
|
|
})
|
|
|
|
describe('humanizeBranchSlug', () => {
|
|
it('turns a kebab slug into a readable label', () => {
|
|
expect(humanizeBranchSlug('supported-models-list')).toBe('Supported models list')
|
|
expect(humanizeBranchSlug('fix-auth')).toBe('Fix auth')
|
|
})
|
|
|
|
it('returns empty string for an empty slug', () => {
|
|
expect(humanizeBranchSlug('')).toBe('')
|
|
})
|
|
})
|
|
|
|
describe('buildBranchNamePrompt', () => {
|
|
it('includes the user prompt and omits the assistant section when absent', () => {
|
|
const prompt = buildBranchNamePrompt({ firstPrompt: 'Add a logout button' })
|
|
expect(prompt).toContain('Add a logout button')
|
|
expect(prompt).not.toContain("Agent's initial response")
|
|
})
|
|
|
|
it('keeps the default prompt general without style rules', () => {
|
|
const prompt = buildBranchNamePrompt({ firstPrompt: 'Add a logout button' })
|
|
expect(prompt).toContain('Generate a short git branch name')
|
|
expect(prompt).toContain('Output ONLY the branch name on a single line')
|
|
expect(prompt).not.toContain('Rules:')
|
|
expect(prompt).not.toMatch(/kebab-case/i)
|
|
expect(prompt).not.toMatch(/between \d+ and \d+ words/i)
|
|
expect(prompt).not.toMatch(/no prefixes/i)
|
|
})
|
|
|
|
it('includes the assistant response when present', () => {
|
|
const prompt = buildBranchNamePrompt({
|
|
firstPrompt: 'Add a logout button',
|
|
assistantMessage: "I'll wire it into the header."
|
|
})
|
|
expect(prompt).toContain("Agent's initial response")
|
|
expect(prompt).toContain("I'll wire it into the header.")
|
|
})
|
|
|
|
it('leads with a custom naming prompt so overrides can own style', () => {
|
|
const prompt = buildBranchNamePrompt(
|
|
{ firstPrompt: 'Add a logout button' },
|
|
'Prefer product nouns.'
|
|
)
|
|
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')
|
|
})
|
|
})
|