combineCommandSequences.ts 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273
  1. import { ClineMessage } from "./ExtensionMessage"
  2. /**
  3. * Combines sequences of command and command_output messages in an array of ClineMessages.
  4. *
  5. * This function processes an array of ClineMessages objects, looking for sequences
  6. * where a 'command' message is followed by one or more 'command_output' messages.
  7. * When such a sequence is found, it combines them into a single message, merging
  8. * their text contents.
  9. *
  10. * @param messages - An array of ClineMessage objects to process.
  11. * @returns A new array of ClineMessage objects with command sequences combined.
  12. *
  13. * @example
  14. * const messages: ClineMessage[] = [
  15. * { type: 'ask', ask: 'command', text: 'ls', ts: 1625097600000 },
  16. * { type: 'ask', ask: 'command_output', text: 'file1.txt', ts: 1625097601000 },
  17. * { type: 'ask', ask: 'command_output', text: 'file2.txt', ts: 1625097602000 }
  18. * ];
  19. * const result = simpleCombineCommandSequences(messages);
  20. * // Result: [{ type: 'ask', ask: 'command', text: 'ls\nfile1.txt\nfile2.txt', ts: 1625097600000 }]
  21. */
  22. export function combineCommandSequences(messages: ClineMessage[]): ClineMessage[] {
  23. const combinedCommands: ClineMessage[] = []
  24. // First pass: combine commands with their outputs
  25. for (let i = 0; i < messages.length; i++) {
  26. if (messages[i].type === "ask" && messages[i].ask === "command") {
  27. let combinedText = messages[i].text || ""
  28. let didAddOutput = false
  29. let j = i + 1
  30. while (j < messages.length) {
  31. if (messages[j].type === "ask" && messages[j].ask === "command") {
  32. // Stop if we encounter the next command
  33. break
  34. }
  35. if (messages[j].ask === "command_output" || messages[j].say === "command_output") {
  36. if (!didAddOutput) {
  37. // Add a newline before the first output
  38. combinedText += `\n${COMMAND_OUTPUT_STRING}`
  39. didAddOutput = true
  40. }
  41. // handle cases where we receive empty command_output (ie when extension is relinquishing control over exit command button)
  42. const output = messages[j].text || ""
  43. if (output.length > 0) {
  44. combinedText += output
  45. }
  46. }
  47. j++
  48. }
  49. combinedCommands.push({
  50. ...messages[i],
  51. text: combinedText,
  52. })
  53. i = j - 1 // Move to the index just before the next command or end of array
  54. }
  55. }
  56. // Second pass: remove command_outputs and replace original commands with combined ones
  57. return messages
  58. .filter((msg) => !(msg.ask === "command_output" || msg.say === "command_output"))
  59. .map((msg) => {
  60. if (msg.type === "ask" && msg.ask === "command") {
  61. const combinedCommand = combinedCommands.find((cmd) => cmd.ts === msg.ts)
  62. return combinedCommand || msg
  63. }
  64. return msg
  65. })
  66. }
  67. export const COMMAND_OUTPUT_STRING = "Output:"