Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .changeset/feat-1314-ralph-instructions-template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
---
"@bradygaster/squad-cli": patch
---

Fix #1314: ship `ralph-instructions.md` template for `squad watch --execute`

The `.squad/ralph-instructions.md` escape hatch used by `squad watch --execute`
had no canonical template, no documentation, and was never created by `squad init`
or `squad upgrade`. This left users who discovered the code path inventing their
own format with no contract or safety guidance.

This patch promotes the escape hatch to a documented feature:

- Adds `packages/squad-cli/templates/ralph-instructions.md` — a comment-documented
stub that explains the override format, trust implications, and the stable contract
(what CAN vs CANNOT be customized via this file).
- Registers it in `TEMPLATE_MANIFEST` as a user-owned entry
(`overwriteOnUpgrade: false`) so `squad init` and `squad upgrade` install it
without clobbering user customizations. The destination path
(`ralph-instructions.md` under `.squad/`) exactly matches the `existsSync` lookup
in `execute.ts`.
- Adds three regression tests in `init-upgrade-parity.test.ts`: install on init,
non-overwrite on upgrade (preservation), and install-when-missing on upgrade.
- Adds one new test in `watch-capabilities.test.ts` asserting the `existsSync`
path includes the correct `.squad/ralph-instructions.md` segment derived from
`teamRoot`.

Closes #1314
67 changes: 67 additions & 0 deletions .squad-templates/ralph-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Ralph Instructions
<!-- User-owned: customize this file to override Ralph's autonomous-execution behavior.
squad init creates this file on first install; squad upgrade never overwrites it. -->

<!--
PURPOSE
-------
When `.squad/ralph-instructions.md` exists, `squad watch --execute` instructs the
spawned Copilot session to read this file and follow ALL sections here instead of
the built-in fallback prompt. If the file is absent, the built-in prompt is used.

CONTRACT (stable — safe to build on)
--------------------------------------
YOU CAN customize via this file:
• Extra instructions given to Ralph at session start (Teams/Slack notifications,
calendar checks, post-task hooks, MCP-powered side effects, escalation paths)
• Additional eligibility rules or priority ordering for issue selection
• Agent persona, tone, or verbosity for session output

YOU CANNOT override via this file:
• Parallelism — Ralph always spawns agents for all actionable issues simultaneously
• Core eligibility filter (squad/squad:* label required, not blocked, not assigned)
• The underlying `gh` / Copilot CLI command used to spawn each session

TRUST IMPLICATIONS
------------------
This file is read by the spawned Copilot session with full agent permissions.
Treat it like code — never paste untrusted content here. Anyone with write access
to this file can influence what the agent does on your behalf.

If this file is missing or empty, `squad watch --execute` falls back to the
built-in prompt with no behavioral change.

PLACEHOLDERS
------------
The following values are injected by execute.ts before the session reads this file:
(none currently — Ralph builds the issue list dynamically at runtime)

FORMAT
------
Plain markdown. Structure with ## sections. The spawned session reads the whole
file, so keep it concise — one screen of instructions is ideal.
-->

## Ralph, Go!

Read this file for your full instructions. Follow ALL sections.
MAXIMIZE PARALLELISM — spawn agents for ALL actionable issues simultaneously.

### Issue Selection

Work on every open, unblocked, unassigned issue labeled `squad` or `squad:{member}`.
Skip issues that are assigned to a human, blocked, or marked `status:on-hold`.

### Post-Task Actions

<!-- Uncomment and customize to add post-task hooks, e.g. Teams notifications:

After completing work on each issue:
- Post a brief summary to the team channel via your Teams MCP tool.
- Update the issue with a progress comment if no PR has been opened yet.
-->

### Escalation

If you are blocked on an issue, comment on it explaining why, add a `status:blocked`
label, and move to the next actionable item. Do not halt the loop.
2 changes: 1 addition & 1 deletion docs/src/content/docs/features/consult-mode.md
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,6 @@ All consultations are tracked in your personal squad at `consultations/{project}

## Next Steps

- **Set up a personal squad:** See [Your Personal Squad](../guide/personal-squad.md) for initial setup with `squad init --global`
- **Set up a personal squad:** See [Team Setup](../features/team-setup.md) for initial setup with `squad init --global`
- **Learn about sharing:** See [Export & Import](./export-import.md) for portable team snapshots
- **Upstream inheritance:** See [Upstream Inheritance](./upstream-inheritance.md) for knowledge sharing across teams
4 changes: 2 additions & 2 deletions docs/src/content/docs/features/squad-rc.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ squad rc --tunnel

**When to use `squad start`:** You want to mirror a terminal session to your phone (demos, pairing, watching long-running processes).

> 💡 **Looking for terminal mirroring?** See [squad start](./remote-control.md).
> 💡 **Looking for terminal mirroring?** See [squad start](./squad-rc.md).

---

Expand Down Expand Up @@ -471,7 +471,7 @@ squad rc --port 8080 # Specific port

## See Also

- [squad start](./remote-control.md) — PTY mirror mode for terminal streaming
- squad start — PTY mirror mode for terminal streaming
- [CLI Reference](../reference/cli.md) — All squad commands
- [Remote Control Protocol](https://github.com/bradygaster/squad/blob/main/packages/squad-sdk/src/remote/protocol.ts) — Wire protocol types
- [RemoteBridge SDK](https://github.com/bradygaster/squad/blob/main/packages/squad-sdk/src/remote/bridge.ts) — Server implementation
Expand Down
2 changes: 1 addition & 1 deletion docs/src/content/docs/features/vscode.md
Original file line number Diff line number Diff line change
Expand Up @@ -241,5 +241,5 @@ const decision = await coordinator.route(userTask, {
- [Getting Started](../get-started/installation.md) — Installation and setup guide
- [Parallel Execution](parallel-execution.md) — How Squadron fan-outs agents
- [Model Selection](model-selection.md) — Cost-first routing strategy
- [Interactive Shell](../guide/shell.md) — Shell commands and features
- [CLI Reference](../reference/cli.md) — Shell commands and features
- [SDK API Reference](../reference/api-reference.md) — Full SDK type and function reference
2 changes: 1 addition & 1 deletion docs/src/content/docs/reference/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ squad start --tunnel --yolo
squad start --tunnel --model gpt-4 --no-config
```

For details on architecture, security, mobile keyboard, and troubleshooting, see [Remote Control Guide](../features/remote-control.md).
For details on architecture, security, mobile keyboard, and troubleshooting, see [Remote Control Guide](../features/squad-rc.md).

---

Expand Down
14 changes: 13 additions & 1 deletion packages/squad-cli/src/cli/core/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,8 @@ import { initSquad as sdkInitSquad, cleanupOrphanInitPrompt, ensurePersonalSquad
import { installGitHooks } from '../commands/install-hooks.js';
import { liftInitMutableStateOntoOrphan } from '../commands/migrate-backend.js';
import { resolveSquadStateMcpSpec } from './mcp-spec.js';
import { describeMcpSpec } from './upgrade.js';
import { describeMcpSpec, ensureUserOwnedTemplates } from './upgrade.js';
import { getTemplatesDir } from './templates.js';
import { ensureSquadStateMcpInRoot, tombstoneStaleSquadStateInProjectMcp } from './mcp-root.js';
import {
readTeamMd,
Expand Down Expand Up @@ -326,6 +327,17 @@ export async function runInit(dest: string, options: RunInitOptions = {}): Promi
// immediately instead of after the 5-second TTL.
clearResolveSquadCache();

// Install user-owned template files that are not yet present on disk.
// These were not created by sdkInitSquad (which owns ceremonies.md, routing.md, etc.
// via the SDK templates path). ensureUserOwnedTemplates reads TEMPLATE_MANIFEST
// entries with overwriteOnUpgrade: false and copies each one only if absent.
Comment on lines +330 to +333
try {
const templatesDir = getTemplatesDir();
ensureUserOwnedTemplates(dest, templatesDir);
} catch {
// Non-fatal: if templates dir is not found (unusual install), skip silently.
}

// ── Personal squad linking ─────────────────────────────────────────
// When a personal squad directory exists and this is a repo init (not global),
// set teamRoot in config.json to point to the global squad root that contains
Expand Down
9 changes: 9 additions & 0 deletions packages/squad-cli/src/cli/core/templates.ts
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,15 @@ export const TEMPLATE_MANIFEST: TemplateFile[] = [
},

// User-owned files (never overwrite)
{
// ralph-instructions.md is read by execute.ts when `squad watch --execute` is run.
// User-owned so customizations survive upgrade. Install path must match the
// existsSync lookup in execute.ts: path.join(teamRoot, '.squad', 'ralph-instructions.md').
source: 'ralph-instructions.md',
destination: 'ralph-instructions.md',
overwriteOnUpgrade: false,
description: 'Ralph autonomous-execution instructions (user-customizable override)',
},
{
source: 'ceremonies.md',
destination: 'ceremonies.md',
Expand Down
39 changes: 39 additions & 0 deletions packages/squad-cli/src/cli/core/upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -853,6 +853,12 @@ async function runEnsureChecks(dest: string, templatesDir: string, filesUpdated:
filesUpdated.push(...builtinAgents);
}

const userOwned = ensureUserOwnedTemplates(dest, templatesDir);
if (userOwned.length > 0) {
success(`installed ${userOwned.length} missing user-owned template(s): ${userOwned.join(', ')}`);
filesUpdated.push(...userOwned);
}

const skillMigration = migrateLegacyCopilotSkills(dest);
if (skillMigration.migrated.length > 0) {
success(`migrated ${skillMigration.migrated.length} skill(s) from .copilot/skills/ → .github/skills/`);
Expand Down Expand Up @@ -1024,6 +1030,39 @@ export function ensureBuiltinAgents(dest: string, templatesDir: string): string[
return created;
}

/**
* Install user-owned template files that are missing on disk.
*
* Iterates TEMPLATE_MANIFEST entries with `overwriteOnUpgrade: false` and
* copies each source template to its destination only if the destination
* does not yet exist. Existing user-customized files are never touched.
*
* Called during both `squad init` (post-SDK scaffolding) and `squad upgrade`
* (via runEnsureChecks) so that newly-added user-owned templates are installed
* for existing squads on upgrade without overwriting any prior customization.
*/
export function ensureUserOwnedTemplates(dest: string, templatesDir: string): string[] {
const created: string[] = [];
const squadDir = path.join(dest, '.squad');
const userOwned = TEMPLATE_MANIFEST.filter(f => !f.overwriteOnUpgrade && !f.source.startsWith('skills/'));

for (const entry of userOwned) {
const srcPath = path.join(templatesDir, entry.source);
// Destination paths can be relative to .squad/ or can use '../' for repo root
const destPath = entry.destination.startsWith('../')
? path.join(dest, entry.destination.slice(3))
: path.join(squadDir, entry.destination);

if (!storage.existsSync(srcPath)) continue;
if (storage.existsSync(destPath)) continue;

storage.mkdirSync(path.dirname(destPath), { recursive: true });
storage.copySync(srcPath, destPath);
created.push(entry.destination);
}
return created;
}

/**
* Run the upgrade command
*/
Expand Down
67 changes: 67 additions & 0 deletions packages/squad-cli/templates/ralph-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Ralph Instructions
<!-- User-owned: customize this file to override Ralph's autonomous-execution behavior.
squad init creates this file on first install; squad upgrade never overwrites it. -->

<!--
PURPOSE
-------
When `.squad/ralph-instructions.md` exists, `squad watch --execute` instructs the
spawned Copilot session to read this file and follow ALL sections here instead of
the built-in fallback prompt. If the file is absent, the built-in prompt is used.

CONTRACT (stable — safe to build on)
--------------------------------------
YOU CAN customize via this file:
• Extra instructions given to Ralph at session start (Teams/Slack notifications,
calendar checks, post-task hooks, MCP-powered side effects, escalation paths)
• Additional eligibility rules or priority ordering for issue selection
• Agent persona, tone, or verbosity for session output

YOU CANNOT override via this file:
• Parallelism — Ralph always spawns agents for all actionable issues simultaneously
• Core eligibility filter (squad/squad:* label required, not blocked, not assigned)
• The underlying `gh` / Copilot CLI command used to spawn each session

TRUST IMPLICATIONS
------------------
This file is read by the spawned Copilot session with full agent permissions.
Treat it like code — never paste untrusted content here. Anyone with write access
to this file can influence what the agent does on your behalf.

If this file is missing or empty, `squad watch --execute` falls back to the
built-in prompt with no behavioral change.

PLACEHOLDERS
------------
The following values are injected by execute.ts before the session reads this file:
(none currently — Ralph builds the issue list dynamically at runtime)

FORMAT
------
Plain markdown. Structure with ## sections. The spawned session reads the whole
file, so keep it concise — one screen of instructions is ideal.
-->

## Ralph, Go!

Read this file for your full instructions. Follow ALL sections.
MAXIMIZE PARALLELISM — spawn agents for ALL actionable issues simultaneously.

### Issue Selection

Work on every open, unblocked, unassigned issue labeled `squad` or `squad:{member}`.
Skip issues that are assigned to a human, blocked, or marked `status:on-hold`.

### Post-Task Actions

<!-- Uncomment and customize to add post-task hooks, e.g. Teams notifications:

After completing work on each issue:
- Post a brief summary to the team channel via your Teams MCP tool.
- Update the issue with a progress comment if no PR has been opened yet.
-->

### Escalation

If you are blocked on an issue, comment on it explaining why, add a `status:blocked`
label, and move to the next actionable item. Do not halt the loop.
67 changes: 67 additions & 0 deletions packages/squad-sdk/templates/ralph-instructions.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Ralph Instructions
<!-- User-owned: customize this file to override Ralph's autonomous-execution behavior.
squad init creates this file on first install; squad upgrade never overwrites it. -->

<!--
PURPOSE
-------
When `.squad/ralph-instructions.md` exists, `squad watch --execute` instructs the
spawned Copilot session to read this file and follow ALL sections here instead of
the built-in fallback prompt. If the file is absent, the built-in prompt is used.

CONTRACT (stable — safe to build on)
--------------------------------------
YOU CAN customize via this file:
• Extra instructions given to Ralph at session start (Teams/Slack notifications,
calendar checks, post-task hooks, MCP-powered side effects, escalation paths)
• Additional eligibility rules or priority ordering for issue selection
• Agent persona, tone, or verbosity for session output

YOU CANNOT override via this file:
• Parallelism — Ralph always spawns agents for all actionable issues simultaneously
• Core eligibility filter (squad/squad:* label required, not blocked, not assigned)
• The underlying `gh` / Copilot CLI command used to spawn each session

TRUST IMPLICATIONS
------------------
This file is read by the spawned Copilot session with full agent permissions.
Treat it like code — never paste untrusted content here. Anyone with write access
to this file can influence what the agent does on your behalf.

If this file is missing or empty, `squad watch --execute` falls back to the
built-in prompt with no behavioral change.

PLACEHOLDERS
------------
The following values are injected by execute.ts before the session reads this file:
(none currently — Ralph builds the issue list dynamically at runtime)

FORMAT
------
Plain markdown. Structure with ## sections. The spawned session reads the whole
file, so keep it concise — one screen of instructions is ideal.
-->

## Ralph, Go!

Read this file for your full instructions. Follow ALL sections.
MAXIMIZE PARALLELISM — spawn agents for ALL actionable issues simultaneously.

### Issue Selection

Work on every open, unblocked, unassigned issue labeled `squad` or `squad:{member}`.
Skip issues that are assigned to a human, blocked, or marked `status:on-hold`.

### Post-Task Actions

<!-- Uncomment and customize to add post-task hooks, e.g. Teams notifications:

After completing work on each issue:
- Post a brief summary to the team channel via your Teams MCP tool.
- Update the issue with a progress comment if no PR has been opened yet.
-->

### Escalation

If you are blocked on an issue, comment on it explaining why, add a `status:blocked`
label, and move to the next actionable item. Do not halt the loop.
Loading
Loading