mirror of
https://github.com/stablyai/orca.git
synced 2026-10-25 16:03:53 +00:00
97 lines
3.4 KiB
TypeScript
97 lines
3.4 KiB
TypeScript
import { MARINE_CREATURES } from './marine-creatures'
|
|
|
|
// Why: the work-derived branch name stays short on purpose — long, sentence-like
|
|
// branches read worse than the creature name they replace. Two-to-four words is
|
|
// the sweet spot the feature targets.
|
|
export const MAX_BRANCH_NAME_WORDS = 4
|
|
const MIN_BRANCH_NAME_WORDS = 2
|
|
|
|
const CREATURE_NAMES_LOWER = new Set(MARINE_CREATURES.map((name) => name.toLowerCase()))
|
|
|
|
function normalizeName(name: string): string {
|
|
return name.trim().toLowerCase()
|
|
}
|
|
|
|
/**
|
|
* True when `branchLeaf` (the segment after any `prefix/`) is an Orca
|
|
* auto-generated creature name, e.g. `Nautilus` or `Octopus-2`. Used as the
|
|
* eligibility gate so auto-rename only ever overwrites a name Orca itself
|
|
* picked — never a branch the user named deliberately.
|
|
*/
|
|
export function isAutoGeneratedCreatureBranchName(branchLeaf: string): boolean {
|
|
const normalized = normalizeName(branchLeaf)
|
|
if (!normalized) {
|
|
return false
|
|
}
|
|
// Collision suffixes (`-2`, `-3`, …) are appended at creation; strip one
|
|
// before checking membership so numbered creatures still qualify.
|
|
const base = normalized.replace(/-\d+$/, '')
|
|
return CREATURE_NAMES_LOWER.has(base)
|
|
}
|
|
|
|
/**
|
|
* Turn raw model output into a safe, short kebab-case branch leaf. Returns ''
|
|
* when nothing usable remains, which callers treat as "skip the rename".
|
|
*/
|
|
export function sanitizeBranchSlug(raw: string, maxWords = MAX_BRANCH_NAME_WORDS): string {
|
|
const words = raw
|
|
.toLowerCase()
|
|
.replace(/[^a-z0-9]+/g, '-')
|
|
.split('-')
|
|
.filter(Boolean)
|
|
if (words.length === 0) {
|
|
return ''
|
|
}
|
|
return words.slice(0, maxWords).join('-')
|
|
}
|
|
|
|
/**
|
|
* Turn a branch slug into a readable workspace display label, e.g.
|
|
* `supported-models-list` → `Supported models list`. Used to update the
|
|
* sidebar name alongside the branch so the two stay in sync.
|
|
*/
|
|
export function humanizeBranchSlug(slug: string): string {
|
|
const joined = slug.split('-').filter(Boolean).join(' ')
|
|
if (!joined) {
|
|
return ''
|
|
}
|
|
return joined.charAt(0).toUpperCase() + joined.slice(1)
|
|
}
|
|
|
|
export type BranchNameWorkContext = {
|
|
/** The user's first prompt to the agent in this workspace. */
|
|
firstPrompt: string
|
|
/** The agent's first response, when it has already arrived. Optional because
|
|
* the rename fires as soon as work begins, before a reply may exist. */
|
|
assistantMessage?: string
|
|
}
|
|
|
|
/**
|
|
* Build the text-generation prompt that asks the configured agent to summarize
|
|
* the work into a branch name. Kept in shared so the prompt is identical across
|
|
* local and SSH generation targets.
|
|
*/
|
|
export function buildBranchNamePrompt(context: BranchNameWorkContext, customPrompt = ''): string {
|
|
const sections = [
|
|
'Generate a git branch name that summarizes the coding task described below.',
|
|
'Rules:',
|
|
`- Use between ${MIN_BRANCH_NAME_WORDS} and ${MAX_BRANCH_NAME_WORDS} words.`,
|
|
'- Lowercase kebab-case only (words joined by single hyphens).',
|
|
'- No slashes, no prefixes, no quotes, no trailing punctuation.',
|
|
'- Describe the work itself, not the agent or the repository.',
|
|
'- Output ONLY the branch name on a single line, nothing else.',
|
|
'',
|
|
'User request:',
|
|
context.firstPrompt.trim()
|
|
]
|
|
const assistant = context.assistantMessage?.trim()
|
|
if (assistant) {
|
|
sections.push('', "Agent's initial response:", assistant)
|
|
}
|
|
const prompt = customPrompt.trim()
|
|
if (prompt) {
|
|
sections.push('', 'Additional user prompt:', prompt)
|
|
}
|
|
return sections.join('\n')
|
|
}
|