Skip to content

fix: monorepo subfolder support — never run git init (#939)#944

Merged
tamirdresher merged 4 commits intodevfrom
squad/939-monorepo-subfolder-support
Apr 14, 2026
Merged

fix: monorepo subfolder support — never run git init (#939)#944
tamirdresher merged 4 commits intodevfrom
squad/939-monorepo-subfolder-support

Conversation

@tamirdresher
Copy link
Copy Markdown
Collaborator

Problem

When squad init runs in a subfolder of a monorepo, it detects that CWD != git root and runs git init to create a nested repo boundary. This breaks monorepo workflows — users end up with git-in-git.

Reported in #939 with a +1 from another monorepo user.

Solution

Never run git init. Instead, split file placement:

File Placed at Why
.github/agents/squad.agent.md Git root Copilot resolves custom agents from git root only
.squad/ (team state) CWD (subfolder) Team state lives where the team works

This enables multiple squads in a monorepo:

/monorepo/ .github/agents/squad.agent.md ← shared agent (at git root) team-alpha/.squad/ ← alpha's team state team-beta/.squad/ ← beta's team state

Changes

  1. packages/squad-cli/src/cli/core/init.ts — replaced git init block with monorepo-aware placement. Added agentFileRoot variable that points to git root when in subfolder.

  2. packages/squad-sdk/src/config/init.ts — added agentFileRoot to InitOptions interface. Uses it for .github/agents/ placement (falls back to teamRoot).

  3. Both squad.agent.md.template files — updated Worktree Awareness section to check CWD first for .squad/, enabling monorepo subfolder resolution.

How the agent finds the right .squad/

The coordinator resolves team root by checking CWD first:

  1. Does CWD have .squad/? → use it (monorepo case)
  2. Does git root have .squad/? → use it (normal case)
  3. Fall back to main checkout discovery

Since Copilot CLI sets CWD to wherever you launched copilot from, cd team-alpha && copilot finds alpha's squad, cd team-beta && copilot finds beta's.

Copilot AI review requested due to automatic review settings April 9, 2026 07:29
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR fixes squad init behavior in monorepo subfolders by removing the nested-repo “auto-fix” and instead placing the Copilot agent file at the git root while keeping .squad/ team state in the working subfolder.

Changes:

  • Updated CLI init flow to detect “monorepo/subfolder” and never run git init, introducing agentFileRoot to control where .github/agents/squad.agent.md is written.
  • Extended SDK InitOptions with agentFileRoot and used it for agent file placement.
  • Updated squad.agent.md.template “Worktree Awareness” section to describe CWD-first .squad/ resolution for monorepos.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 6 comments.

File Description
packages/squad-cli/src/cli/core/init.ts Stops nested git init; passes agentFileRoot and stamps agent version at git root when needed.
packages/squad-sdk/src/config/init.ts Adds agentFileRoot option and uses it when writing .github/agents/squad.agent.md.
packages/squad-cli/templates/squad.agent.md.template Updates template documentation for CWD-first .squad/ resolution.
packages/squad-sdk/templates/squad.agent.md.template Updates template documentation for CWD-first .squad/ resolution.

Comment thread packages/squad-cli/templates/squad.agent.md.template
Comment thread packages/squad-sdk/templates/squad.agent.md.template
Comment thread packages/squad-sdk/src/config/init.ts
Comment thread packages/squad-cli/src/cli/core/init.ts
Comment thread packages/squad-cli/src/cli/core/init.ts Outdated
Comment thread packages/squad-cli/src/cli/core/init.ts Outdated
@tamirdresher
Copy link
Copy Markdown
Collaborator Author

🔍 Review finding: Workflow placement gap in monorepo mode

After running full E2E tests, a review identified a gap:

In monorepo mode, GitHub Actions workflow files (squad-heartbeat.yml, squad-triage.yml, etc.) are placed in the subfolder's .github/workflows/ — a location GitHub Actions completely ignores. Only <repo-root>/.github/workflows/ is processed by GitHub.

Current behavior (with this PR)

  • squad.agent.md → ✅ placed at git root (correct)
  • .squad/ → ✅ placed in subfolder (correct)
  • workflows/*.yml → ❌ placed in subfolder (wrong — GitHub won't find them)

Options

  1. Skip workflows in monorepo-subfolder mode — simplest, and arguably correct since multiple squads in one monorepo would conflict on the same workflow files anyway. Each squad's labels/triage/heartbeat would step on each other.

  2. Place workflows at git root — same approach as squad.agent.md. But raises the conflict question: which squad's triage workflow wins?

  3. Merge workflows intelligently — combine label sets from all squads into one workflow. Most complex.

I'm leaning toward option 1 (skip) for now and tracking the multi-squad workflow story separately. Would love feedback from @bradygaster on the preferred approach.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 6 comments.

Comments suppressed due to low confidence (1)

packages/squad-cli/src/cli/core/init.ts:280

  • runInit() currently never surfaces result.warnings from sdkInitSquad(). With this change, monorepo-subfolder mode intentionally emits a warning about workflows being skipped, but the CLI UX will drop it on the floor. Please print warnings after the created/skipped file lists (and consider using the same yellow warning styling used elsewhere) so users know workflows were intentionally not installed.
  // Ensure version is fully stamped in squad.agent.md
  const agentPath = path.join(agentFileRoot, '.github', 'agents', 'squad.agent.md');
  if (storage.existsSync(agentPath)) {
    stampVersion(agentPath, version);
  }

  // Persist --roles flag for the REPL to pick up during casting
  if (options.roles) {
    const rolesMarker = path.join(squadDir, '.init-roles');
    storage.writeSync(rolesMarker, '1');
    success(`base roles enabled — team will use built-in role catalog`);
  }

  // Report .init-prompt storage
  if (options.prompt) {
    success(`.init-prompt stored — team will be cast when you start squad`);
  }

  // Report created files
  for (const file of result.createdFiles) {
    // Files are already relative to teamRoot, just display as-is
    success(file);
  }

  // Report skipped files
  for (const file of result.skippedFiles) {
    // Files are already relative to teamRoot, just display as-is
    console.log(`${DIM}${file} already exists — skipping${RESET}`);
  }

Comment thread package.json Outdated
Comment thread packages/squad-cli/package.json Outdated
Comment thread packages/squad-sdk/package.json Outdated
Comment thread .github/agents/squad.agent.md
Comment thread test/init-scaffolding.test.ts
Comment thread packages/squad-sdk/src/config/init.ts
@serbrech
Copy link
Copy Markdown

serbrech commented Apr 13, 2026

I'm leaning toward option 1 (skip) for now and tracking the multi-squad workflow story separately. Would love feedback from @bradygaster on the preferred approach.

I agree.
on the longer term, I lean towards 2. Workflows are a repo concept. Even if the workflow was to involve squad, the agent is defined at the gitroot, and the routing could still occur.

  • can workflows be disabled (i.e ADO world...)?
  • are the squad skills also using a relative path (should also be tied to the gitroot, as the skills go with the agent because copilot look at them at the root?)

Q: how do I move to this? just squad upgrade? it's fine if I need to move the files/folders manually.

Copilot and others added 4 commits April 14, 2026 09:15
When squad init is run in a subfolder of a monorepo, it previously
ran git init to create a nested repo boundary. This broke monorepo
workflows by creating git-in-git.

Changes:
- Never run git init in init command
- Detect monorepo (cwd != git root) and split file placement:
  - .github/agents/squad.agent.md → git root (Copilot needs it there)
  - .squad/ → cwd subfolder (team state lives here)
- Add �gentFileRoot option to InitOptions for explicit control
- Update team root resolution: check CWD first for .squad/, then
  fall back to git root (enables multiple squads per monorepo)

Closes #939

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Sync canonical template (.squad-templates/) and run sync-templates.mjs
- Fix toRelativePath() to use path.relative() for cross-root paths
- Reuse detectParentGitRepo() instead of duplicating git-root logic
- Add monorepo regression test (no nested .git, agent at root, .squad in subfolder)
- Add .changeset for release tracking

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
GitHub Actions only reads workflows from <repo-root>/.github/workflows/.
In monorepo mode, placing workflows in a subfolder has no effect.
Additionally, multiple squads would conflict on the same workflow files.

- Skip workflow placement when agentFileRoot != teamRoot
- Emit warning in InitResult.warnings explaining why
- Add workflow skip + warning assertions to monorepo test

Found by Q (Devil's Advocate reviewer) during E2E test review.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ion, CLI test

- Revert all package.json versions from 0.9.1-build.3 to 0.9.1
  (build versions are local-only, changesets drive real bumps)
- Revert .github/agents/squad.agent.md to canonical template content
- Fix isMonorepoSubfolder: use path.resolve() + case-insensitive compare
- Add CLI-level runInit() monorepo test (verifies no .git in subfolder)
- Import path.resolve in SDK init.ts

Addresses review comments on #944.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@tamirdresher tamirdresher force-pushed the squad/939-monorepo-subfolder-support branch from 420de99 to 2a60095 Compare April 14, 2026 06:20
@tamirdresher
Copy link
Copy Markdown
Collaborator Author

✅ All review comments addressed (rebased + 4th commit)

Branch has been rebased onto latest \dev\ and all 12 review comments are fixed:

Version bumps (3 comments)

  • ✅ Reverted all \package.json\ versions from \

@tamirdresher
Copy link
Copy Markdown
Collaborator Author

Hey @serbrech

Workflows: Already skippable via --no-workflows flag. In monorepo mode this PR skips them automatically (since GitHub Actions ignores subfolder workflows anyway). Long-term we agree with option 2 — workflows at the git root with routing.

Skills: They resolve from CWD-relative .copilot/skills/, so each squad in a monorepo gets its own skill set. Works correctly because the agent template checks CWD first for .squad/.

Migration: For now it's 3 manual steps:

  1. rm -rf your-subfolder/.git (delete the nested git)
  2. mv your-subfolder/.github/agents/squad.agent.md .github/agents/ (move agent to git root)
  3. Keep .squad/ in the subfolder as-is

squad upgrade doesn't handle monorepo relocation yet — only squad init is monorepo-aware in this PR. We'll track monorepo-aware upgrade as a follow-up issue. For now the manual steps above are the migration path.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants