CoolFace
Datasetpublic

basant307/AI_Governance_Project

sourceHugging Faceapache-2.0updated 2mo agoView on Hugging Face
0likes48downloads
commands.ts173 linesDownload Raw Back to utils
1/**2 * @license3 * Copyright 2025 Google LLC4 * SPDX-License-Identifier: Apache-2.05 */6 7import { CommandKind, type SlashCommand } from '../ui/commands/types.js';8 9/** Maximum number of stacked skill commands that can be loaded in one prompt. */10export const MAX_STACKED_SKILLS = 5;11 12export type ParsedSlashCommand = {13  commandToExecute: SlashCommand | undefined;14  args: string;15  canonicalPath: string[];16};17 18export type ParsedStackedSkillCommands = {19  /** All matched skill commands (up to MAX_STACKED_SKILLS). */20  skills: SlashCommand[];21  /** Text remaining after the last matched skill token. */22  remainingText: string;23  /** True when more than MAX_STACKED_SKILLS leading tokens were found. */24  exceededMax: boolean;25};26 27/**28 * Parses a raw slash command string into its command, arguments, and canonical path.29 * If no valid command is found, the `commandToExecute` property will be `undefined`.30 *31 * @param query The raw input string, e.g., "/config set theme dark" or "/help".32 * @param commands The list of available top-level slash commands.33 * @returns An object containing the resolved command, its arguments, and its canonical path.34 */35export const parseSlashCommand = (36  query: string,37  commands: readonly SlashCommand[],38): ParsedSlashCommand => {39  const trimmed = query.trim();40 41  const commandText = trimmed.substring(1).trim();42  const parts = commandText.split(/\s+/);43  const commandPath = parts.filter((p) => p); // The parts of the command, e.g., ['memory', 'add']44 45  let currentCommands = commands;46  let commandToExecute: SlashCommand | undefined;47  let pathIndex = 0;48  const canonicalPath: string[] = [];49  let argsStart = 0;50 51  for (const part of commandPath) {52    // TODO: For better performance and architectural clarity, this two-pass53    // search could be replaced. A more optimal approach would be to54    // pre-compute a single lookup map in `CommandService.ts` that resolves55    // all name and alias conflicts during the initial loading phase. The56    // processor would then perform a single, fast lookup on that map.57 58    // First pass: check for an exact match on the primary command name.59    let foundCommand = currentCommands.find((cmd) => cmd.name === part);60 61    // Second pass: if no primary name matches, check for an alias.62    if (!foundCommand) {63      foundCommand = currentCommands.find((cmd) =>64        cmd.altNames?.includes(part),65      );66    }67 68    if (foundCommand) {69      commandToExecute = foundCommand;70      canonicalPath.push(foundCommand.name);71      pathIndex++;72      const partIndex = commandText.indexOf(part, argsStart);73      argsStart = partIndex + part.length;74      if (foundCommand.subCommands) {75        currentCommands = foundCommand.subCommands;76      } else {77        break;78      }79    } else {80      break;81    }82  }83 84  const args = commandToExecute85    ? commandText.slice(argsStart).trim()86    : parts.slice(pathIndex).join(' ');87 88  return { commandToExecute, args, canonicalPath };89};90 91/**92 * Detects multiple leading `/skill-name` tokens in user input.93 *94 * For input like `/feat-dev /e2e-testing implement X`, returns all matched95 * skill commands (up to MAX_STACKED_SKILLS) and the remaining text.96 *97 * Only matches commands with `kind === CommandKind.SKILL`. Stops at the first98 * non-skill token or unmatched `/token`.99 *100 * @param query The raw input string starting with `/`.101 * @param commands The list of available slash commands.102 * @returns Matched skill commands and the remaining text after them.103 */104export const parseStackedSlashCommands = (105  query: string,106  commands: readonly SlashCommand[],107): ParsedStackedSkillCommands => {108  const trimmed = query.trim();109  if (!trimmed.startsWith('/')) {110    return { skills: [], remainingText: trimmed, exceededMax: false };111  }112 113  const commandText = trimmed.substring(1);114  const skills: SlashCommand[] = [];115  let pos = 0;116  let restPos = 0;117  let exceededMax = false;118 119  while (pos < commandText.length) {120    // Skip whitespace between tokens (matches spaces, tabs, etc.).121    while (pos < commandText.length && /\s/.test(commandText[pos]!)) pos++;122    if (pos >= commandText.length) {123      restPos = pos;124      break;125    }126 127    const tokenStart = pos;128    while (pos < commandText.length && !/\s/.test(commandText[pos]!)) pos++;129    const token = commandText.slice(tokenStart, pos);130 131    if (skills.length === 0) {132      if (token.startsWith('/')) break;133      const cmd = findCommandByName(token, commands);134      if (!cmd || cmd.kind !== CommandKind.SKILL) break;135      skills.push(cmd);136      restPos = pos;137      continue;138    }139 140    if (!token.startsWith('/')) break;141    const name = token.substring(1);142    if (!name) break;143 144    const cmd = findCommandByName(name, commands);145    if (!cmd || cmd.kind !== CommandKind.SKILL) break;146 147    if (skills.length >= MAX_STACKED_SKILLS) {148      exceededMax = true;149      break;150    }151 152    skills.push(cmd);153    restPos = pos;154  }155 156  if (skills.length < 2) {157    return { skills: [], remainingText: trimmed, exceededMax: false };158  }159 160  const afterSkills = commandText.slice(restPos).trim();161  return { skills, remainingText: afterSkills, exceededMax };162};163 164function findCommandByName(165  name: string,166  commands: readonly SlashCommand[],167): SlashCommand | undefined {168  return (169    commands.find((cmd) => cmd.name === name) ??170    commands.find((cmd) => cmd.altNames?.includes(name))171  );172}173 
basant307/AI_Governance_Project · CoolFace