@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,352 @@
1
+ /**
2
+ * squad preset — manage squad presets (curated agent collections)
3
+ *
4
+ * Presets are saved to SQUAD_HOME/presets/ (default: ~/.squad/presets/).
5
+ * Each preset is a directory with a preset.json manifest + agents/ charters.
6
+ *
7
+ * Subcommands:
8
+ * squad preset list — list available presets
9
+ * squad preset show <name> — show preset details
10
+ * squad preset apply <name> — install preset agents into current squad
11
+ * squad preset save <name> — save current project agents as a preset
12
+ * squad preset init — initialize presets directory in squad home
13
+ *
14
+ * Note: Presets capture agents only (charters). For full squad snapshots
15
+ * including casting state, skills, and routing rules — e.g. to share a
16
+ * configured squad or publish to an agent toolbox — use `squad export`.
17
+ *
18
+ * @module cli/commands/preset
19
+ */
20
+ import path from 'node:path';
21
+ import { execSync } from 'node:child_process';
22
+ import fs from 'node:fs';
23
+ import { ensureSquadHome, resolvePresetsDir } from '@bradygaster/squad-sdk/resolution';
24
+ import { listPresets, loadPreset, applyPreset, savePreset, seedBuiltinPresets } from '@bradygaster/squad-sdk/presets';
25
+ import { resolveSquad } from '@bradygaster/squad-sdk/resolution';
26
+ import { success, warn, info, BOLD, RESET, DIM } from '../core/output.js';
27
+ import { fatal } from '../core/errors.js';
28
+ /**
29
+ * Entry point for `squad preset` subcommands.
30
+ */
31
+ export async function runPreset(cwd, subcommand, args) {
32
+ switch (subcommand) {
33
+ case 'list':
34
+ await presetList();
35
+ break;
36
+ case 'show': {
37
+ const name = args[0];
38
+ if (!name) {
39
+ fatal('Usage: squad preset show <name>');
40
+ }
41
+ await presetShow(name);
42
+ break;
43
+ }
44
+ case 'apply': {
45
+ const name = args[0];
46
+ if (!name) {
47
+ fatal('Usage: squad preset apply <name> [--force]');
48
+ }
49
+ const force = args.includes('--force');
50
+ await presetApply(cwd, name, force);
51
+ break;
52
+ }
53
+ case 'init': {
54
+ const remote = args.includes('--remote');
55
+ await presetInit(remote);
56
+ break;
57
+ }
58
+ case 'save': {
59
+ const name = args[0];
60
+ if (!name) {
61
+ fatal('Usage: squad preset save <name> [--force] [--description "..."]');
62
+ }
63
+ const force = args.includes('--force');
64
+ const descIdx = args.indexOf('--description');
65
+ const description = descIdx >= 0 ? args[descIdx + 1] : undefined;
66
+ await presetSave(cwd, name, force, description);
67
+ break;
68
+ }
69
+ default:
70
+ fatal(`Unknown preset subcommand: ${subcommand}\n` +
71
+ `Usage:\n` +
72
+ ` squad preset list\n` +
73
+ ` squad preset show <name>\n` +
74
+ ` squad preset apply <name> [--force]\n` +
75
+ ` squad preset save <name>\n` +
76
+ ` squad preset init [--remote]`);
77
+ }
78
+ }
79
+ // ============================================================================
80
+ // Subcommand: init
81
+ // ============================================================================
82
+ async function presetInit(remote) {
83
+ if (remote) {
84
+ await presetInitRemote();
85
+ return;
86
+ }
87
+ const homeDir = ensureSquadHome();
88
+ const presetsDir = path.join(homeDir, 'presets');
89
+ const seeded = seedBuiltinPresets();
90
+ success('Presets directory initialized');
91
+ info(` Path: ${presetsDir}`);
92
+ if (seeded.length > 0) {
93
+ info(` Built-in presets installed: ${seeded.join(', ')}`);
94
+ }
95
+ info(` Run 'squad preset list' to see available presets.`);
96
+ console.log();
97
+ info(`${DIM}Tip: Run 'squad preset init --remote' to back your squad home`);
98
+ info(`with a private GitHub repo so presets roam across machines.${RESET}`);
99
+ }
100
+ async function presetInitRemote() {
101
+ // Check gh CLI is available
102
+ try {
103
+ execSync('gh --version', { stdio: 'pipe' });
104
+ }
105
+ catch {
106
+ fatal('GitHub CLI (gh) is required for --remote. Install it: https://cli.github.com');
107
+ }
108
+ // Check gh auth
109
+ try {
110
+ execSync('gh auth status', { stdio: 'pipe' });
111
+ }
112
+ catch {
113
+ fatal('Not logged in to GitHub CLI. Run: gh auth login');
114
+ }
115
+ const os = await import('node:os');
116
+ const envHome = process.env['SQUAD_HOME'];
117
+ const homeDir = envHome ? path.resolve(envHome) : path.join(os.homedir(), '.squad');
118
+ const repoName = 'squad-home';
119
+ // Get GitHub username
120
+ let ghUser;
121
+ try {
122
+ ghUser = execSync('gh api user --jq .login', { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
123
+ }
124
+ catch {
125
+ fatal('Could not determine GitHub username. Run: gh auth login');
126
+ return;
127
+ }
128
+ const repoFullName = `${ghUser}/${repoName}`;
129
+ // Check if SQUAD_HOME is already a git repo
130
+ if (fs.existsSync(path.join(homeDir, '.git'))) {
131
+ info(`Squad home is already a git repo: ${homeDir}`);
132
+ const seeded = seedBuiltinPresets();
133
+ if (seeded.length > 0) {
134
+ info(` Built-in presets installed: ${seeded.join(', ')}`);
135
+ }
136
+ success('Squad home ready — presets roam via git push/pull.');
137
+ return;
138
+ }
139
+ // If ~/.squad/ doesn't exist yet, try to clone existing remote repo
140
+ if (!fs.existsSync(homeDir)) {
141
+ let repoExists = false;
142
+ try {
143
+ execSync(`gh repo view ${repoFullName} --json name`, { stdio: 'pipe' });
144
+ repoExists = true;
145
+ }
146
+ catch {
147
+ repoExists = false;
148
+ }
149
+ if (repoExists) {
150
+ // Second machine — clone existing squad home
151
+ info(`Found existing repo ${repoFullName} — cloning...`);
152
+ try {
153
+ execSync(`gh repo clone ${repoFullName} "${homeDir}"`, { stdio: 'inherit' });
154
+ const seeded = seedBuiltinPresets();
155
+ if (seeded.length > 0) {
156
+ info(` New built-in presets added: ${seeded.join(', ')}`);
157
+ try {
158
+ execSync(`git -C "${homeDir}" add -A && git -C "${homeDir}" commit -m "seed built-in presets" --allow-empty`, { stdio: 'pipe' });
159
+ }
160
+ catch { /* nothing new to commit */ }
161
+ }
162
+ success(`Squad home cloned from ${repoFullName}`);
163
+ info(` Path: ${homeDir}`);
164
+ success('Presets synced — you\'re ready to go.');
165
+ return;
166
+ }
167
+ catch {
168
+ fatal(`Failed to clone ${repoFullName}. Check permissions.`);
169
+ }
170
+ }
171
+ // Repo doesn't exist — create fresh
172
+ info(`Creating private repo ${repoFullName}...`);
173
+ try {
174
+ fs.mkdirSync(homeDir, { recursive: true });
175
+ execSync(`git init`, { cwd: homeDir, stdio: 'pipe' });
176
+ execSync(`gh repo create ${repoName} --private --source "${homeDir}" --push --description "Squad home — presets and config"`, {
177
+ stdio: 'inherit',
178
+ });
179
+ }
180
+ catch {
181
+ fatal(`Failed to create repo. Try manually: gh repo create ${repoName} --private`);
182
+ }
183
+ }
184
+ else {
185
+ // ~/.squad/ exists but isn't a git repo — init and connect
186
+ info(`Initializing git in existing squad home: ${homeDir}`);
187
+ execSync(`git init`, { cwd: homeDir, stdio: 'pipe' });
188
+ let repoExists = false;
189
+ try {
190
+ execSync(`gh repo view ${repoFullName} --json name`, { stdio: 'pipe' });
191
+ repoExists = true;
192
+ }
193
+ catch {
194
+ repoExists = false;
195
+ }
196
+ if (!repoExists) {
197
+ info(`Creating private repo ${repoFullName}...`);
198
+ try {
199
+ execSync(`gh repo create ${repoName} --private --source "${homeDir}" --push --description "Squad home — presets and config"`, {
200
+ stdio: 'inherit',
201
+ });
202
+ }
203
+ catch {
204
+ fatal(`Failed to create repo ${repoFullName}.`);
205
+ }
206
+ }
207
+ else {
208
+ info(`Connecting to existing repo ${repoFullName}...`);
209
+ try {
210
+ execSync(`git remote add origin https://github.com/${repoFullName}.git`, { cwd: homeDir, stdio: 'pipe' });
211
+ execSync(`git pull origin main --allow-unrelated-histories`, { cwd: homeDir, stdio: 'pipe' });
212
+ }
213
+ catch {
214
+ warn('Could not pull from remote. You may need to resolve manually.');
215
+ }
216
+ }
217
+ }
218
+ // Seed built-in presets and push
219
+ const seeded = seedBuiltinPresets();
220
+ try {
221
+ execSync(`git -C "${homeDir}" add -A`, { stdio: 'pipe' });
222
+ execSync(`git -C "${homeDir}" commit -m "Initialize squad home with presets"`, { stdio: 'pipe' });
223
+ execSync(`git -C "${homeDir}" push -u origin main`, { stdio: 'pipe' });
224
+ }
225
+ catch {
226
+ // May fail if nothing to commit or push
227
+ }
228
+ success(`Squad home initialized with private repo: ${repoFullName}`);
229
+ info(` Path: ${homeDir}`);
230
+ if (seeded.length > 0) {
231
+ info(` Built-in presets installed: ${seeded.join(', ')}`);
232
+ }
233
+ console.log();
234
+ info(`On another machine, run the same command to sync your presets:`);
235
+ info(` ${BOLD}squad preset init --remote${RESET}`);
236
+ }
237
+ // ============================================================================
238
+ // Subcommand: list
239
+ // ============================================================================
240
+ async function presetList() {
241
+ const presetsDir = resolvePresetsDir();
242
+ if (!presetsDir) {
243
+ info('No presets directory found.');
244
+ info(' Run `squad preset init --remote` to set up with a GitHub repo (recommended).');
245
+ info(' Or `squad preset init` for local-only setup.');
246
+ return;
247
+ }
248
+ const presets = listPresets();
249
+ if (presets.length === 0) {
250
+ info(`Presets directory exists at ${presetsDir} but contains no presets.`);
251
+ info(' Create a preset directory with a preset.json manifest.');
252
+ return;
253
+ }
254
+ console.log(`\n${BOLD}Available Presets${RESET} (${presets.length}):\n`);
255
+ const maxNameLen = Math.max(...presets.map(p => p.name.length), 4);
256
+ console.log(` ${'Name'.padEnd(maxNameLen)} ` +
257
+ `${'Agents'} ` +
258
+ `Description`);
259
+ console.log(` ${'─'.repeat(maxNameLen)} ` +
260
+ `${'─'.repeat(6)} ` +
261
+ `${'─'.repeat(40)}`);
262
+ for (const preset of presets) {
263
+ console.log(` ${preset.name.padEnd(maxNameLen)} ` +
264
+ `${String(preset.agents.length).padEnd(6)} ` +
265
+ `${DIM}${preset.description}${RESET}`);
266
+ }
267
+ console.log();
268
+ }
269
+ // ============================================================================
270
+ // Subcommand: show
271
+ // ============================================================================
272
+ async function presetShow(name) {
273
+ const preset = loadPreset(name);
274
+ if (!preset) {
275
+ fatal(`Preset '${name}' not found. Run 'squad preset list' to see available presets.`);
276
+ }
277
+ console.log(`\n${BOLD}${preset.name}${RESET} v${preset.version}`);
278
+ console.log(` ${preset.description}`);
279
+ if (preset.author)
280
+ console.log(` Author: ${preset.author}`);
281
+ if (preset.tags?.length)
282
+ console.log(` Tags: ${preset.tags.join(', ')}`);
283
+ console.log(`\n ${BOLD}Agents${RESET} (${preset.agents.length}):`);
284
+ for (const agent of preset.agents) {
285
+ console.log(` • ${BOLD}${agent.name}${RESET} (${agent.role})${agent.description ? ` — ${DIM}${agent.description}${RESET}` : ''}`);
286
+ }
287
+ console.log();
288
+ }
289
+ // ============================================================================
290
+ // Subcommand: apply
291
+ // ============================================================================
292
+ async function presetApply(cwd, name, force) {
293
+ // Find target squad directory
294
+ const squadDir = resolveSquad(cwd);
295
+ if (!squadDir) {
296
+ fatal('No .squad/ directory found. Run `squad init` first, or use from a repo with a squad.');
297
+ }
298
+ const targetAgentsDir = path.join(squadDir, 'agents');
299
+ const results = applyPreset(name, targetAgentsDir, { force });
300
+ if (results.length === 1 && results[0].status === 'error' && results[0].agent === name) {
301
+ fatal(results[0].reason ?? `Failed to apply preset '${name}'`);
302
+ }
303
+ let installed = 0;
304
+ let skipped = 0;
305
+ let errors = 0;
306
+ for (const result of results) {
307
+ switch (result.status) {
308
+ case 'installed':
309
+ success(` ${result.agent}`);
310
+ installed++;
311
+ break;
312
+ case 'skipped':
313
+ warn(` ${result.agent} — ${result.reason}`);
314
+ skipped++;
315
+ break;
316
+ case 'error':
317
+ console.error(` ✗ ${result.agent} — ${result.reason}`);
318
+ errors++;
319
+ break;
320
+ }
321
+ }
322
+ console.log();
323
+ if (installed > 0)
324
+ success(`Applied preset '${name}': ${installed} agents installed`);
325
+ if (skipped > 0)
326
+ info(` ${skipped} agents skipped (already exist)`);
327
+ if (errors > 0)
328
+ warn(` ${errors} agents had errors`);
329
+ }
330
+ // ============================================================================
331
+ // Subcommand: save
332
+ // ============================================================================
333
+ async function presetSave(cwd, name, force, description) {
334
+ const squadDir = resolveSquad(cwd);
335
+ if (!squadDir) {
336
+ fatal('No .squad/ directory found. Initialize a squad first with `squad init`.');
337
+ }
338
+ try {
339
+ const destDir = savePreset(name, squadDir, { force, description });
340
+ success(`Preset '${name}' saved`);
341
+ info(` Location: ${destDir}`);
342
+ info(` Use it in any project: squad preset apply ${name}`);
343
+ console.log();
344
+ info(`${DIM}Tip: Presets save agents only (charters). For a full squad snapshot`);
345
+ info(`including casting state, skills, and routing rules — e.g. to share`);
346
+ info(`a configured squad or publish to an agent toolbox — use 'squad export'.${RESET}`);
347
+ }
348
+ catch (err) {
349
+ fatal(String(err));
350
+ }
351
+ }
352
+ //# sourceMappingURL=preset.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"preset.js","sourceRoot":"","sources":["../../../src/cli/commands/preset.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ /**
2
+ * Squad Sync — synchronizes squad-state branches and git notes with a remote.
3
+ *
4
+ * Used directly (`squad sync`) or invoked by git hooks (pre-push, post-merge, post-rewrite).
5
+ * Handles both orphan and two-layer backends transparently.
6
+ *
7
+ * Design:
8
+ * - Fetches remote squad-state branch(es) and fast-forwards local refs
9
+ * - Pushes local squad-state branch(es) to remote
10
+ * - For two-layer, also syncs refs/notes/squad* namespaces
11
+ * - Uses fast-forward-only semantics to avoid data loss on divergence
12
+ * - Recursion guard via SQUAD_SYNC_ACTIVE env var
13
+ */
14
+ export interface SyncOptions {
15
+ direction: 'push' | 'pull' | 'both';
16
+ remote?: string;
17
+ cwd?: string;
18
+ quiet?: boolean;
19
+ }
20
+ /**
21
+ * Main sync entrypoint.
22
+ */
23
+ export declare function runSync(options: SyncOptions): Promise<void>;
24
+ //# sourceMappingURL=sync.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sync.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/sync.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AASH,MAAM,WAAW,WAAW;IAC1B,SAAS,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,CAAC;IACpC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;AA2MD;;GAEG;AACH,wBAAsB,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,CA+BjE"}
@@ -0,0 +1,257 @@
1
+ /**
2
+ * Squad Sync — synchronizes squad-state branches and git notes with a remote.
3
+ *
4
+ * Used directly (`squad sync`) or invoked by git hooks (pre-push, post-merge, post-rewrite).
5
+ * Handles both orphan and two-layer backends transparently.
6
+ *
7
+ * Design:
8
+ * - Fetches remote squad-state branch(es) and fast-forwards local refs
9
+ * - Pushes local squad-state branch(es) to remote
10
+ * - For two-layer, also syncs refs/notes/squad* namespaces
11
+ * - Uses fast-forward-only semantics to avoid data loss on divergence
12
+ * - Recursion guard via SQUAD_SYNC_ACTIVE env var
13
+ */
14
+ import { execFileSync } from 'node:child_process';
15
+ import path from 'node:path';
16
+ import fs from 'node:fs';
17
+ const SQUAD_SYNC_ENV = 'SQUAD_SYNC_ACTIVE';
18
+ const STATE_BRANCH_PREFIX = 'squad-state';
19
+ /**
20
+ * Detect the configured state backend from .squad/config.json
21
+ */
22
+ function detectBackend(cwd) {
23
+ try {
24
+ const configPath = path.join(cwd, '.squad', 'config.json');
25
+ const raw = fs.readFileSync(configPath, 'utf-8');
26
+ const config = JSON.parse(raw);
27
+ return config.stateBackend || null;
28
+ }
29
+ catch {
30
+ return null;
31
+ }
32
+ }
33
+ /**
34
+ * Get git repo root for the given working directory.
35
+ */
36
+ function getRepoRoot(cwd) {
37
+ return execFileSync('git', ['rev-parse', '--show-toplevel'], {
38
+ cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
39
+ }).trim();
40
+ }
41
+ /**
42
+ * Discover all local squad-state branches (supports subsquads).
43
+ * Uses strict prefix matching: squad-state or squad-state/<name>
44
+ */
45
+ function discoverStateBranches(cwd) {
46
+ try {
47
+ const output = execFileSync('git', ['for-each-ref', '--format=%(refname:short)', `refs/heads/${STATE_BRANCH_PREFIX}`], {
48
+ cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
49
+ }).trim();
50
+ const exact = output ? output.split('\n').filter(Boolean) : [];
51
+ // Also find squad-state/* (subsquad branches)
52
+ const subOutput = execFileSync('git', ['for-each-ref', '--format=%(refname:short)', `refs/heads/${STATE_BRANCH_PREFIX}/`], {
53
+ cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
54
+ }).trim();
55
+ const subs = subOutput ? subOutput.split('\n').filter(Boolean) : [];
56
+ return [...exact, ...subs];
57
+ }
58
+ catch {
59
+ return [];
60
+ }
61
+ }
62
+ /**
63
+ * Discover remote squad-state branches.
64
+ */
65
+ function discoverRemoteStateBranches(cwd, remote) {
66
+ try {
67
+ const output = execFileSync('git', ['for-each-ref', '--format=%(refname:short)', `refs/remotes/${remote}/${STATE_BRANCH_PREFIX}`], {
68
+ cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
69
+ }).trim();
70
+ const exact = output ? output.split('\n').filter(Boolean) : [];
71
+ const subOutput = execFileSync('git', ['for-each-ref', '--format=%(refname:short)', `refs/remotes/${remote}/${STATE_BRANCH_PREFIX}/`], {
72
+ cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
73
+ }).trim();
74
+ const subs = subOutput ? subOutput.split('\n').filter(Boolean) : [];
75
+ return [...exact, ...subs];
76
+ }
77
+ catch {
78
+ return [];
79
+ }
80
+ }
81
+ /**
82
+ * Resolve the default remote for the current branch (or fallback to 'origin').
83
+ */
84
+ function resolveRemote(cwd) {
85
+ try {
86
+ const branch = execFileSync('git', ['symbolic-ref', '--short', 'HEAD'], {
87
+ cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
88
+ }).trim();
89
+ const remote = execFileSync('git', ['config', `branch.${branch}.remote`], {
90
+ cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
91
+ }).trim();
92
+ return remote || 'origin';
93
+ }
94
+ catch {
95
+ return 'origin';
96
+ }
97
+ }
98
+ /**
99
+ * Pull: fetch remote state branches and fast-forward local refs.
100
+ */
101
+ function syncPull(cwd, remote, backend, quiet) {
102
+ // Fetch squad-state refs from remote
103
+ try {
104
+ execFileSync('git', ['fetch', remote, `+refs/heads/${STATE_BRANCH_PREFIX}:refs/remotes/${remote}/${STATE_BRANCH_PREFIX}`, `+refs/heads/${STATE_BRANCH_PREFIX}/*:refs/remotes/${remote}/${STATE_BRANCH_PREFIX}/*`], {
105
+ cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
106
+ });
107
+ }
108
+ catch {
109
+ // Remote may not have these refs yet — not an error
110
+ if (!quiet)
111
+ console.log(' No remote squad-state refs found (first push will create them).');
112
+ return;
113
+ }
114
+ // Fast-forward local branches from remote-tracking refs
115
+ const remoteBranches = discoverRemoteStateBranches(cwd, remote);
116
+ for (const remoteBranch of remoteBranches) {
117
+ // remoteBranch is like "origin/squad-state" — extract local name
118
+ const localName = remoteBranch.replace(`${remote}/`, '');
119
+ const localRef = `refs/heads/${localName}`;
120
+ const remoteRef = `refs/remotes/${remoteBranch}`;
121
+ try {
122
+ // Check if local branch exists
123
+ execFileSync('git', ['rev-parse', '--verify', localRef], {
124
+ cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
125
+ });
126
+ // Local exists — try fast-forward
127
+ const localSha = execFileSync('git', ['rev-parse', localRef], {
128
+ cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
129
+ }).trim();
130
+ const remoteSha = execFileSync('git', ['rev-parse', remoteRef], {
131
+ cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
132
+ }).trim();
133
+ if (localSha === remoteSha)
134
+ continue; // Already up-to-date
135
+ // Check if fast-forward is possible (local is ancestor of remote)
136
+ try {
137
+ execFileSync('git', ['merge-base', '--is-ancestor', localSha, remoteSha], {
138
+ cwd, stdio: ['pipe', 'pipe', 'pipe'],
139
+ });
140
+ // Fast-forward: update local ref
141
+ execFileSync('git', ['update-ref', localRef, remoteSha], {
142
+ cwd, stdio: ['pipe', 'pipe', 'pipe'],
143
+ });
144
+ if (!quiet)
145
+ console.log(` ✓ ${localName}: fast-forwarded`);
146
+ }
147
+ catch {
148
+ // Diverged — cannot fast-forward
149
+ if (!quiet)
150
+ console.log(` ⚠ ${localName}: diverged from remote (manual merge needed)`);
151
+ }
152
+ }
153
+ catch {
154
+ // Local branch doesn't exist — create it tracking the remote
155
+ const remoteSha = execFileSync('git', ['rev-parse', remoteRef], {
156
+ cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
157
+ }).trim();
158
+ execFileSync('git', ['update-ref', localRef, remoteSha], {
159
+ cwd, stdio: ['pipe', 'pipe', 'pipe'],
160
+ });
161
+ if (!quiet)
162
+ console.log(` ✓ ${localName}: created from remote`);
163
+ }
164
+ }
165
+ // For two-layer: also fetch notes
166
+ if (backend === 'two-layer') {
167
+ try {
168
+ execFileSync('git', ['fetch', remote, '+refs/notes/squad*:refs/notes/squad*'], {
169
+ cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
170
+ });
171
+ if (!quiet)
172
+ console.log(' ✓ notes synced');
173
+ }
174
+ catch {
175
+ // Notes may not exist yet
176
+ }
177
+ }
178
+ }
179
+ /**
180
+ * Push: push local state branches to remote.
181
+ */
182
+ function syncPush(cwd, remote, backend, quiet) {
183
+ const branches = discoverStateBranches(cwd);
184
+ if (branches.length === 0) {
185
+ if (!quiet)
186
+ console.log(' No local squad-state branches to push.');
187
+ return;
188
+ }
189
+ // Build refspecs for all state branches
190
+ const refspecs = branches.map(b => `refs/heads/${b}:refs/heads/${b}`);
191
+ try {
192
+ execFileSync('git', ['push', '--no-verify', remote, ...refspecs], {
193
+ cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
194
+ });
195
+ if (!quiet)
196
+ console.log(` ✓ pushed: ${branches.join(', ')}`);
197
+ }
198
+ catch (err) {
199
+ const msg = err instanceof Error ? err.stderr || err.message : String(err);
200
+ if (msg.includes('non-fast-forward')) {
201
+ if (!quiet)
202
+ console.log(` ⚠ push rejected (non-fast-forward). Run 'squad sync --pull' first.`);
203
+ }
204
+ else {
205
+ if (!quiet)
206
+ console.log(` ⚠ push failed: ${msg}`);
207
+ }
208
+ }
209
+ // For two-layer: also push notes
210
+ if (backend === 'two-layer') {
211
+ try {
212
+ execFileSync('git', ['push', '--no-verify', remote, 'refs/notes/squad*:refs/notes/squad*'], {
213
+ cwd, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'],
214
+ });
215
+ if (!quiet)
216
+ console.log(' ✓ notes pushed');
217
+ }
218
+ catch {
219
+ // Notes may not exist yet — not an error
220
+ }
221
+ }
222
+ }
223
+ /**
224
+ * Main sync entrypoint.
225
+ */
226
+ export async function runSync(options) {
227
+ // Recursion guard — prevent re-entry when our push triggers pre-push
228
+ if (process.env[SQUAD_SYNC_ENV]) {
229
+ return;
230
+ }
231
+ process.env[SQUAD_SYNC_ENV] = '1';
232
+ try {
233
+ const cwd = options.cwd || process.cwd();
234
+ const repoRoot = getRepoRoot(cwd);
235
+ const remote = options.remote || resolveRemote(repoRoot);
236
+ const backend = detectBackend(repoRoot);
237
+ const quiet = options.quiet ?? false;
238
+ // Skip sync for backends that don't need it
239
+ if (backend === 'local' || backend === 'external' || backend === null) {
240
+ if (!quiet)
241
+ console.log(`squad sync: backend is '${backend || 'local'}' — no remote sync needed.`);
242
+ return;
243
+ }
244
+ if (!quiet)
245
+ console.log(`squad sync: ${options.direction} (remote: ${remote}, backend: ${backend || 'orphan'})`);
246
+ if (options.direction === 'pull' || options.direction === 'both') {
247
+ syncPull(repoRoot, remote, backend, quiet);
248
+ }
249
+ if (options.direction === 'push' || options.direction === 'both') {
250
+ syncPush(repoRoot, remote, backend, quiet);
251
+ }
252
+ }
253
+ finally {
254
+ delete process.env[SQUAD_SYNC_ENV];
255
+ }
256
+ }
257
+ //# sourceMappingURL=sync.js.map