DO NOT MERGE - Revert: feat(cli): Add squad loop command (#767) - #774
DO NOT MERGE - Revert: feat(cli): Add squad loop command (#767)#774diberry wants to merge 1 commit into
Conversation
There was a problem hiding this comment.
Pull request overview
Reverts the previously-merged squad loop feature (PR #767) because it landed prematurely, removing the loop command implementation, its templates, tests, docs, and associated repo state updates.
Changes:
- Removes the
squad loopcommand implementation (loop.ts) plus theloop.mdtemplates and unit tests. - Deletes Loop documentation (CLI reference section + feature page) and the changeset for the CLI release.
- Adjusts the CLI entry to a placeholder
loophandler and updatessquad.agenttemplate copies (but currently leaves template canon out of sync).
Reviewed changes
Copilot reviewed 14 out of 16 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
test/cli/loop.test.ts |
Removes unit tests that were specific to the Loop command. |
templates/squad.agent.md.template |
Updates a mirrored squad.agent template (currently risks template-sync mismatch vs canonical). |
templates/loop.md |
Removes Loop prompt template from root templates. |
packages/squad-cli/templates/squad.agent.md.template |
Updates CLI package’s mirrored squad.agent template. |
packages/squad-cli/templates/loop.md |
Removes Loop prompt template from CLI package templates. |
packages/squad-cli/src/cli/commands/loop.ts |
Removes Loop command implementation. |
packages/squad-cli/src/cli-entry.ts |
Removes Loop implementation wiring but leaves a stub loop command in help/dispatch. |
docs/src/content/docs/reference/cli.md |
Removes Loop command docs and updates command count. |
docs/src/content/docs/features/loop.md |
Deletes the Loop feature documentation page. |
.squad/skills/pr-review-response/SKILL.md |
Removes a skill file that was introduced/modified as part of the reverted work. |
.squad/log/2026-03-27T21-42-00Z-decisions-archival.md |
Removes a Scribe archival log entry associated with the reverted work. |
.squad/agents/eecom/history.md |
Removes Loop-related learnings entry. |
.changeset/loop-command.md |
Removes the changeset announcing the Loop command release. |
| 4. CROSS-AGENT: Append team updates to affected agents' history.md. | ||
| 5. DECISIONS ARCHIVE: If decisions.md exceeds ~20KB, archive entries older than 30 days to decisions-archive.md. | ||
| 6. GIT COMMIT: git add .squad/ && commit (write msg to temp file, use -F). Skip if nothing staged. | ||
| 7. HISTORY SUMMARIZATION: If any history.md >12KB, summarize old entries to ## Core Context. |
There was a problem hiding this comment.
Template-sync CI will likely fail: template-sync.test.ts defines .squad-templates/squad.agent.md as canonical and requires mirror copies (templates/squad.agent.md.template, packages/*/templates/squad.agent.md.template, and .github/agents/squad.agent.md) to be byte-for-byte identical. This change updates a mirror copy but does not update the canonical .squad-templates/squad.agent.md (and .github/agents/squad.agent.md), so the repo will be out of sync. Fix by running node scripts/sync-templates.mjs (or manually applying the same edit) so canonical + all mirrors match exactly.
| 7. HISTORY SUMMARIZATION: If any history.md >12KB, summarize old entries to ## Core Context. |
| console.log(` ${BOLD}loop${RESET} Continuous work loop (Ralph mode)`); | ||
| console.log(` Usage: loop [--filter <label>] [--interval <minutes>]`); | ||
| console.log(` Default: checks every 10 minutes (Ctrl+C to stop)`); | ||
| console.log(` ${BOLD}hire${RESET} Team creation wizard`); |
There was a problem hiding this comment.
The PR is described as a revert of adding the squad loop command, but the CLI still advertises loop in the help output and implements a stub handler that prints “full implementation pending”. This leaves users with a documented command that doesn’t work and also conflicts with the docs update that removed squad loop from the CLI reference. Either remove the loop command entirely (help + dispatch), or reintroduce/restore documentation and a working implementation.
| if (cmd === 'loop') { | ||
| // --help | ||
| if (args.includes('--help') || args.includes('-h')) { | ||
| console.log(`\n${BOLD}squad loop${RESET} — Prompt-driven continuous work loop\n`); | ||
| console.log(`Usage: squad loop [options]\n`); | ||
| console.log(`Reads loop.md and runs it as a continuous work loop.\n`); | ||
| console.log(`Options:`); | ||
| console.log(` ${BOLD}--init${RESET} Generate a boilerplate loop.md`); | ||
| console.log(` ${BOLD}--file <path>${RESET} Path to loop file (default: loop.md)`); | ||
| console.log(` ${BOLD}--interval <min>${RESET} Override loop interval in minutes`); | ||
| console.log(` ${BOLD}--timeout <min>${RESET} Override max minutes per cycle`); | ||
| console.log(` ${BOLD}--copilot-flags "..."${RESET} Extra flags for Copilot CLI`); | ||
| console.log(` ${BOLD}--agent-cmd <cmd>${RESET} Override the agent command`); | ||
| console.log(`\nCapabilities (composable with the loop):`); | ||
| console.log(` ${BOLD}--self-pull${RESET} git fetch/pull at round start`); | ||
| console.log(` ${BOLD}--monitor-email${RESET} Scan email for actionable items`); | ||
| console.log(` ${BOLD}--monitor-teams${RESET} Scan Teams for actionable messages`); | ||
| console.log(` ${BOLD}--decision-hygiene${RESET} Auto-merge decision inbox`); | ||
| console.log(` ${BOLD}--retro${RESET} Enforce retrospective checks`); | ||
| console.log(`\nFrontmatter (in loop.md):`); | ||
| console.log(` configured: true ${DIM}(required — confirms intentional setup)${RESET}`); | ||
| console.log(` interval: 10 ${DIM}(minutes between cycles)${RESET}`); | ||
| console.log(` timeout: 30 ${DIM}(max minutes per cycle)${RESET}`); | ||
| console.log(` description: "..." ${DIM}(shown in status output)${RESET}`); | ||
| console.log(`\nExamples:`); | ||
| console.log(` squad loop ${DIM}# run loop.md${RESET}`); | ||
| console.log(` squad loop --init ${DIM}# generate boilerplate${RESET}`); | ||
| console.log(` squad loop --file ops/loop.md ${DIM}# custom loop file${RESET}`); | ||
| console.log(` squad loop --monitor-email ${DIM}# with email monitoring${RESET}`); | ||
| return; | ||
| } | ||
|
|
||
| const { runLoop, generateLoopFile } = await import('./cli/commands/loop.js'); | ||
|
|
||
| // --init: scaffold a boilerplate loop.md | ||
| if (args.includes('--init')) { | ||
| const fileIdx = args.indexOf('--file'); | ||
| const filePath = (fileIdx !== -1 && args[fileIdx + 1]) ? args[fileIdx + 1]! : 'loop.md'; | ||
| const { FSStorageProvider } = await import('@bradygaster/squad-sdk'); | ||
| const storage = new FSStorageProvider(); | ||
| const pathMod = await import('node:path'); | ||
| const absPath = pathMod.default.resolve(process.cwd(), filePath); | ||
| if (storage.existsSync(absPath)) { | ||
| console.log(`⚠️ ${filePath} already exists. Remove it first to regenerate.`); | ||
| } else { | ||
| storage.writeSync(absPath, generateLoopFile()); | ||
| console.log(`✅ Created ${filePath} — open it and set \`configured: true\` to activate.`); | ||
| } | ||
| return; | ||
| } | ||
|
|
||
| // Parse flags | ||
| const fileIdx = args.indexOf('--file'); | ||
| const filePath = (fileIdx !== -1 && args[fileIdx + 1]) ? args[fileIdx + 1] : undefined; | ||
|
|
||
| const filterIdx = args.indexOf('--filter'); | ||
| const filter = (filterIdx !== -1 && args[filterIdx + 1]) ? args[filterIdx + 1] : undefined; | ||
| const intervalIdx = args.indexOf('--interval'); | ||
| const interval = (intervalIdx !== -1 && args[intervalIdx + 1]) | ||
| const intervalMinutes = (intervalIdx !== -1 && args[intervalIdx + 1]) | ||
| ? parseInt(args[intervalIdx + 1]!, 10) | ||
| : undefined; | ||
|
|
||
| const timeoutIdx = args.indexOf('--timeout'); | ||
| const timeout = (timeoutIdx !== -1 && args[timeoutIdx + 1]) | ||
| ? parseInt(args[timeoutIdx + 1]!, 10) | ||
| : undefined; | ||
|
|
||
| const copilotFlagsIdx = args.indexOf('--copilot-flags'); | ||
| const copilotFlags = (copilotFlagsIdx !== -1 && args[copilotFlagsIdx + 1]) | ||
| ? args[copilotFlagsIdx + 1] | ||
| : undefined; | ||
|
|
||
| const agentCmdIdx = args.indexOf('--agent-cmd'); | ||
| const agentCmd = (agentCmdIdx !== -1 && args[agentCmdIdx + 1]) | ||
| ? args[agentCmdIdx + 1] | ||
| : undefined; | ||
|
|
||
| // Capability flags | ||
| const { createDefaultRegistry: createReg } = await import('./cli/commands/watch/index.js'); | ||
| const reg = createReg(); | ||
| const capabilities: Record<string, boolean | Record<string, unknown>> = {}; | ||
| for (const cap of reg.all()) { | ||
| if (args.includes(`--${cap.name}`)) capabilities[cap.name] = true; | ||
| if (args.includes(`--no-${cap.name}`)) capabilities[cap.name] = false; | ||
| : 10; | ||
| console.log(`🔄 Squad loop starting... (full implementation pending)`); | ||
| if (filter) { | ||
| console.log(` Filter: ${filter}`); | ||
| } | ||
|
|
||
| await runLoop(process.cwd(), { | ||
| filePath, | ||
| interval, | ||
| timeout, | ||
| copilotFlags, | ||
| agentCmd, | ||
| capabilities, | ||
| }); | ||
| console.log(` Interval: ${intervalMinutes} minutes`); |
There was a problem hiding this comment.
The loop command handler currently parses --interval but doesn’t validate it. If a non-numeric value is passed (e.g. --interval abc), parseInt yields NaN and the CLI prints Interval: NaN minutes. If the command is going to remain exposed, validate the parsed value and fail with a clear usage message (or default back to 10).
| ## CLI Commands (16 commands) | ||
|
|
||
| | Command | Description | Requires `.squad/` | | ||
| |---------|-------------|:------------------:| | ||
| | `squad` | Enter interactive shell (no args) | No | | ||
| | `squad init` | Initialize Squad in the current repo (idempotent — safe to run multiple times) | No | | ||
| | `squad init --global` | Create a personal squad in your platform-specific directory | No | | ||
| | `squad init --mode remote <path>` | Initialize linked to a remote team root (dual-root mode) | No | | ||
| | `squad link <team-repo-path>` | Link project to a remote team root | Yes | | ||
| | `squad loop` | Run a prompt-driven work loop from `loop.md` | Yes | | ||
| | `squad loop --init` | Create a starter `loop.md` file | Yes | | ||
| | `squad loop --file <path>` | Run a loop from a custom file path | Yes | | ||
| | `squad start [--tunnel] [--port N] [--command cmd]` | Start Copilot with remote phone access via PTY and WebSocket | No | | ||
| | `squad status` | Show which squad is active and why | Yes | | ||
| | `squad doctor` | Validate squad setup integrity and diagnose issues (alias: `heartbeat`) | Yes | | ||
| | `squad upgrade` | Upgrade Squad-owned files to latest version | Yes | | ||
| | `squad upgrade --migrate-directory` | Rename legacy `.ai-team/` directory to `.squad/` | Yes | | ||
| | `squad link <team-repo-path>` | Link project to a remote team root | Yes | | ||
| | `squad triage` | Auto-triage issues and assign to team (primary name; `watch` is an alias) | Yes | | ||
| | `squad triage --interval <min>` | Continuous triage (default: every 10 min) | Yes | |
There was a problem hiding this comment.
Docs now remove squad loop from the CLI reference (and reduce the command count), but the CLI help/dispatcher still exposes a loop command. Once the desired behavior is decided (remove loop vs. keep as alias), please update this table/count to match the shipped CLI to avoid confusing users.
Reverts the squash merge of PR #767 (commit efb56ac). The loop command was merged prematurely — needs further review before landing on dev.