@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.
Files changed (68) hide show
  1. package/dist/cli/commands/install-hooks.d.ts +26 -0
  2. package/dist/cli/commands/install-hooks.d.ts.map +1 -0
  3. package/dist/cli/commands/install-hooks.js +244 -0
  4. package/dist/cli/commands/install-hooks.js.map +1 -0
  5. package/dist/cli/commands/migrate-backend.d.ts +16 -0
  6. package/dist/cli/commands/migrate-backend.d.ts.map +1 -0
  7. package/dist/cli/commands/migrate-backend.js +87 -0
  8. package/dist/cli/commands/migrate-backend.js.map +1 -0
  9. package/dist/cli/commands/preset.d.ts +24 -0
  10. package/dist/cli/commands/preset.d.ts.map +1 -0
  11. package/dist/cli/commands/preset.js +352 -0
  12. package/dist/cli/commands/preset.js.map +1 -0
  13. package/dist/cli/commands/sync.d.ts +24 -0
  14. package/dist/cli/commands/sync.d.ts.map +1 -0
  15. package/dist/cli/commands/sync.js +257 -0
  16. package/dist/cli/commands/sync.js.map +1 -0
  17. package/dist/cli/commands/watch/capabilities/board.d.ts.map +1 -1
  18. package/dist/cli/commands/watch/capabilities/board.js +7 -10
  19. package/dist/cli/commands/watch/capabilities/board.js.map +1 -1
  20. package/dist/cli/commands/watch/capabilities/fleet-dispatch.d.ts.map +1 -1
  21. package/dist/cli/commands/watch/capabilities/fleet-dispatch.js +7 -13
  22. package/dist/cli/commands/watch/capabilities/fleet-dispatch.js.map +1 -1
  23. package/dist/cli/commands/watch/capabilities/self-pull.d.ts.map +1 -1
  24. package/dist/cli/commands/watch/capabilities/self-pull.js +9 -10
  25. package/dist/cli/commands/watch/capabilities/self-pull.js.map +1 -1
  26. package/dist/cli/commands/watch/capabilities/two-pass.d.ts.map +1 -1
  27. package/dist/cli/commands/watch/capabilities/two-pass.js +5 -7
  28. package/dist/cli/commands/watch/capabilities/two-pass.js.map +1 -1
  29. package/dist/cli/commands/watch/config.d.ts +4 -1
  30. package/dist/cli/commands/watch/config.d.ts.map +1 -1
  31. package/dist/cli/commands/watch/config.js +2 -1
  32. package/dist/cli/commands/watch/config.js.map +1 -1
  33. package/dist/cli/commands/watch/types.d.ts +1 -5
  34. package/dist/cli/commands/watch/types.d.ts.map +1 -1
  35. package/dist/cli/core/gh-cli.d.ts.map +1 -1
  36. package/dist/cli/core/gh-cli.js +6 -7
  37. package/dist/cli/core/gh-cli.js.map +1 -1
  38. package/dist/cli/core/init.d.ts +2 -10
  39. package/dist/cli/core/init.d.ts.map +1 -1
  40. package/dist/cli/core/init.js +63 -56
  41. package/dist/cli/core/init.js.map +1 -1
  42. package/dist/cli/index.d.ts +0 -2
  43. package/dist/cli/index.d.ts.map +1 -1
  44. package/dist/cli/index.js +0 -2
  45. package/dist/cli/index.js.map +1 -1
  46. package/dist/cli-entry.js +71 -10
  47. package/dist/cli-entry.js.map +1 -1
  48. package/package.json +4 -3
  49. package/templates/notes-protocol.md +202 -0
  50. package/templates/scribe-charter.md +95 -1
  51. package/templates/scripts/notes/fetch.ps1 +88 -0
  52. package/templates/scripts/notes/write-note.ps1 +126 -0
  53. package/templates/skills/cross-machine-coordination/SKILL.md +8 -0
  54. package/templates/skills/init-mode/SKILL.md +3 -3
  55. package/templates/skills/model-selection/SKILL.md +8 -0
  56. package/templates/skills/nap/SKILL.md +8 -0
  57. package/templates/skills/personal-squad/SKILL.md +8 -0
  58. package/templates/skills/ralph-two-pass-scan/SKILL.md +8 -0
  59. package/templates/skills/release-process/SKILL.md +91 -5
  60. package/templates/squad.agent.md.template +137 -14
  61. package/dist/cli/commands/skill.d.ts +0 -31
  62. package/dist/cli/commands/skill.d.ts.map +0 -1
  63. package/dist/cli/commands/skill.js +0 -496
  64. package/dist/cli/commands/skill.js.map +0 -1
  65. package/dist/cli/commands/watch/agent-spawn.d.ts +0 -62
  66. package/dist/cli/commands/watch/agent-spawn.d.ts.map +0 -1
  67. package/dist/cli/commands/watch/agent-spawn.js +0 -127
  68. 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
- Instead:
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 Brady, 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.**
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` → "Brady"
86
- 3. Asks: *"Hey Brady, what are you building?"*
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: "nap"
3
+ description: "Context hygiene — compress, prune, archive .squad/ state"
4
+ domain: "maintenance"
5
+ confidence: "medium"
6
+ source: "extracted"
7
+ ---
8
+
1
9
  # Skill: nap
2
10
 
3
11
  > Context hygiene — compress, prune, archive .squad/ state
@@ -1,3 +1,11 @@
1
+ ---
2
+ name: "personal-squad"
3
+ description: "User-level AI agents that travel with you across projects"
4
+ domain: "configuration"
5
+ confidence: "medium"
6
+ source: "manual"
7
+ ---
8
+
1
9
  # Personal Squad — Skill Document
2
10
 
3
11
  ## What is a Personal Squad?
@@ -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