mirror of
https://github.com/harivansh-afk/sandbox-agent.git
synced 2026-04-18 15:03:06 +00:00
* Restore foundry onboarding stack * Consolidate foundry rename * Create foundry tasks without prompts * Rename Foundry handoffs to tasks
122 lines
3.5 KiB
TypeScript
122 lines
3.5 KiB
TypeScript
export interface ResolveCreateFlowDecisionInput {
|
|
task: string;
|
|
explicitTitle?: string;
|
|
explicitBranchName?: string;
|
|
localBranches: string[];
|
|
taskBranches: string[];
|
|
}
|
|
|
|
export interface ResolveCreateFlowDecisionResult {
|
|
title: string;
|
|
branchName: string;
|
|
}
|
|
|
|
function firstNonEmptyLine(input: string): string {
|
|
const lines = input
|
|
.split(/\r?\n/)
|
|
.map((line) => line.trim())
|
|
.filter((line) => line.length > 0);
|
|
return lines[0] ?? "";
|
|
}
|
|
|
|
export function deriveFallbackTitle(task: string, explicitTitle?: string): string {
|
|
const source = (explicitTitle && explicitTitle.trim()) || firstNonEmptyLine(task) || "update task";
|
|
const explicitPrefixMatch = source.match(/^\s*(feat|fix|docs|refactor):\s+(.+)$/i);
|
|
if (explicitPrefixMatch) {
|
|
const explicitTypePrefix = explicitPrefixMatch[1]!.toLowerCase();
|
|
const explicitSummary = explicitPrefixMatch[2]!
|
|
.split("")
|
|
.map((char) => (/^[a-zA-Z0-9 -]$/.test(char) ? char : " "))
|
|
.join("")
|
|
.split(/\s+/)
|
|
.filter((token) => token.length > 0)
|
|
.join(" ")
|
|
.slice(0, 62)
|
|
.trim();
|
|
|
|
return `${explicitTypePrefix}: ${explicitSummary || "update task"}`;
|
|
}
|
|
|
|
const lowered = source.toLowerCase();
|
|
|
|
const typePrefix =
|
|
lowered.includes("fix") || lowered.includes("bug")
|
|
? "fix"
|
|
: lowered.includes("doc") || lowered.includes("readme")
|
|
? "docs"
|
|
: lowered.includes("refactor")
|
|
? "refactor"
|
|
: "feat";
|
|
|
|
const cleaned = source
|
|
.split("")
|
|
.map((char) => (/^[a-zA-Z0-9 -]$/.test(char) ? char : " "))
|
|
.join("")
|
|
.split(/\s+/)
|
|
.filter((token) => token.length > 0)
|
|
.join(" ");
|
|
|
|
const summary = (cleaned || "update task").slice(0, 62).trim();
|
|
return `${typePrefix}: ${summary}`.trim();
|
|
}
|
|
|
|
export function sanitizeBranchName(input: string): string {
|
|
const normalized = input
|
|
.toLowerCase()
|
|
.split("")
|
|
.map((char) => (/^[a-z0-9]$/.test(char) ? char : "-"))
|
|
.join("");
|
|
|
|
let result = "";
|
|
let previousDash = false;
|
|
for (const char of normalized) {
|
|
if (char === "-") {
|
|
if (!previousDash && result.length > 0) {
|
|
result += char;
|
|
}
|
|
previousDash = true;
|
|
continue;
|
|
}
|
|
|
|
result += char;
|
|
previousDash = false;
|
|
}
|
|
|
|
const trimmed = result.replace(/-+$/g, "");
|
|
if (trimmed.length <= 50) {
|
|
return trimmed;
|
|
}
|
|
return trimmed.slice(0, 50).replace(/-+$/g, "");
|
|
}
|
|
|
|
export function resolveCreateFlowDecision(input: ResolveCreateFlowDecisionInput): ResolveCreateFlowDecisionResult {
|
|
const explicitBranch = input.explicitBranchName?.trim();
|
|
const title = deriveFallbackTitle(input.task, input.explicitTitle);
|
|
const generatedBase = sanitizeBranchName(title) || "task";
|
|
|
|
const branchBase = explicitBranch && explicitBranch.length > 0 ? explicitBranch : generatedBase;
|
|
|
|
const existingBranches = new Set(input.localBranches.map((value) => value.trim()).filter((value) => value.length > 0));
|
|
const existingTaskBranches = new Set(input.taskBranches.map((value) => value.trim()).filter((value) => value.length > 0));
|
|
const conflicts = (name: string): boolean => existingBranches.has(name) || existingTaskBranches.has(name);
|
|
|
|
if (explicitBranch && conflicts(branchBase)) {
|
|
throw new Error(`Branch '${branchBase}' already exists. Choose a different --name/--branch value.`);
|
|
}
|
|
|
|
if (explicitBranch) {
|
|
return { title, branchName: branchBase };
|
|
}
|
|
|
|
let candidate = branchBase;
|
|
let index = 2;
|
|
while (conflicts(candidate)) {
|
|
candidate = `${branchBase}-${index}`;
|
|
index += 1;
|
|
}
|
|
|
|
return {
|
|
title,
|
|
branchName: candidate,
|
|
};
|
|
}
|