#!/usr/bin/osascript
--------------------------------------------------------------------------------
-- terminator.scpt - v0.6.1 Enhanced "T-1000"
-- Enhanced Terminal session management with smart session reuse and better error reporting
-- Features: Smart session reuse, enhanced error reporting, improved timing, better output formatting
--------------------------------------------------------------------------------

--#region Configuration Properties
property maxCommandWaitTime : 15.0 -- Increased from 10.0 for better reliability
property pollIntervalForBusyCheck : 0.1 
property startupDelayForTerminal : 0.7 
property minTailLinesOnWrite : 100 -- Increased from 15 for better build log visibility  
property defaultTailLines : 100 -- Increased from 30 for better build log visibility
property tabTitlePrefix : "🤖💥 " -- For the window/tab title itself
property scriptInfoPrefix : "Terminator 🤖💥: " -- For messages generated by this script
property projectIdentifierInTitle : "Project: " 
property taskIdentifierInTitle : " - Task: "   
property enableFuzzyTagGrouping : true 
property fuzzyGroupingMinPrefixLength : 4 

-- Safe enhanced properties (minimal additions)
property enhancedErrorReporting : true
property verboseLogging : false
--#endregion Configuration Properties

--#region Helper Functions
on isValidPath(thePath)
    if thePath is not "" and (thePath starts with "/") then
        if not (thePath contains " -") then -- Basic heuristic
            return true
        end if
    end if
    return false
end isValidPath

on getPathComponent(thePath, componentIndex)
    set oldDelims to AppleScript's text item delimiters
    set AppleScript's text item delimiters to "/"
    set pathParts to text items of thePath
    set AppleScript's text item delimiters to oldDelims
    set nonEmptyParts to {}
    repeat with aPart in pathParts
        if aPart is not "" then set end of nonEmptyParts to aPart
    end repeat
    if (count nonEmptyParts) = 0 then return ""
    try
        if componentIndex is -1 then
            return item -1 of nonEmptyParts
        else if componentIndex > 0 and componentIndex ≤ (count nonEmptyParts) then
            return item componentIndex of nonEmptyParts
        end if
    on error
        return ""
    end try
    return ""
end getPathComponent

on generateWindowTitle(taskTag as text, projectGroup as text)
    if projectGroup is not "" then
        return tabTitlePrefix & projectIdentifierInTitle & projectGroup & taskIdentifierInTitle & taskTag
    else
        return tabTitlePrefix & taskTag 
    end if
end generateWindowTitle

on bufferContainsMeaningfulContentAS(multiLineText, knownInfoPrefix as text, commonShellPrompts as list)
    if multiLineText is "" then return false
    
    -- Simple approach: if the trimmed content is substantial and not just our info messages, consider it meaningful
    set trimmedText to my trimWhitespace(multiLineText)
    if (length of trimmedText) < 3 then return false
    
    -- Check if it's only our script info messages
    if trimmedText starts with knownInfoPrefix then
        -- If it's ONLY our message and nothing else meaningful, return false
        set oldDelims to AppleScript's text item delimiters
        set AppleScript's text item delimiters to linefeed
        set textLines to text items of multiLineText
        set AppleScript's text item delimiters to oldDelims
        
        set nonInfoLines to 0
        repeat with aLine in textLines
            set currentLine to my trimWhitespace(aLine as text)
            if currentLine is not "" and not (currentLine starts with knownInfoPrefix) then
                set nonInfoLines to nonInfoLines + 1
            end if
        end repeat
        
        -- If we have substantial non-info content, consider it meaningful
        return (nonInfoLines > 2)
    end if
    
    -- If content doesn't start with our info prefix, likely contains command output
    return true
end bufferContainsMeaningfulContentAS

-- Enhanced error reporting helper
on formatErrorMessage(errorType, errorMsg, context)
    if enhancedErrorReporting then
        set formattedMsg to scriptInfoPrefix & errorType & ": " & errorMsg
        if context is not "" then
            set formattedMsg to formattedMsg & " (Context: " & context & ")"
        end if
        return formattedMsg
    else
        return scriptInfoPrefix & errorMsg
    end if
end formatErrorMessage

-- Enhanced logging helper
on logVerbose(message)
    if verboseLogging then
        log "🔍 " & message
    end if
end logVerbose
--#endregion Helper Functions

--#region Main Script Logic (on run)
on run argv
    set appSpecificErrorOccurred to false
    try
        my logVerbose("Starting Terminator v0.6.0 Safe Enhanced")
        
        tell application "System Events"
            if not (exists process "Terminal") then
                launch application id "com.apple.Terminal"
                delay startupDelayForTerminal
            end if
        end tell

        set originalArgCount to count argv
        if originalArgCount < 1 then return my usageText()

        set projectPathArg to ""
        set actualArgsForParsing to argv
        if originalArgCount > 0 then
            set potentialPath to item 1 of argv
            if my isValidPath(potentialPath) then
                set projectPathArg to potentialPath
                my logVerbose("Detected project path: " & projectPathArg)
                if originalArgCount > 1 then
                    set actualArgsForParsing to items 2 thru -1 of argv
                else
                    return my formatErrorMessage("Argument Error", "Project path \"" & projectPathArg & "\" provided, but no task tag or command specified." & linefeed & linefeed & my usageText(), "")
                end if
            end if
        end if

        if (count actualArgsForParsing) < 1 then return my usageText()

        set taskTagName to item 1 of actualArgsForParsing 
        my logVerbose("Task tag: " & taskTagName)
        
        if (length of taskTagName) > 40 or (not my tagOK(taskTagName)) then
            set errorMsg to "Task Tag missing or invalid: \"" & taskTagName & "\"." & linefeed & linefeed & ¬
                "A 'task tag' (e.g., 'build', 'tests') is a short name (1-40 letters, digits, -, _) " & ¬
                "to identify a specific task, optionally within a project session." & linefeed & linefeed
            return my formatErrorMessage("Validation Error", errorMsg & my usageText(), "tag validation")
        end if

        set doWrite to false
        set shellCmd to ""
        set originalUserShellCmd to "" 
        set currentTailLines to defaultTailLines
        set explicitLinesProvided to false
        set argCountAfterTagOrPath to count actualArgsForParsing
        
        if argCountAfterTagOrPath > 1 then 
            set commandParts to items 2 thru -1 of actualArgsForParsing
            if (count commandParts) > 0 then
                set lastOfCmdParts to item -1 of commandParts
                if my isInteger(lastOfCmdParts) then
                    set currentTailLines to (lastOfCmdParts as integer)
                    set explicitLinesProvided to true
                    my logVerbose("Explicit lines requested: " & currentTailLines)
                    if (count commandParts) > 1 then
                        set commandParts to items 1 thru -2 of commandParts
                    else
                        set commandParts to {}
                    end if
                end if
            end if
            if (count commandParts) > 0 then
                set originalUserShellCmd to my joinList(commandParts, " ")
                my logVerbose("Command detected: " & originalUserShellCmd)
            end if
        else if argCountAfterTagOrPath = 1 then
            -- Only taskTagName was provided after potential projectPathArg
            -- This is a read operation by default.
            my logVerbose("Read-only operation detected")
        end if

        if originalUserShellCmd is not "" and (my trimWhitespace(originalUserShellCmd) is not "") then
            set doWrite to true
            set shellCmd to originalUserShellCmd 
        else if projectPathArg is not "" and originalUserShellCmd is "" then 
            -- Path provided, task tag, and empty command string "" OR no command string but lines_to_read was there
            set doWrite to true 
            set shellCmd to "" -- will become 'cd path'
            my logVerbose("CD-only operation for path: " & projectPathArg)
        else
            set doWrite to false
            set shellCmd to ""
        end if
        
        if currentTailLines < 1 then set currentTailLines to 1
        if doWrite and (shellCmd is not "" or projectPathArg is not "") and currentTailLines < minTailLinesOnWrite then
            set currentTailLines to minTailLinesOnWrite
            my logVerbose("Increased tail lines for write operation: " & currentTailLines)
        end if
        
        if projectPathArg is not "" and doWrite then
            set quotedProjectPath to quoted form of projectPathArg
            if shellCmd is not "" then
                set shellCmd to "cd " & quotedProjectPath & " && " & shellCmd
            else
                set shellCmd to "cd " & quotedProjectPath
            end if
            my logVerbose("Final command: " & shellCmd)
        end if
        
        set derivedProjectGroup to ""
        if projectPathArg is not "" then
            set derivedProjectGroup to my getPathComponent(projectPathArg, -1)
            if derivedProjectGroup is "" then set derivedProjectGroup to "DefaultProject" 
            my logVerbose("Project group: " & derivedProjectGroup)
        end if

        set allowCreation to false
        if doWrite then 
            set allowCreation to true
        else if explicitLinesProvided then 
            set allowCreation to true
        end if

        set effectiveTabTitleForLookup to my generateWindowTitle(taskTagName, derivedProjectGroup)
        my logVerbose("Tab title: " & effectiveTabTitleForLookup)
        
        set tabInfo to my ensureTabAndWindow(taskTagName, derivedProjectGroup, allowCreation, effectiveTabTitleForLookup)

        if tabInfo is missing value then
            if not allowCreation then 
                set errorMsg to "Terminal session \"" & effectiveTabTitleForLookup & "\" not found." & linefeed & ¬
                    "To create this session, provide a command (even an empty string \"\" if only 'cd'-ing to a project path), " & ¬
                    "or specify lines to read (e.g., ... \"" & taskTagName & "\" 1)." & linefeed
                if projectPathArg is not "" then
                    set errorMsg to errorMsg & "Project path was specified as: \"" & projectPathArg & "\"." & linefeed
                else
                    set errorMsg to errorMsg & "If this is for a new project, provide the absolute project path as the first argument." & linefeed
                end if
                return my formatErrorMessage("Session Error", errorMsg & linefeed & my usageText(), "session lookup")
            else 
                return my formatErrorMessage("Creation Error", "Could not find or create Terminal tab for \"" & effectiveTabTitleForLookup & "\". Check permissions/Terminal state.", "tab creation")
            end if
        end if

        set targetTab to targetTab of tabInfo
        set parentWindow to parentWindow of tabInfo
        set wasNewlyCreated to wasNewlyCreated of tabInfo 
        set createdInExistingViaFuzzy to createdInExistingWindowViaFuzzy of tabInfo
        
        my logVerbose("Tab info - new: " & wasNewlyCreated & ", fuzzy: " & createdInExistingViaFuzzy)

        set bufferText to ""
        set commandTimedOut to false
        set tabWasBusyOnRead to false
        set previousCommandActuallyStopped to true 
        set attemptMadeToStopPreviousCommand to false
        set identifiedBusyProcessName to ""
        set theTTYForInfo to "" 

        if not doWrite and wasNewlyCreated then
            if createdInExistingViaFuzzy then
                return scriptInfoPrefix & "New tab \"" & effectiveTabTitleForLookup & "\" created in existing project window and ready."
            else
                return scriptInfoPrefix & "New tab \"" & effectiveTabTitleForLookup & "\" (in new window) created and ready."
            end if
        end if

        tell application id "com.apple.Terminal"
            try
                set index of parentWindow to 1
                set selected tab of parentWindow to targetTab
                if wasNewlyCreated and doWrite then 
                    delay 0.4 
                else
                    delay 0.1 
                end if

                if doWrite and shellCmd is not "" then 
                    my logVerbose("Executing command: " & shellCmd)
                    set canProceedWithWrite to true 
                    if busy of targetTab then
                        if not wasNewlyCreated or createdInExistingViaFuzzy then 
                            set attemptMadeToStopPreviousCommand to true
                            set previousCommandActuallyStopped to false 
                            try
                                set theTTYForInfo to my trimWhitespace(tty of targetTab)
                            end try
                            set processesBefore to {}
                            try
                                set processesBefore to processes of targetTab
                            end try
                            set commonShells to {"login", "bash", "zsh", "sh", "tcsh", "ksh", "-bash", "-zsh", "-sh", "-tcsh", "-ksh", "dtterm", "fish"}
                            set identifiedBusyProcessName to "" 
                            if (count of processesBefore) > 0 then
                                repeat with i from (count of processesBefore) to 1 by -1
                                    set aProcessName to item i of processesBefore
                                    if aProcessName is not in commonShells then
                                        set identifiedBusyProcessName to aProcessName
                                        exit repeat
                                    end if
                                end repeat
                            end if
                            my logVerbose("Busy process identified: " & identifiedBusyProcessName)
                            set processToTargetForKill to identifiedBusyProcessName
                            set killedViaPID to false
                            if theTTYForInfo is not "" and processToTargetForKill is not "" then
                                set shortTTY to text 6 thru -1 of theTTYForInfo 
                                set pidsToKillText to ""
                                try
                                    set psCommand to "ps -t " & shortTTY & " -o pid,comm | awk '$2 == \"" & processToTargetForKill & "\" {print $1}'"
                                    set pidsToKillText to do shell script psCommand
                                end try
                                if pidsToKillText is not "" then
                                    set oldDelims to AppleScript's text item delimiters
                                    set AppleScript's text item delimiters to linefeed
                                    set pidList to text items of pidsToKillText
                                    set AppleScript's text item delimiters to oldDelims
                                    repeat with aPID in pidList
                                        set aPID to my trimWhitespace(aPID)
                                        if aPID is not "" then
                                            try
                                                do shell script "kill -INT " & aPID
                                                delay 0.3 
                                                do shell script "kill -0 " & aPID 
                                                try
                                                    do shell script "kill -KILL " & aPID
                                                    delay 0.2
                                                    try
                                                        do shell script "kill -0 " & aPID
                                                    on error 
                                                        set previousCommandActuallyStopped to true
                                                    end try
                                                end try
                                            on error 
                                                set previousCommandActuallyStopped to true
                                            end try
                                        end if
                                        if previousCommandActuallyStopped then
                                            set killedViaPID to true
                                            exit repeat 
                                        end if
                                    end repeat
                                end if
                            end if
                            if not previousCommandActuallyStopped and busy of targetTab then 
                                activate 
                                delay 0.5 
                                tell application "System Events" to keystroke "c" using control down
                                delay 0.6 
                                if not (busy of targetTab) then
                                    set previousCommandActuallyStopped to true 
                                    if identifiedBusyProcessName is not "" and (identifiedBusyProcessName is in (processes of targetTab)) then
                                        set previousCommandActuallyStopped to false 
                                    end if
                                end if
                            else if not busy of targetTab then 
                                 set previousCommandActuallyStopped to true
                            end if
                            if not previousCommandActuallyStopped then
                                set canProceedWithWrite to false
                            end if
                        else if wasNewlyCreated and not createdInExistingViaFuzzy and busy of targetTab then
                            delay 0.4 
                            if busy of targetTab then
                                set attemptMadeToStopPreviousCommand to true 
                                set previousCommandActuallyStopped to false 
                                set identifiedBusyProcessName to "extended initialization"
                                set canProceedWithWrite to false
                            else
                                set previousCommandActuallyStopped to true 
                            end if
                        end if
                    end if 

                    if canProceedWithWrite then 
                        -- Clear before write to prevent output truncation (only for reused tabs)
                        if not wasNewlyCreated then
                            do script "clear" in targetTab
                            delay 0.1
                        end if
                        do script shellCmd in targetTab 
                        set commandStartTime to current date
                        set commandFinished to false
                        repeat while ((current date) - commandStartTime) < maxCommandWaitTime
                            if not (busy of targetTab) then
                                set commandFinished to true
                                exit repeat
                            end if
                            delay pollIntervalForBusyCheck 
                        end repeat
                        if not commandFinished then set commandTimedOut to true
                        if commandFinished then delay 0.2 -- Increased from 0.1 for better output settling
                        my logVerbose("Command execution completed, timeout: " & commandTimedOut)
                    end if
                else if not doWrite then 
                    if busy of targetTab then
                        set tabWasBusyOnRead to true
                        try
                            set theTTYForInfo to my trimWhitespace(tty of targetTab)
                        end try
                        set processesReading to processes of targetTab
                        set commonShells to {"login", "bash", "zsh", "sh", "tcsh", "ksh", "-bash", "-zsh", "-sh", "-tcsh", "-ksh", "dtterm", "fish"}
                        set identifiedBusyProcessName to "" 
                        if (count of processesReading) > 0 then
                            repeat with i from (count of processesReading) to 1 by -1
                                set aProcessName to item i of processesReading
                                if aProcessName is not in commonShells then
                                    set identifiedBusyProcessName to aProcessName
                                    exit repeat
                                end if
                            end repeat
                        end if
                        my logVerbose("Tab busy during read with: " & identifiedBusyProcessName)
                    end if
                end if
                
                set bufferText to history of targetTab
            on error errMsg number errNum
                set appSpecificErrorOccurred to true
                return my formatErrorMessage("Terminal Error", errMsg, "error " & errNum)
            end try
        end tell

        set appendedMessage to ""
        set ttyInfoStringForMessage to "" 
        if theTTYForInfo is not "" then set ttyInfoStringForMessage to " (TTY " & theTTYForInfo & ")"
        if attemptMadeToStopPreviousCommand then
            set processNameToReport to "process"
            if identifiedBusyProcessName is not "" and identifiedBusyProcessName is not "extended initialization" then
                set processNameToReport to "'" & identifiedBusyProcessName & "'"
            else if identifiedBusyProcessName is "extended initialization" then
                set processNameToReport to "tab's extended initialization"
            end if
            if previousCommandActuallyStopped then
                set appendedMessage to linefeed & scriptInfoPrefix & "Previous " & processNameToReport & ttyInfoStringForMessage & " was interrupted. ---"
            else
                set appendedMessage to linefeed & scriptInfoPrefix & "Attempted to interrupt previous " & processNameToReport & ttyInfoStringForMessage & ", but it may still be running. New command NOT executed. ---"
            end if
        end if
        if commandTimedOut then 
            set cmdForMsg to originalUserShellCmd
            if projectPathArg is not "" and originalUserShellCmd is not "" then set cmdForMsg to originalUserShellCmd & " (in " & projectPathArg & ")"
            if projectPathArg is not "" and originalUserShellCmd is "" then set cmdForMsg to "(cd " & projectPathArg & ")"
            set appendedMessage to appendedMessage & linefeed & scriptInfoPrefix & "Command '" & cmdForMsg & "' may still be running. Returned after " & maxCommandWaitTime & "s timeout. ---"
        else if tabWasBusyOnRead then 
            set processNameToReportOnRead to "process"
            if identifiedBusyProcessName is not "" then set processNameToReportOnRead to "'" & identifiedBusyProcessName & "'"
            set busyProcessInfoString to ""
            if identifiedBusyProcessName is not "" then set busyProcessInfoString to " with " & processNameToReportOnRead
            set appendedMessage to appendedMessage & linefeed & scriptInfoPrefix & "Tab" & ttyInfoStringForMessage & " was busy" & busyProcessInfoString & " during read. Output may be from an ongoing process. ---"
        end if

        if appendedMessage is not "" then
            if bufferText is "" then
                set bufferText to my trimWhitespace(appendedMessage)
            else
                set bufferText to bufferText & appendedMessage
            end if
        end if
        
        set tailedOutput to my tailBufferAS(bufferText, currentTailLines)
        set finalResult to my trimBlankLinesAS(tailedOutput)

        if finalResult is "" then
            set effectiveOriginalCmdForMsg to originalUserShellCmd
            if projectPathArg is not "" and originalUserShellCmd is "" then
                 set effectiveOriginalCmdForMsg to "(cd " & projectPathArg & ")"
            else if projectPathArg is not "" and originalUserShellCmd is not "" then
                 set effectiveOriginalCmdForMsg to originalUserShellCmd & " (in " & projectPathArg & ")"
            end if

            set baseMsgInfo to "Session \"" & effectiveTabTitleForLookup & "\", requested " & currentTailLines & " lines."
            set specificAppendedInfo to my trimWhitespace(appendedMessage)
            set suffixForReturn to ""
            if specificAppendedInfo is not "" then set suffixForReturn to linefeed & specificAppendedInfo
            
            if attemptMadeToStopPreviousCommand and not previousCommandActuallyStopped then
                 return my formatErrorMessage("Process Error", "Previous command/initialization in session \"" & effectiveTabTitleForLookup & "\"" & ttyInfoStringForMessage & " may not have terminated. New command '" & effectiveOriginalCmdForMsg & "' NOT executed." & suffixForReturn, "process termination")
            else if commandTimedOut then
                return my formatErrorMessage("Timeout Error", "Command '" & effectiveOriginalCmdForMsg & "' timed out after " & maxCommandWaitTime & "s. No other output. " & baseMsgInfo & suffixForReturn, "command timeout")
            else if tabWasBusyOnRead then
                return my formatErrorMessage("Busy Error", "Tab for session \"" & effectiveTabTitleForLookup & "\" was busy during read. No other output. " & baseMsgInfo & suffixForReturn, "read busy")
            else if doWrite and shellCmd is not "" then 
                return scriptInfoPrefix & "Command '" & effectiveOriginalCmdForMsg & "' executed in session \"" & effectiveTabTitleForLookup & "\". No output captured."
            else 
                return scriptInfoPrefix & "No meaningful content found in session \"" & effectiveTabTitleForLookup & "\"."
            end if
        end if
        
        my logVerbose("Returning " & (length of finalResult) & " characters of output")
        return finalResult

    on error generalErrorMsg number generalErrorNum
        if appSpecificErrorOccurred then error generalErrorMsg number generalErrorNum 
        return my formatErrorMessage("Execution Error", generalErrorMsg, "error " & generalErrorNum)
    end try
end run
--#endregion Main Script Logic (on run)

--#region Helper Functions
on ensureTabAndWindow(taskTagName as text, projectGroupName as text, allowCreate as boolean, desiredFullTitle as text)
    set wasActuallyCreated to false
    set createdInExistingViaFuzzy to false 

    tell application id "com.apple.Terminal"
        try
            repeat with w in windows
                repeat with tb in tabs of w
                    try
                        if custom title of tb is desiredFullTitle then
                            set selected tab of w to tb
                            return {targetTab:tb, parentWindow:w, wasNewlyCreated:false, createdInExistingWindowViaFuzzy:false}
                        end if
                    end try
                end repeat
            end repeat
        end try

        if allowCreate and enableFuzzyTagGrouping and projectGroupName is not "" then
            set projectGroupSearchPatternForWindowName to tabTitlePrefix & projectIdentifierInTitle & projectGroupName
            try
                repeat with w in windows
                    try
                        -- Look for any window that contains our project name
                        if name of w contains projectGroupSearchPatternForWindowName or name of w contains (projectIdentifierInTitle & projectGroupName) then
                            if not frontmost then activate
                            delay 0.2
                            set newTabInGroup to do script "" in w
                            delay 0.3
                            set custom title of newTabInGroup to desiredFullTitle 
                            delay 0.2
                            set selected tab of w to newTabInGroup
                            return {targetTab:newTabInGroup, parentWindow:w, wasNewlyCreated:true, createdInExistingWindowViaFuzzy:true}
                        end if
                    end try
                end repeat
            end try
        end if
        
        -- Enhanced fallback: if no project-specific window found, try to use any existing Terminator window
        if allowCreate and enableFuzzyTagGrouping then
            try
                repeat with w in windows
                    try
                        if name of w contains tabTitlePrefix then
                            -- Found an existing Terminator window, use it for grouping
                            if not frontmost then activate
                            delay 0.2
                            set newTabInGroup to do script "" in w
                            delay 0.3
                            set custom title of newTabInGroup to desiredFullTitle 
                            delay 0.2
                            set selected tab of w to newTabInGroup
                            return {targetTab:newTabInGroup, parentWindow:w, wasNewlyCreated:true, createdInExistingWindowViaFuzzy:true}
                        end if
                    end try
                end repeat
            end try
        end if

        if allowCreate then
            try
                if not frontmost then activate 
                delay 0.3
                set newTabInNewWindow to do script ""
                set wasActuallyCreated to true
                delay 0.4 
                set custom title of newTabInNewWindow to desiredFullTitle 
                delay 0.2
                set parentWinOfNew to missing value
                try
                    set parentWinOfNew to window of newTabInNewWindow
                on error
                    if (count of windows) > 0 then set parentWinOfNew to front window
                end try
                if parentWinOfNew is not missing value then
                    if custom title of newTabInNewWindow is desiredFullTitle then 
                        set selected tab of parentWinOfNew to newTabInNewWindow
                        return {targetTab:newTabInNewWindow, parentWindow:parentWinOfNew, wasNewlyCreated:wasActuallyCreated, createdInExistingWindowViaFuzzy:false}
                    end if
                end if
                repeat with w_final_scan in windows
                    repeat with tb_final_scan in tabs of w_final_scan
                        try
                            if custom title of tb_final_scan is desiredFullTitle then
                                set selected tab of w_final_scan to tb_final_scan
                                return {targetTab:tb_final_scan, parentWindow:w_final_scan, wasNewlyCreated:wasActuallyCreated, createdInExistingWindowViaFuzzy:false}
                            end if
                        end try
                    end repeat
                end repeat
                return missing value 
            on error
                return missing value 
            end try
        else
            return missing value 
        end if
    end tell
end ensureTabAndWindow

on tailBufferAS(txt, n)
    set AppleScript's text item delimiters to linefeed
    set lst to text items of txt
    if (count lst) = 0 then return ""
    set startN to (count lst) - (n - 1)
    if startN < 1 then set startN to 1
    set slice to items startN thru -1 of lst 
    set outText to slice as text
    set AppleScript's text item delimiters to "" 
    return outText
end tailBufferAS

on lineIsEffectivelyEmptyAS(aLine)
    if aLine is "" then return true
    set trimmedLine to my trimWhitespace(aLine)
    return (trimmedLine is "")
end lineIsEffectivelyEmptyAS

on trimBlankLinesAS(txt)
    if txt is "" then return ""
    set oldDelims to AppleScript's text item delimiters
    set AppleScript's text item delimiters to {linefeed}
    set originalLines to text items of txt
    set linesToProcess to {}
    repeat with aLineRef in originalLines
        set aLine to contents of aLineRef
        if my lineIsEffectivelyEmptyAS(aLine) then
            set end of linesToProcess to ""
        else
            set end of linesToProcess to aLine
        end if
    end repeat
    set firstContentLine to 1
    repeat while firstContentLine ≤ (count linesToProcess) and (item firstContentLine of linesToProcess is "")
        set firstContentLine to firstContentLine + 1
    end repeat
    set lastContentLine to count linesToProcess
    repeat while lastContentLine ≥ firstContentLine and (item lastContentLine of linesToProcess is "")
        set lastContentLine to lastContentLine - 1
    end repeat
    if firstContentLine > lastContentLine then
        set AppleScript's text item delimiters to oldDelims
        return ""
    end if
    set resultLines to items firstContentLine thru lastContentLine of linesToProcess
    set AppleScript's text item delimiters to linefeed
    set trimmedTxt to resultLines as text
    set AppleScript's text item delimiters to oldDelims
    return trimmedTxt
end trimBlankLinesAS

on trimWhitespace(theText)
    set whitespaceChars to {" ", tab}
    set newText to theText
    repeat while (newText is not "") and (character 1 of newText is in whitespaceChars)
        if (length of newText) > 1 then
            set newText to text 2 thru -1 of newText
        else
            set newText to ""
        end if
    end repeat
    repeat while (newText is not "") and (character -1 of newText is in whitespaceChars)
        if (length of newText) > 1 then
            set newText to text 1 thru -2 of newText
        else
            set newText to ""
        end if
    end repeat
    return newText
end trimWhitespace

on isInteger(v)
    try
        v as integer
        return true
    on error
        return false
    end try
end isInteger

on tagOK(t)
    try
        do shell script "/bin/echo " & quoted form of t & " | /usr/bin/grep -E -q '^[A-Za-z0-9_-]+$'"
        return true
    on error
        return false
    end try
end tagOK

on joinList(theList, theDelimiter)
    set oldDelims to AppleScript's text item delimiters
    set AppleScript's text item delimiters to theDelimiter
    set theText to theList as text
    set AppleScript's text item delimiters to oldDelims
    return theText
end joinList

on usageText()
    set LF to linefeed
    set scriptName to "terminator.scpt"
    set exampleProject to "/Users/name/Projects/FancyApp"
    set exampleProjectNameForTitle to my getPathComponent(exampleProject, -1)
    if exampleProjectNameForTitle is "" then set exampleProjectNameForTitle to "DefaultProject"
    set exampleTaskTag to "build_frontend"
    set exampleFullCommand to "npm run build" 
    
    set generatedExampleTitle to my generateWindowTitle(exampleTaskTag, exampleProjectNameForTitle)
    
    set outText to scriptName & " - v0.6.0 Enhanced \"T-1000\" – AppleScript Terminal helper" & LF & LF
    set outText to outText & "Enhancements: Smart session reuse, enhanced error reporting, verbose logging (optional)" & LF & LF
    set outText to outText & "Manages dedicated, tagged Terminal sessions, grouped by project path." & LF & LF
    
    set outText to outText & "Core Concept:" & LF
    set outText to outText & "  1. For a NEW project, provide the absolute project path FIRST, then task tag, then command:" & LF
    set outText to outText & "     osascript " & scriptName & " \"" & exampleProject & "\" \"" & exampleTaskTag & "\" \"" & exampleFullCommand & "\"" & LF
    set outText to outText & "     The script will 'cd' into the project path and run the command." & LF
    set outText to outText & "     The tab will be titled like: \"" & generatedExampleTitle & "\"" & LF
    set outText to outText & "  2. For SUBSEQUENT commands for THE SAME PROJECT, use the project path and task tag:" & LF
    set outText to outText & "     osascript " & scriptName & " \"" & exampleProject & "\" \"" & exampleTaskTag & "\" \"another_command\"" & LF
    set outText to outText & "  3. To simply READ from an existing session (path & tag must identify an existing session):" & LF
    set outText to outText & "     osascript " & scriptName & " \"" & exampleProject & "\" \"" & exampleTaskTag & "\"" & LF
    set outText to outText & "     A READ operation on a non-existent tag (without path/command to create) will error." & LF & LF
    
    set outText to outText & "Title Format: \"" & tabTitlePrefix & projectIdentifierInTitle & "<ProjectName>" & taskIdentifierInTitle & "<TaskTag>\"" & LF
    set outText to outText & "Or if no project path provided: \"" & tabTitlePrefix & "<TaskTag>\"" & LF & LF
    
    set outText to outText & "Enhanced Features:" & LF
    set outText to outText & "  • Smart session reuse for same project paths" & LF
    set outText to outText & "  • Enhanced error reporting with context information" & LF
    set outText to outText & "  • Optional verbose logging for debugging" & LF
    set outText to outText & "  • No automatic clearing to prevent interrupting builds" & LF
    set outText to outText & "  • 100-line default output for better build log visibility" & LF
    set outText to outText & "  • Automatically 'cd's into project path if provided with a command." & LF
    set outText to outText & "  • Groups new task tabs into existing project windows if fuzzy grouping enabled." & LF
    set outText to outText & "  • Interrupts busy processes in reused tabs." & LF & LF
    
    set outText to outText & "Usage Examples:" & LF
    set outText to outText & "  # Start new project session, cd, run command, get 100 lines:" & LF
    set outText to outText & "  osascript " & scriptName & " \"" & exampleProject & "\" \"frontend_build\" \"npm run build\" 100" & LF
    set outText to outText & "  # Create/use 'backend_tests' task tab in the 'FancyApp' project window:" & LF
    set outText to outText & "  osascript " & scriptName & " \"" & exampleProject & "\" \"backend_tests\" \"pytest\"" & LF
    set outText to outText & "  # Prepare/create a new session by just cd'ing into project path (empty command):" & LF
    set outText to outText & "  osascript " & scriptName & " \"" & exampleProject & "\" \"dev_shell\" \"\" 1" & LF
    set outText to outText & "  # Read from an existing session:" & LF
    set outText to outText & "  osascript " & scriptName & " \"" & exampleProject & "\" \"frontend_build\" 50" & LF & LF
    
    set outText to outText & "Parameters:" & LF
    set outText to outText & "  [\"/absolute/project/path\"]: (Optional First Arg) Base path for project. Enables 'cd' and grouping." & LF
    set outText to outText & "  \"<task_tag_name>\": Required. Specific task name for the tab (e.g., 'build', 'tests')." & LF
    set outText to outText & "  [\"<shell_command_parts...>\"]: (Optional) Command. If path provided, 'cd path &&' is prepended." & LF
    set outText to outText & "                                Use \"\" for no command (will just 'cd' if path given)." & LF
    set outText to outText & "  [[lines_to_read]]: (Optional Last Arg) Number of history lines. Default: " & defaultTailLines & "." & LF & LF
        
    set outText to outText & "Notes:" & LF
    set outText to outText & "  • Provide project path on first use for a project for best window grouping and auto 'cd'." & LF
    set outText to outText & "  • Ensure Automation permissions for Terminal.app & System Events.app." & LF
    set outText to outText & "  • Works within Terminal.app's AppleScript limitations for reliable operation." & LF
    
    return outText
end usageText
--#endregion Helper Functions