import type { CommandMode, ParsedCommand, WorkPackage } from "./work-package-types"; const commandModes: CommandMode[] = ["ask", "plan", "change", "execute"]; export function parseWorkPackageCommand( input: string, workPackages: WorkPackage[], ): ParsedCommand | null { const trimmedInput = input.trim(); if (!trimmedInput.startsWith("@")) { return null; } const mentionBody = trimmedInput.slice(1); const candidates = workPackages .flatMap((workPackage) => [ { name: workPackage.title, workPackage }, { name: workPackage.shortName, workPackage }, ]) .sort((left, right) => right.name.length - left.name.length); for (const candidate of candidates) { const packageName = candidate.name.toLowerCase(); const inputName = mentionBody.toLowerCase(); if (!inputName.startsWith(packageName)) { continue; } const boundaryCharacter = mentionBody[candidate.name.length]; const isBoundary = boundaryCharacter === undefined || boundaryCharacter === " " || boundaryCharacter === "#" || boundaryCharacter === ":"; if (!isBoundary) { continue; } const remainingText = mentionBody.slice(candidate.name.length).trim(); const [maybeMode, ...instructionParts] = remainingText.split(/\s+/); const mode = commandModes.includes(maybeMode as CommandMode) ? (maybeMode as CommandMode) : "ask"; return { referencedPackageName: candidate.name, workPackageId: candidate.workPackage.id, mode, instruction: mode === maybeMode ? instructionParts.join(" ").trim() : remainingText.replace(/^#\S+\s*/, "").trim(), }; } return { mode: "ask", instruction: trimmedInput, }; } export function getPackageSuggestions(input: string, workPackages: WorkPackage[]) { const match = input.match(/@([A-Za-z\s-]*)$/); if (!match) { return []; } const query = match[1].toLowerCase().trim(); return workPackages .filter((workPackage) => { if (!query) { return true; } return ( workPackage.title.toLowerCase().includes(query) || workPackage.shortName.toLowerCase().includes(query) ); }) .slice(0, 6); }