@bradygaster/squad-cli 0.9.5-insider.1 → 0.9.6-insider.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/dist/cli/commands/install-hooks.d.ts +26 -0
- package/dist/cli/commands/install-hooks.d.ts.map +1 -0
- package/dist/cli/commands/install-hooks.js +244 -0
- package/dist/cli/commands/install-hooks.js.map +1 -0
- package/dist/cli/commands/migrate-backend.d.ts +16 -0
- package/dist/cli/commands/migrate-backend.d.ts.map +1 -0
- package/dist/cli/commands/migrate-backend.js +87 -0
- package/dist/cli/commands/migrate-backend.js.map +1 -0
- package/dist/cli/commands/preset.d.ts +24 -0
- package/dist/cli/commands/preset.d.ts.map +1 -0
- package/dist/cli/commands/preset.js +352 -0
- package/dist/cli/commands/preset.js.map +1 -0
- package/dist/cli/commands/sync.d.ts +24 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +257 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/commands/watch/capabilities/board.d.ts.map +1 -1
- package/dist/cli/commands/watch/capabilities/board.js +7 -10
- package/dist/cli/commands/watch/capabilities/board.js.map +1 -1
- package/dist/cli/commands/watch/capabilities/fleet-dispatch.d.ts.map +1 -1
- package/dist/cli/commands/watch/capabilities/fleet-dispatch.js +7 -13
- package/dist/cli/commands/watch/capabilities/fleet-dispatch.js.map +1 -1
- package/dist/cli/commands/watch/capabilities/self-pull.d.ts.map +1 -1
- package/dist/cli/commands/watch/capabilities/self-pull.js +9 -10
- package/dist/cli/commands/watch/capabilities/self-pull.js.map +1 -1
- package/dist/cli/commands/watch/capabilities/two-pass.d.ts.map +1 -1
- package/dist/cli/commands/watch/capabilities/two-pass.js +5 -7
- package/dist/cli/commands/watch/capabilities/two-pass.js.map +1 -1
- package/dist/cli/commands/watch/config.d.ts +4 -1
- package/dist/cli/commands/watch/config.d.ts.map +1 -1
- package/dist/cli/commands/watch/config.js +2 -1
- package/dist/cli/commands/watch/config.js.map +1 -1
- package/dist/cli/commands/watch/types.d.ts +1 -5
- package/dist/cli/commands/watch/types.d.ts.map +1 -1
- package/dist/cli/core/gh-cli.d.ts.map +1 -1
- package/dist/cli/core/gh-cli.js +6 -7
- package/dist/cli/core/gh-cli.js.map +1 -1
- package/dist/cli/core/init.d.ts +2 -10
- package/dist/cli/core/init.d.ts.map +1 -1
- package/dist/cli/core/init.js +63 -56
- package/dist/cli/core/init.js.map +1 -1
- package/dist/cli/index.d.ts +0 -2
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +0 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli-entry.js +71 -10
- package/dist/cli-entry.js.map +1 -1
- package/package.json +4 -3
- package/templates/notes-protocol.md +202 -0
- package/templates/scribe-charter.md +95 -1
- package/templates/scripts/notes/fetch.ps1 +88 -0
- package/templates/scripts/notes/write-note.ps1 +126 -0
- package/templates/skills/cross-machine-coordination/SKILL.md +8 -0
- package/templates/skills/init-mode/SKILL.md +3 -3
- package/templates/skills/model-selection/SKILL.md +8 -0
- package/templates/skills/nap/SKILL.md +8 -0
- package/templates/skills/personal-squad/SKILL.md +8 -0
- package/templates/skills/ralph-two-pass-scan/SKILL.md +8 -0
- package/templates/skills/release-process/SKILL.md +91 -5
- package/templates/squad.agent.md.template +137 -14
- package/dist/cli/commands/skill.d.ts +0 -31
- package/dist/cli/commands/skill.d.ts.map +0 -1
- package/dist/cli/commands/skill.js +0 -496
- package/dist/cli/commands/skill.js.map +0 -1
- package/dist/cli/commands/watch/agent-spawn.d.ts +0 -62
- package/dist/cli/commands/watch/agent-spawn.d.ts.map +0 -1
- package/dist/cli/commands/watch/agent-spawn.js +0 -127
- package/dist/cli/commands/watch/agent-spawn.js.map +0 -1
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
# Squad Notes Protocol
|
|
2
|
+
|
|
3
|
+
> Contract for agent state via git notes. Agents write commit-scoped context
|
|
4
|
+
> here instead of modifying `.squad/` files in PRs.
|
|
5
|
+
>
|
|
6
|
+
> **Version:** 1.0
|
|
7
|
+
> **Backends:** `git-notes`, `orphan`
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
Squad state has two layers:
|
|
14
|
+
|
|
15
|
+
1. **Git notes layer** (this document) — thin, commit-scoped annotations that
|
|
16
|
+
attach agent context to commits without appearing in PRs or diffs.
|
|
17
|
+
2. **Permanent state layer** — long-lived decisions, routing rules, and archives
|
|
18
|
+
stored via the configured state backend (`git-notes` or `orphan` branch).
|
|
19
|
+
|
|
20
|
+
Agents write notes during their work rounds. Ralph promotes flagged notes to
|
|
21
|
+
permanent state after a PR merges.
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## Namespaces
|
|
26
|
+
|
|
27
|
+
Each agent writes to its own namespace to prevent conflicts:
|
|
28
|
+
|
|
29
|
+
| Namespace | Owner | Purpose |
|
|
30
|
+
|-----------|-------|---------|
|
|
31
|
+
| `refs/notes/squad/data` | Data | Architecture decisions, implementation choices |
|
|
32
|
+
| `refs/notes/squad/worf` | Worf | Security reviews, vulnerability assessments |
|
|
33
|
+
| `refs/notes/squad/seven` | Seven | Documentation quality, API contract decisions |
|
|
34
|
+
| `refs/notes/squad/ralph` | Ralph | Work-round progress, task-state annotations |
|
|
35
|
+
| `refs/notes/squad/q` | Q | Devil's advocate findings, risk assessments |
|
|
36
|
+
| `refs/notes/squad/research` | Any agent | Research notes that should survive branch deletion |
|
|
37
|
+
| `refs/notes/squad/review` | Any agent | Code review context (mirrors Gerrit's pattern) |
|
|
38
|
+
|
|
39
|
+
**Rule**: Only write to your own namespace. The shared namespaces
|
|
40
|
+
(`research`, `review`) use `append` — never `add`.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Note JSON Schema
|
|
45
|
+
|
|
46
|
+
All notes MUST be valid JSON. Minimum required fields:
|
|
47
|
+
|
|
48
|
+
```json
|
|
49
|
+
{
|
|
50
|
+
"agent": "Data",
|
|
51
|
+
"timestamp": "2026-03-23T14:00:00Z",
|
|
52
|
+
"type": "decision | research | review | progress | security",
|
|
53
|
+
"content": "..."
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Decision notes
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"agent": "Data",
|
|
62
|
+
"timestamp": "2026-03-23T14:00:00Z",
|
|
63
|
+
"type": "decision",
|
|
64
|
+
"decision": "Use JWT RS256 for auth middleware",
|
|
65
|
+
"reasoning": "Existing pattern in codebase — auth.go:47-89.",
|
|
66
|
+
"alternatives_considered": ["HS256", "session tokens"],
|
|
67
|
+
"confidence": "high",
|
|
68
|
+
"promote_to_permanent": true
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Set `"promote_to_permanent": true` to signal Ralph to copy this to
|
|
73
|
+
`decisions.md` after the PR merges.
|
|
74
|
+
|
|
75
|
+
### Research notes
|
|
76
|
+
|
|
77
|
+
```json
|
|
78
|
+
{
|
|
79
|
+
"agent": "Data",
|
|
80
|
+
"timestamp": "2026-03-23T14:00:00Z",
|
|
81
|
+
"type": "research",
|
|
82
|
+
"topic": "JWT vs session tokens",
|
|
83
|
+
"findings": {},
|
|
84
|
+
"effort_hours": 2.5,
|
|
85
|
+
"archive_on_close": true
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Set `"archive_on_close": true` to signal Ralph to archive this to
|
|
90
|
+
`state/research/` even if the PR is rejected.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Write Commands
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# Write a decision note on the current commit
|
|
98
|
+
git notes --ref=squad/{your-agent} add \
|
|
99
|
+
-m '{"agent":"{Agent}","timestamp":"...","type":"decision","decision":"..."}' \
|
|
100
|
+
HEAD
|
|
101
|
+
|
|
102
|
+
# Append to an existing note (multiple items on same commit)
|
|
103
|
+
git notes --ref=squad/{your-agent} append \
|
|
104
|
+
-m '{"agent":"{Agent}","timestamp":"...","type":"progress","content":"..."}' \
|
|
105
|
+
HEAD
|
|
106
|
+
|
|
107
|
+
# Read your note
|
|
108
|
+
git notes --ref=squad/{your-agent} show HEAD
|
|
109
|
+
|
|
110
|
+
# List all commits with notes in your namespace
|
|
111
|
+
git notes --ref=squad/{your-agent} list
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Or use the helper script:
|
|
115
|
+
|
|
116
|
+
```powershell
|
|
117
|
+
./scripts/notes/write-note.ps1 -Agent data -Type decision \
|
|
118
|
+
-Content '{"decision":"Use JWT","reasoning":"..."}' \
|
|
119
|
+
[-Commit HEAD] [-Promote] [-Archive]
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
---
|
|
123
|
+
|
|
124
|
+
## Fetch / Push
|
|
125
|
+
|
|
126
|
+
**Notes are NOT fetched or pushed by default.** Every clone needs setup.
|
|
127
|
+
|
|
128
|
+
### One-time setup
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
git config --add remote.origin.fetch 'refs/notes/*:refs/notes/*'
|
|
132
|
+
git fetch origin 'refs/notes/*:refs/notes/*'
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
Or use the helper:
|
|
136
|
+
|
|
137
|
+
```powershell
|
|
138
|
+
./scripts/notes/fetch.ps1 -Setup
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Every work round
|
|
142
|
+
|
|
143
|
+
1. **Start**: `git fetch origin 'refs/notes/*:refs/notes/*'`
|
|
144
|
+
2. **End**: `git push origin 'refs/notes/*:refs/notes/*'`
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## Conflict Handling
|
|
149
|
+
|
|
150
|
+
1. **Per-agent namespaces prevent 99% of conflicts.** Only one agent writes to
|
|
151
|
+
`refs/notes/squad/data`, so there are no write conflicts in normal use.
|
|
152
|
+
|
|
153
|
+
2. **Same agent, two machines:** First push wins. Losing machine should fetch
|
|
154
|
+
and append:
|
|
155
|
+
```bash
|
|
156
|
+
git fetch origin 'refs/notes/*:refs/notes/*'
|
|
157
|
+
git notes --ref=squad/{agent} append -m '{...}' HEAD
|
|
158
|
+
git push origin 'refs/notes/*:refs/notes/*'
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
3. **Shared namespaces** (`research`, `review`): Always use `git notes append`,
|
|
162
|
+
never `git notes add`.
|
|
163
|
+
|
|
164
|
+
4. **Push conflict recovery:**
|
|
165
|
+
```bash
|
|
166
|
+
git fetch origin 'refs/notes/*:refs/notes/*'
|
|
167
|
+
git notes merge refs/notes/remotes/origin/squad/{namespace}
|
|
168
|
+
git push origin 'refs/notes/*:refs/notes/*'
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
---
|
|
172
|
+
|
|
173
|
+
## When to Use Notes vs State Backend
|
|
174
|
+
|
|
175
|
+
| Use git notes | Use state backend |
|
|
176
|
+
|---------------|-------------------|
|
|
177
|
+
| Why THIS choice on THIS commit | Universal routing rules, conventions |
|
|
178
|
+
| Decisions scoped to a feature | Long-lived decisions for all future work |
|
|
179
|
+
| Research for a specific investigation | Research archives (promoted from notes) |
|
|
180
|
+
| Security sign-offs per commit | Agent history persisting across features |
|
|
181
|
+
| Agent-to-agent context for current feature | Team agreements and policies |
|
|
182
|
+
|
|
183
|
+
When in doubt: **notes first, promote to permanent state later.** Ralph handles
|
|
184
|
+
the promotion automatically when `promote_to_permanent` is set.
|
|
185
|
+
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
## Ralph Promotion Rules
|
|
189
|
+
|
|
190
|
+
**After PR merge:**
|
|
191
|
+
|
|
192
|
+
1. Fetch all notes from remote
|
|
193
|
+
2. Traverse commits reachable from the default branch that have notes
|
|
194
|
+
3. For each note with `"promote_to_permanent": true` → append to `decisions.md`
|
|
195
|
+
4. Push state
|
|
196
|
+
|
|
197
|
+
**After PR close/rejection:**
|
|
198
|
+
|
|
199
|
+
1. List notes in `squad/research` on the closed branch's commits
|
|
200
|
+
2. For each note with `"archive_on_close": true` → archive to `research/`
|
|
201
|
+
3. Push state
|
|
202
|
+
4. Notes on rejected commits are NOT promoted — this is the desired behavior
|
|
@@ -24,6 +24,59 @@
|
|
|
24
24
|
|
|
25
25
|
**Worktree awareness:** Use the `TEAM ROOT` provided in the spawn prompt to resolve all `.squad/` paths. If no TEAM ROOT is given, run `git rev-parse --show-toplevel` as fallback. Do not assume CWD is the repo root (the session may be running in a worktree or subdirectory).
|
|
26
26
|
|
|
27
|
+
**State backend awareness:** Check `STATE_BACKEND` from the spawn prompt. If it's `"orphan"` or `"git-notes"`, run the **State Leak Guard** before any other work.
|
|
28
|
+
|
|
29
|
+
### State Leak Guard (orphan/git-notes backends only)
|
|
30
|
+
|
|
31
|
+
Before logging or merging, check if any agent accidentally committed state files to the working branch:
|
|
32
|
+
|
|
33
|
+
```powershell
|
|
34
|
+
# Check if state files are staged or committed but shouldn't be
|
|
35
|
+
$stateFiles = @(
|
|
36
|
+
'.squad/decisions.md',
|
|
37
|
+
'.squad/decisions-archive.md'
|
|
38
|
+
)
|
|
39
|
+
$statePatterns = @(
|
|
40
|
+
'.squad/agents/*/history.md',
|
|
41
|
+
'.squad/agents/*/history-archive.md',
|
|
42
|
+
'.squad/log/*',
|
|
43
|
+
'.squad/orchestration-log/*',
|
|
44
|
+
'.squad/decisions/inbox/*'
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# 1. Check git status for accidentally staged state files
|
|
48
|
+
$dirty = git status --porcelain | Where-Object { $_.Length -gt 3 } | ForEach-Object {
|
|
49
|
+
$_.Substring(3) -replace '^.* -> ',''
|
|
50
|
+
} | Where-Object {
|
|
51
|
+
$f = $_
|
|
52
|
+
($f -in $stateFiles) -or ($statePatterns | Where-Object { $f -like $_ })
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if ($dirty) {
|
|
56
|
+
# Unstage any accidentally added state files
|
|
57
|
+
$dirty | ForEach-Object { git reset HEAD -- $_ 2>$null }
|
|
58
|
+
# Restore from HEAD (discard working tree changes for state files)
|
|
59
|
+
$dirty | ForEach-Object { git checkout HEAD -- $_ 2>$null }
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
# 2. Check if the most recent commit on this branch has state files
|
|
63
|
+
$lastCommitFiles = git diff-tree --no-commit-id --name-only -r HEAD 2>$null
|
|
64
|
+
$leakedInCommit = $lastCommitFiles | Where-Object {
|
|
65
|
+
$f = $_
|
|
66
|
+
($f -in $stateFiles) -or ($statePatterns | Where-Object { $f -like $_ })
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if ($leakedInCommit) {
|
|
70
|
+
# State files leaked into the last commit — amend to remove them
|
|
71
|
+
$leakedInCommit | ForEach-Object { git rm --cached -- $_ 2>$null }
|
|
72
|
+
git commit --amend --no-edit 2>$null
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
If any files were cleaned, log: `⚠️ State leak guard: removed {N} state file(s) from working branch.`
|
|
77
|
+
|
|
78
|
+
After the guard, proceed with normal Scribe work (but persist state via the configured backend, not the working branch).
|
|
79
|
+
|
|
27
80
|
After every substantial work session:
|
|
28
81
|
|
|
29
82
|
1. **Log the session** to `.squad/log/{timestamp}-{topic}.md`:
|
|
@@ -57,9 +110,50 @@ After every substantial work session:
|
|
|
57
110
|
```
|
|
58
111
|
|
|
59
112
|
5. **Commit `.squad/` changes:**
|
|
113
|
+
**Check `STATE_BACKEND` from spawn prompt.** This determines WHERE state gets committed.
|
|
114
|
+
|
|
60
115
|
**IMPORTANT — Windows compatibility:** Do NOT use `git -C {path}` (unreliable with Windows paths).
|
|
61
116
|
Do NOT embed newlines in `git commit -m` (backtick-n fails silently in PowerShell).
|
|
62
|
-
|
|
117
|
+
|
|
118
|
+
**If STATE_BACKEND is "orphan":**
|
|
119
|
+
State files must be committed to the `squad-state` orphan branch, NOT the working branch.
|
|
120
|
+
- Identify changed `.squad/` state files via `git status --porcelain` filtered to allowed paths.
|
|
121
|
+
- For each file, use git plumbing to write to the orphan branch:
|
|
122
|
+
```powershell
|
|
123
|
+
# Create a temporary worktree for the orphan branch
|
|
124
|
+
$orphanWt = Join-Path ([System.IO.Path]::GetTempPath()) "squad-state-$(Get-Random)"
|
|
125
|
+
git worktree add $orphanWt squad-state 2>$null
|
|
126
|
+
if ($LASTEXITCODE -ne 0) { git worktree add --orphan $orphanWt squad-state }
|
|
127
|
+
# Copy state files to orphan worktree
|
|
128
|
+
$filesToSync | ForEach-Object {
|
|
129
|
+
$dest = Join-Path $orphanWt $_
|
|
130
|
+
New-Item -ItemType Directory -Path (Split-Path $dest) -Force | Out-Null
|
|
131
|
+
Copy-Item $_ $dest -Force
|
|
132
|
+
}
|
|
133
|
+
# Commit in orphan worktree
|
|
134
|
+
Push-Location $orphanWt
|
|
135
|
+
git add .squad/
|
|
136
|
+
git diff --cached --quiet
|
|
137
|
+
if ($LASTEXITCODE -ne 0) {
|
|
138
|
+
$msgFile = [System.IO.Path]::GetTempFileName()
|
|
139
|
+
Set-Content -Path $msgFile -Value "docs(ai-team): $summary" -Encoding utf8
|
|
140
|
+
git commit -F $msgFile
|
|
141
|
+
Remove-Item $msgFile
|
|
142
|
+
git push origin squad-state
|
|
143
|
+
}
|
|
144
|
+
Pop-Location
|
|
145
|
+
git worktree remove $orphanWt --force
|
|
146
|
+
```
|
|
147
|
+
- After committing to orphan, reset working tree state files: `git checkout HEAD -- .squad/`
|
|
148
|
+
- ⚠️ NEVER commit `.squad/` state files to the working branch when using orphan backend.
|
|
149
|
+
|
|
150
|
+
**If STATE_BACKEND is "git-notes":**
|
|
151
|
+
State is already persisted in git notes refs by agents. Scribe only needs to:
|
|
152
|
+
- Push any locally created note refs: `git push origin 'refs/notes/squad/*'`
|
|
153
|
+
- Commit decisions.md (the merged canonical file) to the working branch as normal.
|
|
154
|
+
|
|
155
|
+
**If STATE_BACKEND is "worktree" (default):**
|
|
156
|
+
Commit to the working branch as normal:
|
|
63
157
|
- `cd` into the team root first.
|
|
64
158
|
- Stage only files Scribe actually modified in this session.
|
|
65
159
|
Use `git status --porcelain` to build an explicit file list filtered to allowed `.squad/` paths:
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env pwsh
|
|
2
|
+
# scripts/notes/fetch.ps1
|
|
3
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
# Fetch git notes from remote. Run on every Ralph-watch startup and before
|
|
5
|
+
# any agent reads or writes notes.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# ./scripts/notes/fetch.ps1 # fetch only
|
|
9
|
+
# ./scripts/notes/fetch.ps1 -Setup # first-time: add refspec + fetch
|
|
10
|
+
# ./scripts/notes/fetch.ps1 -Merge # fetch + merge (use after push conflict)
|
|
11
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
[CmdletBinding()]
|
|
14
|
+
param(
|
|
15
|
+
[string]$Remote = "origin",
|
|
16
|
+
[string]$RepoPath = ".",
|
|
17
|
+
[switch]$Setup,
|
|
18
|
+
[switch]$Merge,
|
|
19
|
+
[switch]$Quiet
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
function Log ([string]$msg, [string]$color = "White") {
|
|
23
|
+
if (-not $Quiet) { Write-Host "[notes/fetch] $msg" -ForegroundColor $color }
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
$repo = Resolve-Path $RepoPath
|
|
27
|
+
|
|
28
|
+
# ── One-time setup: add fetch refspec ──────────────────────────────────────
|
|
29
|
+
if ($Setup) {
|
|
30
|
+
$existing = git -C $repo config --get-all "remote.$Remote.fetch" 2>&1 |
|
|
31
|
+
Where-Object { $_ -match "refs/notes" }
|
|
32
|
+
if ($existing) {
|
|
33
|
+
Log "Notes refspec already configured." DarkGray
|
|
34
|
+
} else {
|
|
35
|
+
git -C $repo config --add "remote.$Remote.fetch" "refs/notes/*:refs/notes/*"
|
|
36
|
+
Log "Added notes refspec to remote.$Remote.fetch" Green
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
# ── Fetch notes ─────────────────────────────────────────────────────────────
|
|
41
|
+
Log "Fetching notes from $Remote..."
|
|
42
|
+
$output = git -C $repo fetch $Remote "refs/notes/*:refs/notes/*" 2>&1
|
|
43
|
+
if ($LASTEXITCODE -ne 0) {
|
|
44
|
+
Log "Fetch warning: $output" DarkYellow
|
|
45
|
+
} else {
|
|
46
|
+
Log "Notes fetched." Green
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# ── Merge notes if requested (after push conflict) ──────────────────────────
|
|
50
|
+
if ($Merge) {
|
|
51
|
+
# Abort any stale merge-in-progress state
|
|
52
|
+
$mergeLock = Join-Path $repo ".git/NOTES_MERGE_PARTIAL"
|
|
53
|
+
if (Test-Path $mergeLock) {
|
|
54
|
+
Log "Stale notes merge in progress — aborting before retry" DarkYellow
|
|
55
|
+
git -C $repo notes merge --abort 2>&1 | Out-Null
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
$namespaces = git -C $repo for-each-ref "refs/notes/squad/" --format="%(refname)" 2>&1
|
|
59
|
+
foreach ($ref in $namespaces) {
|
|
60
|
+
$ns = $ref -replace "refs/notes/", ""
|
|
61
|
+
$remoteRef = "refs/notes/remotes/$Remote/$ns"
|
|
62
|
+
$remoteExists = git -C $repo for-each-ref $remoteRef --format="%(refname)" 2>&1
|
|
63
|
+
if ($remoteExists) {
|
|
64
|
+
Log "Merging notes: $ns (cat_sort_uniq)"
|
|
65
|
+
git -C $repo notes --ref=$ns merge -s cat_sort_uniq $remoteRef 2>&1 | Out-Null
|
|
66
|
+
if ($LASTEXITCODE -ne 0) {
|
|
67
|
+
Log " Merge failed on $ns — aborting and continuing" Red
|
|
68
|
+
git -C $repo notes merge --abort 2>&1 | Out-Null
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
Log "Notes merge complete." Green
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
# ── Show available namespaces ────────────────────────────────────────────────
|
|
76
|
+
if (-not $Quiet) {
|
|
77
|
+
$refs = git -C $repo for-each-ref "refs/notes/squad/" --format="%(refname)" 2>&1
|
|
78
|
+
if ($refs) {
|
|
79
|
+
Log "Available namespaces:"
|
|
80
|
+
foreach ($r in $refs) {
|
|
81
|
+
$count = (git -C $repo notes --ref=($r -replace "refs/notes/","") list 2>&1 |
|
|
82
|
+
Where-Object { $_ -ne "" } | Measure-Object -Line).Lines
|
|
83
|
+
Log " $r ($count notes)" DarkGray
|
|
84
|
+
}
|
|
85
|
+
} else {
|
|
86
|
+
Log "No squad notes yet." DarkGray
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
#!/usr/bin/env pwsh
|
|
2
|
+
# scripts/notes/write-note.ps1
|
|
3
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
4
|
+
# Helper for agents to write notes without wrestling with JSON escaping.
|
|
5
|
+
# Validates namespace ownership, handles conflicts, pushes automatically.
|
|
6
|
+
#
|
|
7
|
+
# Usage:
|
|
8
|
+
# ./scripts/notes/write-note.ps1 -Agent data -Type decision \
|
|
9
|
+
# -Content '{"decision":"Use JWT","reasoning":"..."}' \
|
|
10
|
+
# [-Commit HEAD] [-Promote] [-Archive]
|
|
11
|
+
# ─────────────────────────────────────────────────────────────────────────────
|
|
12
|
+
|
|
13
|
+
[CmdletBinding()]
|
|
14
|
+
param(
|
|
15
|
+
[Parameter(Mandatory)][string]$Agent,
|
|
16
|
+
|
|
17
|
+
[Parameter(Mandatory)]
|
|
18
|
+
[ValidateSet("decision","research","review","security-review","progress",
|
|
19
|
+
"api-contract","risk-assessment","routing-discovery","counter-argument")]
|
|
20
|
+
[string]$Type,
|
|
21
|
+
|
|
22
|
+
[Parameter(Mandatory)]
|
|
23
|
+
[string]$Content, # JSON object with type-specific fields
|
|
24
|
+
|
|
25
|
+
[string]$Commit = "HEAD",
|
|
26
|
+
[string]$RepoPath = ".",
|
|
27
|
+
[string]$Remote = "origin",
|
|
28
|
+
[switch]$Promote, # set promote_to_permanent: true
|
|
29
|
+
[switch]$Archive, # set archive_on_close: true
|
|
30
|
+
[switch]$NoPush, # skip auto-push
|
|
31
|
+
[switch]$Quiet
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
function Log ([string]$msg, [string]$color = "White") {
|
|
35
|
+
if (-not $Quiet) { Write-Host "[notes/write] $msg" -ForegroundColor $color }
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
$repo = Resolve-Path $RepoPath
|
|
39
|
+
$namespace = "squad/$($Agent.ToLower())"
|
|
40
|
+
|
|
41
|
+
# ── Validate JSON content ────────────────────────────────────────────────────
|
|
42
|
+
try {
|
|
43
|
+
$parsed = $Content | ConvertFrom-Json -ErrorAction Stop
|
|
44
|
+
} catch {
|
|
45
|
+
Write-Error "Content must be valid JSON. Got: $Content"
|
|
46
|
+
exit 1
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
# ── Build full note object ────────────────────────────────────────────────────
|
|
50
|
+
$note = [ordered]@{
|
|
51
|
+
agent = (Get-Culture).TextInfo.ToTitleCase($Agent.ToLower())
|
|
52
|
+
timestamp = [System.DateTime]::UtcNow.ToString("yyyy-MM-ddTHH:mm:ssZ")
|
|
53
|
+
type = $Type
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# Merge content fields into note
|
|
57
|
+
$parsed.PSObject.Properties | ForEach-Object { $note[$_.Name] = $_.Value }
|
|
58
|
+
|
|
59
|
+
# Add flag fields
|
|
60
|
+
if ($Promote) { $note["promote_to_permanent"] = $true }
|
|
61
|
+
if ($Archive) { $note["archive_on_close"] = $true }
|
|
62
|
+
|
|
63
|
+
$noteJson = $note | ConvertTo-Json -Compress -Depth 10
|
|
64
|
+
|
|
65
|
+
# ── Fetch first to avoid conflicts ───────────────────────────────────────────
|
|
66
|
+
Log "Fetching notes before write..."
|
|
67
|
+
git -C $repo fetch $Remote "refs/notes/*:refs/notes/*" 2>&1 | Out-Null
|
|
68
|
+
|
|
69
|
+
# ── Check if note already exists on this commit ─────────────────────────────
|
|
70
|
+
$existing = git -C $repo notes --ref=$namespace show $Commit 2>&1
|
|
71
|
+
$useAppend = ($LASTEXITCODE -eq 0)
|
|
72
|
+
|
|
73
|
+
if ($useAppend) {
|
|
74
|
+
Log "Note exists on $Commit — appending" DarkYellow
|
|
75
|
+
git -C $repo notes --ref=$namespace append -m $noteJson $Commit
|
|
76
|
+
} else {
|
|
77
|
+
git -C $repo notes --ref=$namespace add -m $noteJson $Commit
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if ($LASTEXITCODE -ne 0) {
|
|
81
|
+
Write-Error "Failed to write note to refs/notes/$namespace on $Commit"
|
|
82
|
+
exit 1
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
Log "Note written to refs/notes/$namespace on $($Commit.Substring(0,[Math]::Min(8,$Commit.Length)))" Green
|
|
86
|
+
|
|
87
|
+
# ── Push with retry ──────────────────────────────────────────────────────────
|
|
88
|
+
if (-not $NoPush) {
|
|
89
|
+
$maxRetries = 5
|
|
90
|
+
$nsRef = "refs/notes/$namespace"
|
|
91
|
+
|
|
92
|
+
for ($i = 0; $i -lt $maxRetries; $i++) {
|
|
93
|
+
Log "Pushing notes (attempt $($i+1))..."
|
|
94
|
+
$pushOut = git -C $repo push $Remote "${nsRef}:${nsRef}" 2>&1
|
|
95
|
+
if ($LASTEXITCODE -eq 0) {
|
|
96
|
+
Log "Notes pushed successfully." Green
|
|
97
|
+
break
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
if ($pushOut -match "non-fast-forward|fetch first|rejected") {
|
|
101
|
+
Log "Push conflict — fetch-first retry..." DarkYellow
|
|
102
|
+
|
|
103
|
+
# Force-fetch: overwrite local ref with current remote state
|
|
104
|
+
git -C $repo fetch $Remote "${nsRef}:${nsRef}" 2>&1 | Out-Null
|
|
105
|
+
|
|
106
|
+
# Re-append our note on top of the now-current remote state
|
|
107
|
+
git -C $repo notes --ref=$namespace append -m $noteJson $Commit 2>&1 | Out-Null
|
|
108
|
+
|
|
109
|
+
$jitter = Get-Random -Minimum 0 -Maximum 1000
|
|
110
|
+
$sleep = [Math]::Pow(2, $i) + $jitter / 1000
|
|
111
|
+
Start-Sleep -Seconds $sleep
|
|
112
|
+
|
|
113
|
+
} else {
|
|
114
|
+
Log "Push error: $pushOut" Red
|
|
115
|
+
if ($i -eq $maxRetries - 1) {
|
|
116
|
+
Write-Warning "Failed after $maxRetries retries. Push manually: git push origin '${nsRef}:${nsRef}'"
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
# ── Show result ───────────────────────────────────────────────────────────────
|
|
123
|
+
if (-not $Quiet) {
|
|
124
|
+
Log "Note content:"
|
|
125
|
+
$note | ConvertTo-Json -Depth 5 | Write-Host -ForegroundColor DarkGray
|
|
126
|
+
}
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "cross-machine-coordination"
|
|
3
|
+
description: "Enables squad agents on different machines to share work via git-based task queuing"
|
|
4
|
+
domain: "orchestration"
|
|
5
|
+
confidence: "medium"
|
|
6
|
+
source: "manual"
|
|
7
|
+
---
|
|
8
|
+
|
|
1
9
|
# Skill: Cross-Machine Coordination Pattern
|
|
2
10
|
|
|
3
11
|
**Skill ID:** `cross-machine-coordination`
|
|
@@ -20,7 +20,7 @@ Init Mode activates when `.squad/team.md` does not exist, or exists but has zero
|
|
|
20
20
|
|
|
21
21
|
No team exists yet. Propose one — but **DO NOT create any files until the user confirms.**
|
|
22
22
|
|
|
23
|
-
1. **Identify the user.** Run `git config user.name` to learn who you're working with. Use their name in conversation (e.g., *"Hey
|
|
23
|
+
1. **Identify the user.** Run `git config user.name` to learn who you're working with. Use their name in conversation (e.g., *"Hey {user}, what are you building?"*). Store their name (NOT email) in `team.md` under Project Context. **Never read or store `git config user.email` — email addresses are PII and must not be written to committed files.**
|
|
24
24
|
2. Ask: *"What are you building? (language, stack, what it does)"*
|
|
25
25
|
3. **Cast the team.** Before proposing names, run the Casting & Persistent Naming algorithm (see that section):
|
|
26
26
|
- Determine team size (typically 4–5 + Scribe).
|
|
@@ -82,8 +82,8 @@ The `union` merge driver keeps all lines from both sides, which is correct for a
|
|
|
82
82
|
|
|
83
83
|
**Example flow:**
|
|
84
84
|
1. Coordinator detects no team.md → Init Mode
|
|
85
|
-
2. Runs `git config user.name` → "
|
|
86
|
-
3. Asks: *"Hey
|
|
85
|
+
2. Runs `git config user.name` → "{user}"
|
|
86
|
+
3. Asks: *"Hey {user}, what are you building?"*
|
|
87
87
|
4. User: *"TypeScript CLI tool with GitHub API integration"*
|
|
88
88
|
5. Coordinator runs casting algorithm → selects "The Usual Suspects" universe
|
|
89
89
|
6. Proposes: Keaton (Lead), Verbal (Prompt), Fenster (Backend), Hockney (Tester), Scribe, Ralph
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "model-selection"
|
|
3
|
+
description: "Determines which LLM model to use for each agent spawn"
|
|
4
|
+
domain: "orchestration"
|
|
5
|
+
confidence: "medium"
|
|
6
|
+
source: "extracted"
|
|
7
|
+
---
|
|
8
|
+
|
|
1
9
|
# Model Selection
|
|
2
10
|
|
|
3
11
|
> Determines which LLM model to use for each agent spawn.
|
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: "ralph-two-pass-scan"
|
|
3
|
+
description: "Cuts GitHub API calls by separating lightweight list scanning from full hydration"
|
|
4
|
+
domain: "work-monitoring"
|
|
5
|
+
confidence: "high"
|
|
6
|
+
source: "extracted"
|
|
7
|
+
---
|
|
8
|
+
|
|
1
9
|
# Skill: Ralph — Two-Pass Issue Scanning
|
|
2
10
|
**Confidence:** high
|
|
3
11
|
**Domain:** work-monitoring
|