From c02a14cd0e219488f9104bb5311a97373823b895 Mon Sep 17 00:00:00 2001 From: FlowmemoryAI <283694809+FlowmemoryAI@users.noreply.github.com> Date: Wed, 13 May 2026 13:27:02 -0500 Subject: [PATCH] Add FlowMemory terminal goal dispatcher --- infra/scripts/send-goal-to-agent.ps1 | 192 +++++++++++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 infra/scripts/send-goal-to-agent.ps1 diff --git a/infra/scripts/send-goal-to-agent.ps1 b/infra/scripts/send-goal-to-agent.ps1 new file mode 100644 index 00000000..d64a5b38 --- /dev/null +++ b/infra/scripts/send-goal-to-agent.ps1 @@ -0,0 +1,192 @@ +param( + [ValidateSet("contracts", "indexer", "crypto", "chain", "dashboard", "hardware", "research", "review")] + [string] $Agent, + + [string] $Goal, + + [string] $GoalFile, + + [switch] $List, + + [switch] $DryRun, + + [switch] $NoEnter +) + +$ErrorActionPreference = "Stop" +Set-StrictMode -Version Latest + +Add-Type @" +using System; +using System.Text; +using System.Runtime.InteropServices; +using System.Collections.Generic; + +public class FlowMemoryWindowTools { + public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam); + + [DllImport("user32.dll")] + public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam); + + [DllImport("user32.dll")] + public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount); + + [DllImport("user32.dll")] + public static extern bool IsWindowVisible(IntPtr hWnd); + + [DllImport("user32.dll")] + public static extern bool SetForegroundWindow(IntPtr hWnd); + + public static List> GetVisibleWindows() { + var windows = new List>(); + EnumWindows(delegate(IntPtr hWnd, IntPtr lParam) { + if (IsWindowVisible(hWnd)) { + var sb = new StringBuilder(512); + GetWindowText(hWnd, sb, sb.Capacity); + var title = sb.ToString(); + if (!String.IsNullOrWhiteSpace(title)) { + windows.Add(Tuple.Create(hWnd, title)); + } + } + return true; + }, IntPtr.Zero); + return windows; + } +} +"@ + +$agents = @{ + contracts = @{ + TitlePattern = "flowmemory-contracts" + Worktree = "E:\FlowMemory\flowmemory-contracts" + } + indexer = @{ + TitlePattern = "flowmemory-indexer" + Worktree = "E:\FlowMemory\flowmemory-indexer" + } + crypto = @{ + TitlePattern = "flowmemory-crypto" + Worktree = "E:\FlowMemory\flowmemory-crypto" + } + chain = @{ + TitlePattern = "flowmemory-chain" + Worktree = "E:\FlowMemory\flowmemory-chain" + } + dashboard = @{ + TitlePattern = "flowmemory-dashboard" + Worktree = "E:\FlowMemory\flowmemory-dashboard" + } + hardware = @{ + TitlePattern = "flowmemory-hardware" + Worktree = "E:\FlowMemory\flowmemory-hardware" + } + research = @{ + TitlePattern = "flowmemory-research" + Worktree = "E:\FlowMemory\flowmemory-research" + } + review = @{ + TitlePattern = "flowmemory-review" + Worktree = "E:\FlowMemory\flowmemory-review" + } +} + +function Get-AgentWindow { + param( + [Parameter(Mandatory = $true)] + [string] $Pattern + ) + + $windows = [FlowMemoryWindowTools]::GetVisibleWindows() + $matches = @($windows | Where-Object { $_.Item2 -like "*$Pattern*" }) + + if ($matches.Count -eq 0) { + throw "No visible terminal window matched title pattern '$Pattern'. Use -List to inspect visible windows." + } + + if ($matches.Count -gt 1) { + $titles = ($matches | ForEach-Object { $_.Item2 }) -join "; " + throw "More than one visible terminal matched '$Pattern': $titles" + } + + return $matches[0] +} + +function Get-VisibleAgentWindows { + [FlowMemoryWindowTools]::GetVisibleWindows() | + Where-Object { $_.Item2 -match "FlowMemory|flowmemory|Codex|codex|PowerShell" } | + Sort-Object { $_.Item2 } | + ForEach-Object { + [pscustomobject]@{ + Handle = $_.Item1 + Title = $_.Item2 + } + } +} + +if ($List) { + Get-VisibleAgentWindows | Format-Table -AutoSize + exit 0 +} + +if (-not $Agent) { + throw "Specify -Agent or use -List." +} + +if ([string]::IsNullOrWhiteSpace($Goal) -and [string]::IsNullOrWhiteSpace($GoalFile)) { + throw "Specify -Goal or -GoalFile." +} + +if (-not $agents.ContainsKey($Agent)) { + throw "Unknown agent '$Agent'." +} + +$agentConfig = $agents[$Agent] +$window = Get-AgentWindow -Pattern $agentConfig.TitlePattern + +$dispatchRoot = "E:\FlowMemory\agent-dispatch" +New-Item -ItemType Directory -Force -Path $dispatchRoot | Out-Null + +$goalText = $Goal +if (-not [string]::IsNullOrWhiteSpace($GoalFile)) { + $goalText = Get-Content -LiteralPath $GoalFile -Raw +} + +$goalPath = Join-Path $dispatchRoot "$Agent-goal.md" +$payload = @" +# FlowMemory $Agent Agent Goal + +Generated: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss zzz") +Worktree: $($agentConfig.Worktree) +Window title: $($window.Item2) + +$goalText +"@ + +Set-Content -LiteralPath $goalPath -Value $payload -Encoding UTF8 + +$oneLineGoal = "/goal Read the goal file at $goalPath and execute it from your current FlowMemory worktree. First restate the objective, allowed scope, forbidden scope, branch, and current git status. Then proceed only within the goal." + +Write-Host "Agent: $Agent" +Write-Host "Window: $($window.Item2)" +Write-Host "Goal file: $goalPath" +Write-Host "Prompt:" +Write-Host $oneLineGoal + +if ($DryRun) { + Write-Host "Dry run only. Nothing was pasted." + exit 0 +} + +Set-Clipboard -Value $oneLineGoal +[FlowMemoryWindowTools]::SetForegroundWindow($window.Item1) | Out-Null +Start-Sleep -Milliseconds 350 + +$shell = New-Object -ComObject WScript.Shell +$shell.SendKeys("^v") +Start-Sleep -Milliseconds 150 + +if (-not $NoEnter) { + $shell.SendKeys("{ENTER}") +} + +Write-Host "Dispatched goal to $Agent."