Repository navigation
Description
Summary
squad watch --execute has several bugs that prevent it from working. All are fixable. This issue documents root causes and tested fixes.
1. Wrong Copilot CLI invocation
execute.js buildAgentCommand uses gh copilot --message — neither --message nor gh copilot as a subcommand exist. The Copilot CLI binary is copilot (or gh copilot -- to pass through), and the non-interactive flag is -p.
Additionally, without permission flags, copilot answers in text but cannot do work (no file I/O, no shell, no git).
// Broken:
const args = ['copilot', '--message', prompt];
return { cmd: 'gh', args };
// Working:
const args = ['-p', prompt, '--yolo', '--no-ask-user', '--silent'];
return { cmd: 'copilot', args };Use copilot directly, not gh copilot — on Windows gh is a .cmd batch file that opens a visible console window even with windowsHide: true.
2. Config file execute: true doesn't enable the capability
The CLI flag path (--execute) explicitly sets capabilities['execute'] = true. The config file path reads execute: true into config.execute but never maps it to config.capabilities. The preflight system only checks config.capabilities[cap.name] — so the capability silently doesn't load.
// Fix in loadWatchConfig (config.js), after the merge block:
if (merged.execute && !merged.capabilities['execute']) {
merged.capabilities['execute'] = true;
}3. Single prompt for all issues blocks Ralph
executeAll sends every eligible issue in one copilot prompt and waits synchronously (up to 30min timeout). Ralph can't poll or triage during this time.
Fix: Spawn one detached copilot process per issue (max N per round). Use spawn() with detached: true, stdio: 'ignore' so Ralph returns immediately. Track dispatched issue numbers in a state file to avoid re-dispatching on the next poll.
4. Console windows on Windows (MCP servers via npx)
Copilot spawns MCP servers (Context7, WorkIQ, Playwright) via npx, which is a .cmd batch file on Windows. Every .cmd invocation opens a visible console window — 10-15 of them per copilot session, coming and going.
windowsHide: true on the parent spawn does not propagate to grandchild processes.
Workaround: Use --config-dir pointing to a headless MCP config where stdio servers use node directly instead of npx:
{
"mcpServers": {
"Context7": {
"type": "stdio",
"command": "node",
"args": ["/path/to/node_modules/@upstash/context7-mcp/dist/index.js"]
}
}
}node.exe is a real executable — no console window. HTTP-based MCP servers (Microsoft Docs, FluentUI) don't spawn subprocesses and work as-is.
Proper fix (in copilot CLI): When running in non-interactive mode (-p), copilot should spawn MCP stdio subprocesses with windowsHide: true or the Windows CREATE_NO_WINDOW flag. Alternatively, provide a --no-mcp or --headless flag.
5. No worktree isolation
Multiple copilot sessions in the same working directory cause git conflicts. Each issue should get its own worktree:
execFileSync('git', ['worktree', 'add', `../repo-${issue.number}`, '-b', `squad/${issue.number}-${slug}`, 'main']);
spawn('copilot', args, { cwd: worktreePath, detached: true, ... });Recommended executeAll architecture
Per round (non-blocking):
1. Load state file → skip already-dispatched issues
2. Pick up to 3 eligible issues
3. For each:
a. git worktree add ../repo-{N} -b squad/{N}-{slug} main
b. spawn('copilot', ['-p', prompt, '--yolo', '--no-ask-user', '--silent',
'--config-dir', '.squad/.copilot-headless'],
{ cwd: worktreePath, detached: true, stdio: 'ignore', windowsHide: true })
4. Save dispatched issue numbers to state file
5. Return immediately → Ralph keeps polling
Environment
- Squad CLI: v0.9.4-insider.1
- OS: Windows 11
- Copilot CLI: latest (WinGet)
All fixes tested and working locally via monkey-patches to the installed squad CLI.
Activity
fix: replace obsolete --message flag with -p across watch and loop
fix: replace obsolete --message flag with -p across watch and loop
fix: replace obsolete --message flag with -p across watch and loop
fix: replace obsolete --message flag with -p across watch and loop
fix: replace obsolete --message flag with -p across watch and loop (#988
fix: replace obsolete --message flag with -p across watch and loop (#988