@bradygaster/squad-sdk 0.9.0 → 0.9.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +296 -296
- package/dist/agents/history-shadow.js +30 -30
- package/dist/build/github-dist.js +42 -42
- package/dist/config/init.js +173 -173
- package/dist/sharing/consult.js +78 -78
- package/package.json +1 -1
- package/templates/casting/Futurama.json +9 -9
- package/templates/casting-history.json +4 -4
- package/templates/casting-policy.json +37 -37
- package/templates/casting-reference.md +104 -104
- package/templates/casting-registry.json +3 -3
- package/templates/ceremonies.md +41 -41
- package/templates/charter.md +53 -53
- package/templates/constraint-tracking.md +38 -38
- package/templates/cooperative-rate-limiting.md +229 -229
- package/templates/copilot-instructions.md +46 -46
- package/templates/history.md +10 -10
- package/templates/identity/now.md +9 -9
- package/templates/identity/wisdom.md +15 -15
- package/templates/issue-lifecycle.md +412 -412
- package/templates/keda-scaler.md +164 -164
- package/templates/machine-capabilities.md +74 -74
- package/templates/mcp-config.md +90 -90
- package/templates/multi-agent-format.md +28 -28
- package/templates/plugin-marketplace.md +49 -49
- package/templates/ralph-circuit-breaker.md +313 -313
- package/templates/raw-agent-output.md +37 -37
- package/templates/roster.md +60 -60
- package/templates/routing.md +39 -39
- package/templates/run-output.md +50 -50
- package/templates/schedule.json +19 -19
- package/templates/scribe-charter.md +119 -119
- package/templates/skill.md +24 -24
- package/templates/skills/agent-collaboration/SKILL.md +42 -42
- package/templates/skills/agent-conduct/SKILL.md +24 -24
- package/templates/skills/architectural-proposals/SKILL.md +151 -151
- package/templates/skills/ci-validation-gates/SKILL.md +84 -84
- package/templates/skills/cli-wiring/SKILL.md +47 -47
- package/templates/skills/client-compatibility/SKILL.md +89 -89
- package/templates/skills/cross-squad/SKILL.md +114 -114
- package/templates/skills/distributed-mesh/SKILL.md +287 -287
- package/templates/skills/distributed-mesh/mesh.json.example +30 -30
- package/templates/skills/distributed-mesh/sync-mesh.ps1 +111 -111
- package/templates/skills/distributed-mesh/sync-mesh.sh +104 -104
- package/templates/skills/docs-standards/SKILL.md +71 -71
- package/templates/skills/economy-mode/SKILL.md +114 -114
- package/templates/skills/external-comms/SKILL.md +329 -329
- package/templates/skills/gh-auth-isolation/SKILL.md +183 -183
- package/templates/skills/git-workflow/SKILL.md +204 -204
- package/templates/skills/github-multi-account/SKILL.md +95 -95
- package/templates/skills/history-hygiene/SKILL.md +36 -36
- package/templates/skills/humanizer/SKILL.md +105 -105
- package/templates/skills/init-mode/SKILL.md +102 -102
- package/templates/skills/model-selection/SKILL.md +117 -117
- package/templates/skills/nap/SKILL.md +24 -24
- package/templates/skills/personal-squad/SKILL.md +57 -57
- package/templates/skills/project-conventions/SKILL.md +56 -56
- package/templates/skills/release-process/SKILL.md +423 -423
- package/templates/skills/reskill/SKILL.md +92 -92
- package/templates/skills/reviewer-protocol/SKILL.md +79 -79
- package/templates/skills/secret-handling/SKILL.md +200 -200
- package/templates/skills/session-recovery/SKILL.md +155 -155
- package/templates/skills/squad-conventions/SKILL.md +69 -69
- package/templates/skills/test-discipline/SKILL.md +37 -37
- package/templates/skills/windows-compatibility/SKILL.md +74 -74
- package/templates/workflows/squad-ci.yml +24 -24
- package/templates/workflows/squad-docs.yml +54 -54
- package/templates/workflows/squad-heartbeat.yml +171 -171
- package/templates/workflows/squad-insider-release.yml +61 -61
- package/templates/workflows/squad-issue-assign.yml +161 -161
- package/templates/workflows/squad-label-enforce.yml +181 -181
- package/templates/workflows/squad-preview.yml +55 -55
- package/templates/workflows/squad-promote.yml +120 -120
- package/templates/workflows/squad-release.yml +77 -77
- package/templates/workflows/squad-triage.yml +260 -260
- package/templates/workflows/sync-squad-labels.yml +169 -169
|
@@ -1,74 +1,74 @@
|
|
|
1
|
-
---
|
|
2
|
-
name: "windows-compatibility"
|
|
3
|
-
description: "Cross-platform path handling and command patterns"
|
|
4
|
-
domain: "platform"
|
|
5
|
-
confidence: "high"
|
|
6
|
-
source: "earned (multiple Windows-specific bugs: colons in filenames, git -C failures, path separators)"
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
## Context
|
|
10
|
-
|
|
11
|
-
Squad runs on Windows, macOS, and Linux. Several bugs have been traced to platform-specific assumptions: ISO timestamps with colons (illegal on Windows), `git -C` with Windows paths (unreliable), forward-slash paths in Node.js on Windows.
|
|
12
|
-
|
|
13
|
-
## Patterns
|
|
14
|
-
|
|
15
|
-
### Filenames & Timestamps
|
|
16
|
-
- **Never use colons in filenames:** ISO 8601 format `2026-03-15T05:30:00Z` is illegal on Windows
|
|
17
|
-
- **Use `safeTimestamp()` utility:** Replaces colons with hyphens → `2026-03-15T05-30-00Z`
|
|
18
|
-
- **Centralize formatting:** Don't inline `.toISOString().replace(/:/g, '-')` — use the utility
|
|
19
|
-
|
|
20
|
-
### Git Commands
|
|
21
|
-
- **Never use `git -C {path}`:** Unreliable with Windows paths (backslashes, spaces, drive letters)
|
|
22
|
-
- **Always `cd` first:** Change directory, then run git commands
|
|
23
|
-
- **Check for changes before commit:** `git diff --cached --quiet` (exit 0 = no changes)
|
|
24
|
-
|
|
25
|
-
### Commit Messages
|
|
26
|
-
- **Never embed newlines in `-m` flag:** Backtick-n (`\n`) fails silently in PowerShell
|
|
27
|
-
- **Use temp file + `-F` flag:** Write message to file, commit with `git commit -F $msgFile`
|
|
28
|
-
|
|
29
|
-
### Paths
|
|
30
|
-
- **Never assume CWD is repo root:** Always use `TEAM ROOT` from spawn prompt or run `git rev-parse --show-toplevel`
|
|
31
|
-
- **Use path.join() or path.resolve():** Don't manually concatenate with `/` or `\`
|
|
32
|
-
|
|
33
|
-
## Examples
|
|
34
|
-
|
|
35
|
-
✓ **Correct:**
|
|
36
|
-
```javascript
|
|
37
|
-
// Timestamp utility
|
|
38
|
-
const safeTimestamp = () => new Date().toISOString().replace(/:/g, '-').split('.')[0] + 'Z';
|
|
39
|
-
|
|
40
|
-
// Git workflow (PowerShell)
|
|
41
|
-
cd $teamRoot
|
|
42
|
-
git add .squad/
|
|
43
|
-
if ($LASTEXITCODE -eq 0) {
|
|
44
|
-
$msg = @"
|
|
45
|
-
docs(ai-team): session log
|
|
46
|
-
|
|
47
|
-
Changes:
|
|
48
|
-
- Added decisions
|
|
49
|
-
"@
|
|
50
|
-
$msgFile = [System.IO.Path]::GetTempFileName()
|
|
51
|
-
Set-Content -Path $msgFile -Value $msg -Encoding utf8
|
|
52
|
-
git commit -F $msgFile
|
|
53
|
-
Remove-Item $msgFile
|
|
54
|
-
}
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
✗ **Incorrect:**
|
|
58
|
-
```javascript
|
|
59
|
-
// Colon in filename
|
|
60
|
-
const logPath = `.squad/log/${new Date().toISOString()}.md`; // ILLEGAL on Windows
|
|
61
|
-
|
|
62
|
-
// git -C with Windows path
|
|
63
|
-
exec('git -C C:\\src\\squad add .squad/'); // UNRELIABLE
|
|
64
|
-
|
|
65
|
-
// Inline newlines in commit message
|
|
66
|
-
exec('git commit -m "First line\nSecond line"'); // FAILS silently in PowerShell
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
## Anti-Patterns
|
|
70
|
-
|
|
71
|
-
- Testing only on one platform (bugs ship to other platforms)
|
|
72
|
-
- Assuming Unix-style paths work everywhere
|
|
73
|
-
- Using `git -C` because it "looks cleaner" (it doesn't work)
|
|
74
|
-
- Skipping `git diff --cached --quiet` check (creates empty commits)
|
|
1
|
+
---
|
|
2
|
+
name: "windows-compatibility"
|
|
3
|
+
description: "Cross-platform path handling and command patterns"
|
|
4
|
+
domain: "platform"
|
|
5
|
+
confidence: "high"
|
|
6
|
+
source: "earned (multiple Windows-specific bugs: colons in filenames, git -C failures, path separators)"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Context
|
|
10
|
+
|
|
11
|
+
Squad runs on Windows, macOS, and Linux. Several bugs have been traced to platform-specific assumptions: ISO timestamps with colons (illegal on Windows), `git -C` with Windows paths (unreliable), forward-slash paths in Node.js on Windows.
|
|
12
|
+
|
|
13
|
+
## Patterns
|
|
14
|
+
|
|
15
|
+
### Filenames & Timestamps
|
|
16
|
+
- **Never use colons in filenames:** ISO 8601 format `2026-03-15T05:30:00Z` is illegal on Windows
|
|
17
|
+
- **Use `safeTimestamp()` utility:** Replaces colons with hyphens → `2026-03-15T05-30-00Z`
|
|
18
|
+
- **Centralize formatting:** Don't inline `.toISOString().replace(/:/g, '-')` — use the utility
|
|
19
|
+
|
|
20
|
+
### Git Commands
|
|
21
|
+
- **Never use `git -C {path}`:** Unreliable with Windows paths (backslashes, spaces, drive letters)
|
|
22
|
+
- **Always `cd` first:** Change directory, then run git commands
|
|
23
|
+
- **Check for changes before commit:** `git diff --cached --quiet` (exit 0 = no changes)
|
|
24
|
+
|
|
25
|
+
### Commit Messages
|
|
26
|
+
- **Never embed newlines in `-m` flag:** Backtick-n (`\n`) fails silently in PowerShell
|
|
27
|
+
- **Use temp file + `-F` flag:** Write message to file, commit with `git commit -F $msgFile`
|
|
28
|
+
|
|
29
|
+
### Paths
|
|
30
|
+
- **Never assume CWD is repo root:** Always use `TEAM ROOT` from spawn prompt or run `git rev-parse --show-toplevel`
|
|
31
|
+
- **Use path.join() or path.resolve():** Don't manually concatenate with `/` or `\`
|
|
32
|
+
|
|
33
|
+
## Examples
|
|
34
|
+
|
|
35
|
+
✓ **Correct:**
|
|
36
|
+
```javascript
|
|
37
|
+
// Timestamp utility
|
|
38
|
+
const safeTimestamp = () => new Date().toISOString().replace(/:/g, '-').split('.')[0] + 'Z';
|
|
39
|
+
|
|
40
|
+
// Git workflow (PowerShell)
|
|
41
|
+
cd $teamRoot
|
|
42
|
+
git add .squad/
|
|
43
|
+
if ($LASTEXITCODE -eq 0) {
|
|
44
|
+
$msg = @"
|
|
45
|
+
docs(ai-team): session log
|
|
46
|
+
|
|
47
|
+
Changes:
|
|
48
|
+
- Added decisions
|
|
49
|
+
"@
|
|
50
|
+
$msgFile = [System.IO.Path]::GetTempFileName()
|
|
51
|
+
Set-Content -Path $msgFile -Value $msg -Encoding utf8
|
|
52
|
+
git commit -F $msgFile
|
|
53
|
+
Remove-Item $msgFile
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
✗ **Incorrect:**
|
|
58
|
+
```javascript
|
|
59
|
+
// Colon in filename
|
|
60
|
+
const logPath = `.squad/log/${new Date().toISOString()}.md`; // ILLEGAL on Windows
|
|
61
|
+
|
|
62
|
+
// git -C with Windows path
|
|
63
|
+
exec('git -C C:\\src\\squad add .squad/'); // UNRELIABLE
|
|
64
|
+
|
|
65
|
+
// Inline newlines in commit message
|
|
66
|
+
exec('git commit -m "First line\nSecond line"'); // FAILS silently in PowerShell
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Anti-Patterns
|
|
70
|
+
|
|
71
|
+
- Testing only on one platform (bugs ship to other platforms)
|
|
72
|
+
- Assuming Unix-style paths work everywhere
|
|
73
|
+
- Using `git -C` because it "looks cleaner" (it doesn't work)
|
|
74
|
+
- Skipping `git diff --cached --quiet` check (creates empty commits)
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
name: Squad CI
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
pull_request:
|
|
5
|
-
branches: [dev, preview, main, insider]
|
|
6
|
-
types: [opened, synchronize, reopened]
|
|
7
|
-
push:
|
|
8
|
-
branches: [dev, insider]
|
|
9
|
-
|
|
10
|
-
permissions:
|
|
11
|
-
contents: read
|
|
12
|
-
|
|
13
|
-
jobs:
|
|
14
|
-
test:
|
|
15
|
-
runs-on: ubuntu-latest
|
|
16
|
-
steps:
|
|
17
|
-
- uses: actions/checkout@v4
|
|
18
|
-
|
|
19
|
-
- uses: actions/setup-node@v4
|
|
20
|
-
with:
|
|
21
|
-
node-version: 22
|
|
22
|
-
|
|
23
|
-
- name: Run tests
|
|
24
|
-
run: node --test test/*.test.js
|
|
1
|
+
name: Squad CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
branches: [dev, preview, main, insider]
|
|
6
|
+
types: [opened, synchronize, reopened]
|
|
7
|
+
push:
|
|
8
|
+
branches: [dev, insider]
|
|
9
|
+
|
|
10
|
+
permissions:
|
|
11
|
+
contents: read
|
|
12
|
+
|
|
13
|
+
jobs:
|
|
14
|
+
test:
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v4
|
|
18
|
+
|
|
19
|
+
- uses: actions/setup-node@v4
|
|
20
|
+
with:
|
|
21
|
+
node-version: 22
|
|
22
|
+
|
|
23
|
+
- name: Run tests
|
|
24
|
+
run: node --test test/*.test.js
|
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
name: Squad Docs — Build & Deploy
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
workflow_dispatch:
|
|
5
|
-
push:
|
|
6
|
-
branches: [preview]
|
|
7
|
-
paths:
|
|
8
|
-
- 'docs/**'
|
|
9
|
-
- '.github/workflows/squad-docs.yml'
|
|
10
|
-
|
|
11
|
-
permissions:
|
|
12
|
-
contents: read
|
|
13
|
-
pages: write
|
|
14
|
-
id-token: write
|
|
15
|
-
|
|
16
|
-
concurrency:
|
|
17
|
-
group: pages
|
|
18
|
-
cancel-in-progress: true
|
|
19
|
-
|
|
20
|
-
jobs:
|
|
21
|
-
build:
|
|
22
|
-
runs-on: ubuntu-latest
|
|
23
|
-
steps:
|
|
24
|
-
- uses: actions/checkout@v4
|
|
25
|
-
|
|
26
|
-
- uses: actions/setup-node@v4
|
|
27
|
-
with:
|
|
28
|
-
node-version: '22'
|
|
29
|
-
cache: npm
|
|
30
|
-
cache-dependency-path: docs/package-lock.json
|
|
31
|
-
|
|
32
|
-
- name: Install docs dependencies
|
|
33
|
-
working-directory: docs
|
|
34
|
-
run: npm ci
|
|
35
|
-
|
|
36
|
-
- name: Build docs site
|
|
37
|
-
working-directory: docs
|
|
38
|
-
run: npm run build
|
|
39
|
-
|
|
40
|
-
- name: Upload Pages artifact
|
|
41
|
-
uses: actions/upload-pages-artifact@v3
|
|
42
|
-
with:
|
|
43
|
-
path: docs/dist
|
|
44
|
-
|
|
45
|
-
deploy:
|
|
46
|
-
needs: build
|
|
47
|
-
runs-on: ubuntu-latest
|
|
48
|
-
environment:
|
|
49
|
-
name: github-pages
|
|
50
|
-
url: ${{ steps.deployment.outputs.page_url }}
|
|
51
|
-
steps:
|
|
52
|
-
- name: Deploy to GitHub Pages
|
|
53
|
-
id: deployment
|
|
54
|
-
uses: actions/deploy-pages@v4
|
|
1
|
+
name: Squad Docs — Build & Deploy
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
push:
|
|
6
|
+
branches: [preview]
|
|
7
|
+
paths:
|
|
8
|
+
- 'docs/**'
|
|
9
|
+
- '.github/workflows/squad-docs.yml'
|
|
10
|
+
|
|
11
|
+
permissions:
|
|
12
|
+
contents: read
|
|
13
|
+
pages: write
|
|
14
|
+
id-token: write
|
|
15
|
+
|
|
16
|
+
concurrency:
|
|
17
|
+
group: pages
|
|
18
|
+
cancel-in-progress: true
|
|
19
|
+
|
|
20
|
+
jobs:
|
|
21
|
+
build:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@v4
|
|
25
|
+
|
|
26
|
+
- uses: actions/setup-node@v4
|
|
27
|
+
with:
|
|
28
|
+
node-version: '22'
|
|
29
|
+
cache: npm
|
|
30
|
+
cache-dependency-path: docs/package-lock.json
|
|
31
|
+
|
|
32
|
+
- name: Install docs dependencies
|
|
33
|
+
working-directory: docs
|
|
34
|
+
run: npm ci
|
|
35
|
+
|
|
36
|
+
- name: Build docs site
|
|
37
|
+
working-directory: docs
|
|
38
|
+
run: npm run build
|
|
39
|
+
|
|
40
|
+
- name: Upload Pages artifact
|
|
41
|
+
uses: actions/upload-pages-artifact@v3
|
|
42
|
+
with:
|
|
43
|
+
path: docs/dist
|
|
44
|
+
|
|
45
|
+
deploy:
|
|
46
|
+
needs: build
|
|
47
|
+
runs-on: ubuntu-latest
|
|
48
|
+
environment:
|
|
49
|
+
name: github-pages
|
|
50
|
+
url: ${{ steps.deployment.outputs.page_url }}
|
|
51
|
+
steps:
|
|
52
|
+
- name: Deploy to GitHub Pages
|
|
53
|
+
id: deployment
|
|
54
|
+
uses: actions/deploy-pages@v4
|
|
@@ -1,171 +1,171 @@
|
|
|
1
|
-
name: Squad Heartbeat (Ralph)
|
|
2
|
-
# ⚠️ SYNC: This workflow is maintained in 4 locations. Changes must be applied to all:
|
|
3
|
-
# - templates/workflows/squad-heartbeat.yml (source template)
|
|
4
|
-
# - packages/squad-cli/templates/workflows/squad-heartbeat.yml (CLI package)
|
|
5
|
-
# - .squad/templates/workflows/squad-heartbeat.yml (installed template)
|
|
6
|
-
# - .github/workflows/squad-heartbeat.yml (active workflow)
|
|
7
|
-
# Run 'squad upgrade' to sync installed copies from source templates.
|
|
8
|
-
|
|
9
|
-
on:
|
|
10
|
-
schedule:
|
|
11
|
-
# Every 30 minutes — adjust via cron expression as needed
|
|
12
|
-
- cron: '*/30 * * * *'
|
|
13
|
-
|
|
14
|
-
# React to completed work or new squad work
|
|
15
|
-
issues:
|
|
16
|
-
types: [closed, labeled]
|
|
17
|
-
pull_request:
|
|
18
|
-
types: [closed]
|
|
19
|
-
|
|
20
|
-
# Manual trigger
|
|
21
|
-
workflow_dispatch:
|
|
22
|
-
|
|
23
|
-
permissions:
|
|
24
|
-
issues: write
|
|
25
|
-
contents: read
|
|
26
|
-
pull-requests: read
|
|
27
|
-
|
|
28
|
-
jobs:
|
|
29
|
-
heartbeat:
|
|
30
|
-
runs-on: ubuntu-latest
|
|
31
|
-
steps:
|
|
32
|
-
- uses: actions/checkout@v4
|
|
33
|
-
|
|
34
|
-
- name: Check triage script
|
|
35
|
-
id: check-script
|
|
36
|
-
run: |
|
|
37
|
-
if [ -f ".squad/templates/ralph-triage.js" ]; then
|
|
38
|
-
echo "has_script=true" >> $GITHUB_OUTPUT
|
|
39
|
-
else
|
|
40
|
-
echo "has_script=false" >> $GITHUB_OUTPUT
|
|
41
|
-
echo "⚠️ ralph-triage.js not found — run 'squad upgrade' to install"
|
|
42
|
-
fi
|
|
43
|
-
|
|
44
|
-
- name: Ralph — Smart triage
|
|
45
|
-
if: steps.check-script.outputs.has_script == 'true'
|
|
46
|
-
env:
|
|
47
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
48
|
-
run: |
|
|
49
|
-
node .squad/templates/ralph-triage.js \
|
|
50
|
-
--squad-dir .squad \
|
|
51
|
-
--output triage-results.json
|
|
52
|
-
|
|
53
|
-
- name: Ralph — Apply triage decisions
|
|
54
|
-
if: steps.check-script.outputs.has_script == 'true' && hashFiles('triage-results.json') != ''
|
|
55
|
-
uses: actions/github-script@v7
|
|
56
|
-
with:
|
|
57
|
-
script: |
|
|
58
|
-
const fs = require('fs');
|
|
59
|
-
const path = 'triage-results.json';
|
|
60
|
-
if (!fs.existsSync(path)) {
|
|
61
|
-
core.info('No triage results — board is clear');
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
const results = JSON.parse(fs.readFileSync(path, 'utf8'));
|
|
66
|
-
if (results.length === 0) {
|
|
67
|
-
core.info('📋 Board is clear — Ralph found no untriaged issues');
|
|
68
|
-
return;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
for (const decision of results) {
|
|
72
|
-
try {
|
|
73
|
-
await github.rest.issues.addLabels({
|
|
74
|
-
owner: context.repo.owner,
|
|
75
|
-
repo: context.repo.repo,
|
|
76
|
-
issue_number: decision.issueNumber,
|
|
77
|
-
labels: [decision.label]
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
await github.rest.issues.createComment({
|
|
81
|
-
owner: context.repo.owner,
|
|
82
|
-
repo: context.repo.repo,
|
|
83
|
-
issue_number: decision.issueNumber,
|
|
84
|
-
body: [
|
|
85
|
-
'### 🔄 Ralph — Auto-Triage',
|
|
86
|
-
'',
|
|
87
|
-
`**Assigned to:** ${decision.assignTo}`,
|
|
88
|
-
`**Reason:** ${decision.reason}`,
|
|
89
|
-
`**Source:** ${decision.source}`,
|
|
90
|
-
'',
|
|
91
|
-
'> Ralph auto-triaged this issue using routing rules.',
|
|
92
|
-
'> To reassign, swap the `squad:*` label.'
|
|
93
|
-
].join('\n')
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
core.info(`Triaged #${decision.issueNumber} → ${decision.assignTo} (${decision.source})`);
|
|
97
|
-
} catch (e) {
|
|
98
|
-
core.warning(`Failed to triage #${decision.issueNumber}: ${e.message}`);
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
core.info(`🔄 Ralph triaged ${results.length} issue(s)`);
|
|
103
|
-
|
|
104
|
-
# Copilot auto-assign step (uses PAT if available)
|
|
105
|
-
- name: Ralph — Assign @copilot issues
|
|
106
|
-
if: success()
|
|
107
|
-
uses: actions/github-script@v7
|
|
108
|
-
with:
|
|
109
|
-
github-token: ${{ secrets.COPILOT_ASSIGN_TOKEN || secrets.GITHUB_TOKEN }}
|
|
110
|
-
script: |
|
|
111
|
-
const fs = require('fs');
|
|
112
|
-
|
|
113
|
-
let teamFile = '.squad/team.md';
|
|
114
|
-
if (!fs.existsSync(teamFile)) {
|
|
115
|
-
teamFile = '.ai-team/team.md';
|
|
116
|
-
}
|
|
117
|
-
if (!fs.existsSync(teamFile)) return;
|
|
118
|
-
|
|
119
|
-
const content = fs.readFileSync(teamFile, 'utf8');
|
|
120
|
-
|
|
121
|
-
// Check if @copilot is on the team with auto-assign
|
|
122
|
-
const hasCopilot = content.includes('🤖 Coding Agent') || content.includes('@copilot');
|
|
123
|
-
const autoAssign = content.includes('<!-- copilot-auto-assign: true -->');
|
|
124
|
-
if (!hasCopilot || !autoAssign) return;
|
|
125
|
-
|
|
126
|
-
// Find issues labeled squad:copilot with no assignee
|
|
127
|
-
try {
|
|
128
|
-
const { data: copilotIssues } = await github.rest.issues.listForRepo({
|
|
129
|
-
owner: context.repo.owner,
|
|
130
|
-
repo: context.repo.repo,
|
|
131
|
-
labels: 'squad:copilot',
|
|
132
|
-
state: 'open',
|
|
133
|
-
per_page: 5
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
const unassigned = copilotIssues.filter(i =>
|
|
137
|
-
!i.assignees || i.assignees.length === 0
|
|
138
|
-
);
|
|
139
|
-
|
|
140
|
-
if (unassigned.length === 0) {
|
|
141
|
-
core.info('No unassigned squad:copilot issues');
|
|
142
|
-
return;
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Get repo default branch
|
|
146
|
-
const { data: repoData } = await github.rest.repos.get({
|
|
147
|
-
owner: context.repo.owner,
|
|
148
|
-
repo: context.repo.repo
|
|
149
|
-
});
|
|
150
|
-
|
|
151
|
-
for (const issue of unassigned) {
|
|
152
|
-
try {
|
|
153
|
-
await github.request('POST /repos/{owner}/{repo}/issues/{issue_number}/assignees', {
|
|
154
|
-
owner: context.repo.owner,
|
|
155
|
-
repo: context.repo.repo,
|
|
156
|
-
issue_number: issue.number,
|
|
157
|
-
assignees: ['copilot-swe-agent[bot]'],
|
|
158
|
-
agent_assignment: {
|
|
159
|
-
target_repo: `${context.repo.owner}/${context.repo.repo}`,
|
|
160
|
-
base_branch: repoData.default_branch,
|
|
161
|
-
custom_instructions: `Read .squad/team.md (or .ai-team/team.md) for team context and .squad/routing.md (or .ai-team/routing.md) for routing rules.`
|
|
162
|
-
}
|
|
163
|
-
});
|
|
164
|
-
core.info(`Assigned copilot-swe-agent[bot] to #${issue.number}`);
|
|
165
|
-
} catch (e) {
|
|
166
|
-
core.warning(`Failed to assign @copilot to #${issue.number}: ${e.message}`);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
} catch (e) {
|
|
170
|
-
core.info(`No squad:copilot label found or error: ${e.message}`);
|
|
171
|
-
}
|
|
1
|
+
name: Squad Heartbeat (Ralph)
|
|
2
|
+
# ⚠️ SYNC: This workflow is maintained in 4 locations. Changes must be applied to all:
|
|
3
|
+
# - templates/workflows/squad-heartbeat.yml (source template)
|
|
4
|
+
# - packages/squad-cli/templates/workflows/squad-heartbeat.yml (CLI package)
|
|
5
|
+
# - .squad/templates/workflows/squad-heartbeat.yml (installed template)
|
|
6
|
+
# - .github/workflows/squad-heartbeat.yml (active workflow)
|
|
7
|
+
# Run 'squad upgrade' to sync installed copies from source templates.
|
|
8
|
+
|
|
9
|
+
on:
|
|
10
|
+
schedule:
|
|
11
|
+
# Every 30 minutes — adjust via cron expression as needed
|
|
12
|
+
- cron: '*/30 * * * *'
|
|
13
|
+
|
|
14
|
+
# React to completed work or new squad work
|
|
15
|
+
issues:
|
|
16
|
+
types: [closed, labeled]
|
|
17
|
+
pull_request:
|
|
18
|
+
types: [closed]
|
|
19
|
+
|
|
20
|
+
# Manual trigger
|
|
21
|
+
workflow_dispatch:
|
|
22
|
+
|
|
23
|
+
permissions:
|
|
24
|
+
issues: write
|
|
25
|
+
contents: read
|
|
26
|
+
pull-requests: read
|
|
27
|
+
|
|
28
|
+
jobs:
|
|
29
|
+
heartbeat:
|
|
30
|
+
runs-on: ubuntu-latest
|
|
31
|
+
steps:
|
|
32
|
+
- uses: actions/checkout@v4
|
|
33
|
+
|
|
34
|
+
- name: Check triage script
|
|
35
|
+
id: check-script
|
|
36
|
+
run: |
|
|
37
|
+
if [ -f ".squad/templates/ralph-triage.js" ]; then
|
|
38
|
+
echo "has_script=true" >> $GITHUB_OUTPUT
|
|
39
|
+
else
|
|
40
|
+
echo "has_script=false" >> $GITHUB_OUTPUT
|
|
41
|
+
echo "⚠️ ralph-triage.js not found — run 'squad upgrade' to install"
|
|
42
|
+
fi
|
|
43
|
+
|
|
44
|
+
- name: Ralph — Smart triage
|
|
45
|
+
if: steps.check-script.outputs.has_script == 'true'
|
|
46
|
+
env:
|
|
47
|
+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
48
|
+
run: |
|
|
49
|
+
node .squad/templates/ralph-triage.js \
|
|
50
|
+
--squad-dir .squad \
|
|
51
|
+
--output triage-results.json
|
|
52
|
+
|
|
53
|
+
- name: Ralph — Apply triage decisions
|
|
54
|
+
if: steps.check-script.outputs.has_script == 'true' && hashFiles('triage-results.json') != ''
|
|
55
|
+
uses: actions/github-script@v7
|
|
56
|
+
with:
|
|
57
|
+
script: |
|
|
58
|
+
const fs = require('fs');
|
|
59
|
+
const path = 'triage-results.json';
|
|
60
|
+
if (!fs.existsSync(path)) {
|
|
61
|
+
core.info('No triage results — board is clear');
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const results = JSON.parse(fs.readFileSync(path, 'utf8'));
|
|
66
|
+
if (results.length === 0) {
|
|
67
|
+
core.info('📋 Board is clear — Ralph found no untriaged issues');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (const decision of results) {
|
|
72
|
+
try {
|
|
73
|
+
await github.rest.issues.addLabels({
|
|
74
|
+
owner: context.repo.owner,
|
|
75
|
+
repo: context.repo.repo,
|
|
76
|
+
issue_number: decision.issueNumber,
|
|
77
|
+
labels: [decision.label]
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
await github.rest.issues.createComment({
|
|
81
|
+
owner: context.repo.owner,
|
|
82
|
+
repo: context.repo.repo,
|
|
83
|
+
issue_number: decision.issueNumber,
|
|
84
|
+
body: [
|
|
85
|
+
'### 🔄 Ralph — Auto-Triage',
|
|
86
|
+
'',
|
|
87
|
+
`**Assigned to:** ${decision.assignTo}`,
|
|
88
|
+
`**Reason:** ${decision.reason}`,
|
|
89
|
+
`**Source:** ${decision.source}`,
|
|
90
|
+
'',
|
|
91
|
+
'> Ralph auto-triaged this issue using routing rules.',
|
|
92
|
+
'> To reassign, swap the `squad:*` label.'
|
|
93
|
+
].join('\n')
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
core.info(`Triaged #${decision.issueNumber} → ${decision.assignTo} (${decision.source})`);
|
|
97
|
+
} catch (e) {
|
|
98
|
+
core.warning(`Failed to triage #${decision.issueNumber}: ${e.message}`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
core.info(`🔄 Ralph triaged ${results.length} issue(s)`);
|
|
103
|
+
|
|
104
|
+
# Copilot auto-assign step (uses PAT if available)
|
|
105
|
+
- name: Ralph — Assign @copilot issues
|
|
106
|
+
if: success()
|
|
107
|
+
uses: actions/github-script@v7
|
|
108
|
+
with:
|
|
109
|
+
github-token: ${{ secrets.COPILOT_ASSIGN_TOKEN || secrets.GITHUB_TOKEN }}
|
|
110
|
+
script: |
|
|
111
|
+
const fs = require('fs');
|
|
112
|
+
|
|
113
|
+
let teamFile = '.squad/team.md';
|
|
114
|
+
if (!fs.existsSync(teamFile)) {
|
|
115
|
+
teamFile = '.ai-team/team.md';
|
|
116
|
+
}
|
|
117
|
+
if (!fs.existsSync(teamFile)) return;
|
|
118
|
+
|
|
119
|
+
const content = fs.readFileSync(teamFile, 'utf8');
|
|
120
|
+
|
|
121
|
+
// Check if @copilot is on the team with auto-assign
|
|
122
|
+
const hasCopilot = content.includes('🤖 Coding Agent') || content.includes('@copilot');
|
|
123
|
+
const autoAssign = content.includes('<!-- copilot-auto-assign: true -->');
|
|
124
|
+
if (!hasCopilot || !autoAssign) return;
|
|
125
|
+
|
|
126
|
+
// Find issues labeled squad:copilot with no assignee
|
|
127
|
+
try {
|
|
128
|
+
const { data: copilotIssues } = await github.rest.issues.listForRepo({
|
|
129
|
+
owner: context.repo.owner,
|
|
130
|
+
repo: context.repo.repo,
|
|
131
|
+
labels: 'squad:copilot',
|
|
132
|
+
state: 'open',
|
|
133
|
+
per_page: 5
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const unassigned = copilotIssues.filter(i =>
|
|
137
|
+
!i.assignees || i.assignees.length === 0
|
|
138
|
+
);
|
|
139
|
+
|
|
140
|
+
if (unassigned.length === 0) {
|
|
141
|
+
core.info('No unassigned squad:copilot issues');
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Get repo default branch
|
|
146
|
+
const { data: repoData } = await github.rest.repos.get({
|
|
147
|
+
owner: context.repo.owner,
|
|
148
|
+
repo: context.repo.repo
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
for (const issue of unassigned) {
|
|
152
|
+
try {
|
|
153
|
+
await github.request('POST /repos/{owner}/{repo}/issues/{issue_number}/assignees', {
|
|
154
|
+
owner: context.repo.owner,
|
|
155
|
+
repo: context.repo.repo,
|
|
156
|
+
issue_number: issue.number,
|
|
157
|
+
assignees: ['copilot-swe-agent[bot]'],
|
|
158
|
+
agent_assignment: {
|
|
159
|
+
target_repo: `${context.repo.owner}/${context.repo.repo}`,
|
|
160
|
+
base_branch: repoData.default_branch,
|
|
161
|
+
custom_instructions: `Read .squad/team.md (or .ai-team/team.md) for team context and .squad/routing.md (or .ai-team/routing.md) for routing rules.`
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
core.info(`Assigned copilot-swe-agent[bot] to #${issue.number}`);
|
|
165
|
+
} catch (e) {
|
|
166
|
+
core.warning(`Failed to assign @copilot to #${issue.number}: ${e.message}`);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
} catch (e) {
|
|
170
|
+
core.info(`No squad:copilot label found or error: ${e.message}`);
|
|
171
|
+
}
|