Last active
October 11, 2025 01:33
-
-
Save shimondoodkin/66572c0888fa089e0f87f1b5e705e052 to your computer and use it in GitHub Desktop.
automating windsurf. auto continue.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| // automating windsurf - continuation. | |
| // current variation of this code is for gpt5-codex model when doing spec-kit. | |
| // it was always showing recommended action but not doing them. so this is a nudge to do the next steps and recommended actions | |
| // | |
| // while it automates it pretty well there is still a problem of overloaded context. | |
| // and need to creae a new conversation evey some calls. you will see messed up responses and files. | |
| // Windsurf menu in help, Toggle developer tools. | |
| // in Developer tools, see console Tab, In console select window: windsurf cascade panel | |
| // might need to type allow pasting | |
| // | |
| // paste to browser console the javascript: | |
| // tips: | |
| // if in future versions the selectors not match well. | |
| // 1. may use the js: debugger; put it as first statment in the function | |
| // statment to stop the debugger on it to find which line is not working. | |
| // hold cursor above variable and see if it gets a value after execution. | |
| // 2 to get a new selectror. first click on point and click element selector | |
| // (on left top edge of devtools there is arrow in square) then point on | |
| // the wanted element or section in the ide, then in the elements tab | |
| // right-click on the selected element and from menu: copy> copy js path, | |
| // then paste the copied path to console | |
| // to make the selector useful if selected too deep trim a little bit remove few selectors at the end. | |
| // or if the selector too specific modify it to make it more general. | |
| // or adapt to your needs , i changed it to have nth-last-child(1) selector in few places | |
| getInput = () => { | |
| const button = document.querySelector("#chat > div div.panel-border button[type=submit]"); | |
| const buttonEnabled = ![...button.classList].includes("cursor-not-allowed"); | |
| const isStop = !!button.querySelector("svg > rect") | |
| const reactProps = button[Object.keys(button).filter(name => name.startsWith('__reactProps'))[0]] | |
| const insertText = (text) => { | |
| const input = document.querySelector("#chat > div div.panel-border button[type=submit]").parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.querySelector("div[contenteditable] p") | |
| input.focus(); | |
| document.execCommand("insertText", false, text); | |
| } | |
| const fakeReactClickEvent = { | |
| nativeEvent: new MouseEvent("click"), | |
| preventDefault: () => { }, | |
| stopPropagation: () => { }, | |
| target: el, | |
| currentTarget: el, | |
| type: "click", | |
| isDefaultPrevented: () => false, | |
| isPropagationStopped: () => false, | |
| }; | |
| return { isStop, buttonEnabled, reactProps, insertText, fakeReactClickEvent } | |
| }; | |
| getContent = () => { | |
| // in list of responses: | |
| const listOfResponses=document.querySelector("#chat > div > div > div > div.overflow-y-hidden.overflow-x-clip > div.flex > div > div > div > div.flex") | |
| // div:nth-last-child(1) | |
| const lastElement = listOfResponses.children[listOfResponses.children.length-1] | |
| //div:nth-last-child(2) | |
| const preLastElement = listOfResponses.children[listOfResponses.children.length-2] | |
| // if last response contains form for feedback, responding was finished | |
| const feedback = lastElement?.querySelector("div > div.flex.flex-1.flex-row.items-center.justify-between.gap-1 > div.flex.flex-1.flex-row.items-center.gap-1\\.5 > div > span") | |
| let outputCompleted = feedback?.innerText == "Feedback submitted" | |
| let lastResponseTitles = []; | |
| let lastSubResponses = undefined; | |
| let lastSubResponse = undefined; | |
| let lastThinking = undefined; | |
| let lastText = undefined; | |
| let runButton = undefined; | |
| let runButtonReactProps = undefined; | |
| let runCommand = undefined; | |
| let runRow = undefined; | |
| // if last response contains form for feedback then tak one before last | |
| const lastResponse = outputCompleted ? preLastElement : lastElement | |
| if (!lastResponse) { | |
| outputCompleted = false; | |
| } | |
| else { | |
| // each response composed of subresponses = wrapper > children | |
| lastSubResponses = lastResponse?.children[0].children | |
| // each sub response has a wrapper > two elements, [ thinking , response ] | |
| lastSubResponse = lastSubResponses?.[lastSubResponses.length - 1] | |
| if (lastSubResponse?.children[0]?.children?.length == 2) { | |
| lastThinking = lastSubResponse?.children[0]?.children[0] | |
| lastText = lastSubResponse?.children[0]?.children[1] | |
| } | |
| else { | |
| // no thinking | |
| lastThinking = undefined | |
| lastText = lastSubResponse?.children[0]?.children[0] | |
| } | |
| if (lastText) { | |
| lastResponseTitles = [...lastText?.querySelectorAll("h1,h2,h3")].map(x => x.innerText) | |
| } | |
| runButton = [...lastElement?.querySelectorAll("button")]?.filter(x => x?.innerText?.includes("RunAlt+⏎"))?.[0] | |
| if(runButton){ | |
| runRow=runButton.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode; | |
| runCommand = runRow?.querySelector("pre.whitespace-pre-wrap")?.innerHTML?.split('</span>')?.pop() | |
| runButtonReactProps = runButton?.[Object.keys(runButton).filter(name => name.startsWith('__reactProps'))[0]] | |
| } | |
| } | |
| return { | |
| outputCompleted, lastResponseTitles, lastText, runButton, runButtonReactProps, runCommand, | |
| lastElement, preLastElement, feedback, lastResponse, lastSubResponses, lastSubResponse, lastThinking, lastResponse, runRow | |
| } | |
| }; | |
| wait = (t) => new Promise(r => setTimeout(r, t)); | |
| stop1 = () => { window.continueEnabled = false; if (window.checkContinueTimeout) if (window.checkContinueTimeout) { clearTimeout(window.checkContinueTimeout) } } | |
| checkContinue = async (autoRun) => { | |
| try { | |
| if (window.checkContinueTimeout) { clearTimeout(window.checkContinueTimeout) } | |
| if (autoRun && !window.continueEnabled) { | |
| return | |
| } | |
| else { | |
| window.continueEnabled = true | |
| } | |
| let content = getContent() | |
| if (content.outputCompleted) { | |
| if (content.lastResponseTitles.join('\n').includes('Recommended Actions') && content.lastResponseTitles.join('\n').includes('Next Steps')) { | |
| let input; | |
| input = getInput() | |
| input.insertText("Do the Recommended Actions and Next Steps") | |
| await wait(1000) | |
| input = getInput() | |
| input.reactProps.onClick(input.fakeReactClickEvent) | |
| await wait(3000) | |
| } | |
| else if (content.lastResponseTitles.join('\n').includes('Recommended Actions')) { | |
| let input; | |
| input = getInput() | |
| input.insertText("Do the Recommended Actions") | |
| await wait(1000) | |
| input = getInput() | |
| input.reactProps.onClick(input.fakeReactClickEvent) | |
| await wait(3000) | |
| } | |
| else if (content.lastResponseTitles.join('\n').includes('Next Steps')) { | |
| let input; | |
| input = getInput() | |
| input.insertText("Do the Next Steps") | |
| await wait(1000) | |
| input = getInput() | |
| input.reactProps.onClick(input.fakeReactClickEvent) | |
| await wait(3000) | |
| } | |
| else if (content.lastResponseTitles.join('\n').includes('Remaining Work')) { | |
| let input; | |
| input = getInput() | |
| input.insertText("Do the Remaining Work") | |
| await wait(1000) | |
| input = getInput() | |
| input.reactProps.onClick(input.fakeReactClickEvent) | |
| await wait(3000) | |
| } | |
| else if (content.lastResponseTitles.join('\n').includes('Follow-up')) { | |
| let input; | |
| input = getInput() | |
| input.insertText("Continue") | |
| await wait(1000) | |
| input = getInput() | |
| input.reactProps.onClick(input.fakeReactClickEvent) | |
| await wait(3000) | |
| } | |
| } | |
| else if (content.runCommand) { | |
| if (content.runCommand.startsWith('npx')) content.runButtonReactProps.onClick(); | |
| else if (content.runCommand.startsWith('Get-Content')) content.runButtonReactProps.onClick(); | |
| else if (content.runCommand.startsWith('flutter')) content.runButtonReactProps.onClick(); | |
| else if (content.runCommand.startsWith('dart')) content.runButtonReactProps.onClick(); | |
| else if (content.runCommand.startsWith('node')) content.runButtonReactProps.onClick(); | |
| await wait(3000) | |
| } | |
| window.checkContinueTimeout = setTimeout(() => checkContinue(true), 1000) | |
| } catch (e) { | |
| console.log('auto continue error', e.stack) | |
| window.continueEnabled = false | |
| } | |
| } | |
| checkContinue() | |
| content1 = getContent() | |
| ainput=getInput() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment