Skip to content

Commit f3cc8a3

Browse files
authored
Merge pull request #71 from FlowmemoryAI/hq/terminal-dispatch
[codex] add terminal goal dispatcher
2 parents ef3ae59 + c02a14c commit f3cc8a3

1 file changed

Lines changed: 192 additions & 0 deletions

File tree

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
param(
2+
[ValidateSet("contracts", "indexer", "crypto", "chain", "dashboard", "hardware", "research", "review")]
3+
[string] $Agent,
4+
5+
[string] $Goal,
6+
7+
[string] $GoalFile,
8+
9+
[switch] $List,
10+
11+
[switch] $DryRun,
12+
13+
[switch] $NoEnter
14+
)
15+
16+
$ErrorActionPreference = "Stop"
17+
Set-StrictMode -Version Latest
18+
19+
Add-Type @"
20+
using System;
21+
using System.Text;
22+
using System.Runtime.InteropServices;
23+
using System.Collections.Generic;
24+
25+
public class FlowMemoryWindowTools {
26+
public delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);
27+
28+
[DllImport("user32.dll")]
29+
public static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);
30+
31+
[DllImport("user32.dll")]
32+
public static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
33+
34+
[DllImport("user32.dll")]
35+
public static extern bool IsWindowVisible(IntPtr hWnd);
36+
37+
[DllImport("user32.dll")]
38+
public static extern bool SetForegroundWindow(IntPtr hWnd);
39+
40+
public static List<Tuple<IntPtr, string>> GetVisibleWindows() {
41+
var windows = new List<Tuple<IntPtr, string>>();
42+
EnumWindows(delegate(IntPtr hWnd, IntPtr lParam) {
43+
if (IsWindowVisible(hWnd)) {
44+
var sb = new StringBuilder(512);
45+
GetWindowText(hWnd, sb, sb.Capacity);
46+
var title = sb.ToString();
47+
if (!String.IsNullOrWhiteSpace(title)) {
48+
windows.Add(Tuple.Create(hWnd, title));
49+
}
50+
}
51+
return true;
52+
}, IntPtr.Zero);
53+
return windows;
54+
}
55+
}
56+
"@
57+
58+
$agents = @{
59+
contracts = @{
60+
TitlePattern = "flowmemory-contracts"
61+
Worktree = "E:\FlowMemory\flowmemory-contracts"
62+
}
63+
indexer = @{
64+
TitlePattern = "flowmemory-indexer"
65+
Worktree = "E:\FlowMemory\flowmemory-indexer"
66+
}
67+
crypto = @{
68+
TitlePattern = "flowmemory-crypto"
69+
Worktree = "E:\FlowMemory\flowmemory-crypto"
70+
}
71+
chain = @{
72+
TitlePattern = "flowmemory-chain"
73+
Worktree = "E:\FlowMemory\flowmemory-chain"
74+
}
75+
dashboard = @{
76+
TitlePattern = "flowmemory-dashboard"
77+
Worktree = "E:\FlowMemory\flowmemory-dashboard"
78+
}
79+
hardware = @{
80+
TitlePattern = "flowmemory-hardware"
81+
Worktree = "E:\FlowMemory\flowmemory-hardware"
82+
}
83+
research = @{
84+
TitlePattern = "flowmemory-research"
85+
Worktree = "E:\FlowMemory\flowmemory-research"
86+
}
87+
review = @{
88+
TitlePattern = "flowmemory-review"
89+
Worktree = "E:\FlowMemory\flowmemory-review"
90+
}
91+
}
92+
93+
function Get-AgentWindow {
94+
param(
95+
[Parameter(Mandatory = $true)]
96+
[string] $Pattern
97+
)
98+
99+
$windows = [FlowMemoryWindowTools]::GetVisibleWindows()
100+
$matches = @($windows | Where-Object { $_.Item2 -like "*$Pattern*" })
101+
102+
if ($matches.Count -eq 0) {
103+
throw "No visible terminal window matched title pattern '$Pattern'. Use -List to inspect visible windows."
104+
}
105+
106+
if ($matches.Count -gt 1) {
107+
$titles = ($matches | ForEach-Object { $_.Item2 }) -join "; "
108+
throw "More than one visible terminal matched '$Pattern': $titles"
109+
}
110+
111+
return $matches[0]
112+
}
113+
114+
function Get-VisibleAgentWindows {
115+
[FlowMemoryWindowTools]::GetVisibleWindows() |
116+
Where-Object { $_.Item2 -match "FlowMemory|flowmemory|Codex|codex|PowerShell" } |
117+
Sort-Object { $_.Item2 } |
118+
ForEach-Object {
119+
[pscustomobject]@{
120+
Handle = $_.Item1
121+
Title = $_.Item2
122+
}
123+
}
124+
}
125+
126+
if ($List) {
127+
Get-VisibleAgentWindows | Format-Table -AutoSize
128+
exit 0
129+
}
130+
131+
if (-not $Agent) {
132+
throw "Specify -Agent or use -List."
133+
}
134+
135+
if ([string]::IsNullOrWhiteSpace($Goal) -and [string]::IsNullOrWhiteSpace($GoalFile)) {
136+
throw "Specify -Goal or -GoalFile."
137+
}
138+
139+
if (-not $agents.ContainsKey($Agent)) {
140+
throw "Unknown agent '$Agent'."
141+
}
142+
143+
$agentConfig = $agents[$Agent]
144+
$window = Get-AgentWindow -Pattern $agentConfig.TitlePattern
145+
146+
$dispatchRoot = "E:\FlowMemory\agent-dispatch"
147+
New-Item -ItemType Directory -Force -Path $dispatchRoot | Out-Null
148+
149+
$goalText = $Goal
150+
if (-not [string]::IsNullOrWhiteSpace($GoalFile)) {
151+
$goalText = Get-Content -LiteralPath $GoalFile -Raw
152+
}
153+
154+
$goalPath = Join-Path $dispatchRoot "$Agent-goal.md"
155+
$payload = @"
156+
# FlowMemory $Agent Agent Goal
157+
158+
Generated: $(Get-Date -Format "yyyy-MM-dd HH:mm:ss zzz")
159+
Worktree: $($agentConfig.Worktree)
160+
Window title: $($window.Item2)
161+
162+
$goalText
163+
"@
164+
165+
Set-Content -LiteralPath $goalPath -Value $payload -Encoding UTF8
166+
167+
$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."
168+
169+
Write-Host "Agent: $Agent"
170+
Write-Host "Window: $($window.Item2)"
171+
Write-Host "Goal file: $goalPath"
172+
Write-Host "Prompt:"
173+
Write-Host $oneLineGoal
174+
175+
if ($DryRun) {
176+
Write-Host "Dry run only. Nothing was pasted."
177+
exit 0
178+
}
179+
180+
Set-Clipboard -Value $oneLineGoal
181+
[FlowMemoryWindowTools]::SetForegroundWindow($window.Item1) | Out-Null
182+
Start-Sleep -Milliseconds 350
183+
184+
$shell = New-Object -ComObject WScript.Shell
185+
$shell.SendKeys("^v")
186+
Start-Sleep -Milliseconds 150
187+
188+
if (-not $NoEnter) {
189+
$shell.SendKeys("{ENTER}")
190+
}
191+
192+
Write-Host "Dispatched goal to $Agent."

0 commit comments

Comments
 (0)