@jaimevalasek/aioson 1.4.0 → 1.5.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 (199) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/LICENSE +661 -21
  3. package/README.md +3 -1
  4. package/docs/en/squad-dashboard.md +372 -0
  5. package/docs/openclaw-bridge.md +308 -0
  6. package/docs/pt/agentes.md +124 -10
  7. package/docs/pt/cenarios.md +46 -2
  8. package/docs/pt/comandos-cli.md +60 -1
  9. package/docs/pt/inicio-rapido.md +18 -2
  10. package/docs/pt/squad-dashboard.md +373 -0
  11. package/docs/testing/genome-2.0-matrix.md +5 -5
  12. package/docs/testing/genome-2.0-rollout.md +9 -9
  13. package/package.json +2 -2
  14. package/src/backup-local.js +74 -0
  15. package/src/cli.js +98 -0
  16. package/src/commands/backup-local-cmd.js +25 -0
  17. package/src/commands/runtime.js +242 -0
  18. package/src/commands/setup-context.js +7 -2
  19. package/src/commands/squad-daemon.js +209 -0
  20. package/src/commands/squad-dashboard.js +39 -0
  21. package/src/commands/squad-deploy.js +64 -0
  22. package/src/commands/squad-doctor.js +52 -0
  23. package/src/commands/squad-mcp.js +270 -0
  24. package/src/commands/squad-processes.js +56 -0
  25. package/src/commands/squad-recovery.js +42 -0
  26. package/src/commands/squad-roi.js +291 -0
  27. package/src/commands/squad-score.js +250 -0
  28. package/src/commands/squad-status.js +37 -1
  29. package/src/commands/squad-validate.js +62 -1
  30. package/src/commands/squad-webhook.js +160 -0
  31. package/src/commands/squad-worker.js +191 -0
  32. package/src/commands/squad-worktrees.js +75 -0
  33. package/src/commands/web-map.js +70 -0
  34. package/src/commands/web-scrape.js +71 -0
  35. package/src/constants.js +8 -0
  36. package/src/context-writer.js +45 -1
  37. package/src/i18n/messages/en.js +127 -1
  38. package/src/i18n/messages/es.js +117 -0
  39. package/src/i18n/messages/fr.js +117 -0
  40. package/src/i18n/messages/pt-BR.js +126 -1
  41. package/src/lib/webhook-server.js +328 -0
  42. package/src/mcp-connectors/registry.js +602 -0
  43. package/src/runtime-store.js +259 -2
  44. package/src/squad/external-session.js +180 -0
  45. package/src/squad/inter-squad.js +74 -0
  46. package/src/squad/recovery-context.js +201 -0
  47. package/src/squad/worktree-manager.js +114 -0
  48. package/src/squad-daemon.js +490 -0
  49. package/src/squad-dashboard/api.js +223 -0
  50. package/src/squad-dashboard/attachment-handler.js +93 -0
  51. package/src/squad-dashboard/context-monitor.js +157 -0
  52. package/src/squad-dashboard/execution-logs.js +115 -0
  53. package/src/squad-dashboard/hunk-review.js +209 -0
  54. package/src/squad-dashboard/metrics.js +133 -0
  55. package/src/squad-dashboard/process-monitor.js +125 -0
  56. package/src/squad-dashboard/renderer.js +858 -0
  57. package/src/squad-dashboard/server.js +232 -0
  58. package/src/squad-dashboard/styles.js +525 -0
  59. package/src/squad-dashboard/token-tracker.js +99 -0
  60. package/src/web.js +284 -0
  61. package/src/worker-runner.js +339 -0
  62. package/template/.aioson/agents/analyst.md +4 -0
  63. package/template/.aioson/agents/architect.md +4 -0
  64. package/template/.aioson/agents/dev.md +120 -11
  65. package/template/.aioson/agents/deyvin.md +8 -0
  66. package/template/.aioson/agents/neo.md +152 -0
  67. package/template/.aioson/agents/orache.md +17 -0
  68. package/template/.aioson/agents/orchestrator.md +26 -0
  69. package/template/.aioson/agents/product.md +60 -12
  70. package/template/.aioson/agents/qa.md +1 -0
  71. package/template/.aioson/agents/setup.md +63 -19
  72. package/template/.aioson/agents/sheldon.md +603 -0
  73. package/template/.aioson/agents/squad.md +191 -0
  74. package/template/.aioson/agents/tester.md +254 -0
  75. package/template/.aioson/agents/ux-ui.md +12 -0
  76. package/template/.aioson/config.md +6 -0
  77. package/template/.aioson/locales/en/agents/analyst.md +8 -0
  78. package/template/.aioson/locales/en/agents/architect.md +8 -0
  79. package/template/.aioson/locales/en/agents/dev.md +66 -7
  80. package/template/.aioson/locales/en/agents/deyvin.md +8 -0
  81. package/template/.aioson/locales/en/agents/neo.md +8 -0
  82. package/template/.aioson/locales/en/agents/orchestrator.md +26 -0
  83. package/template/.aioson/locales/en/agents/qa.md +49 -0
  84. package/template/.aioson/locales/en/agents/setup.md +2 -1
  85. package/template/.aioson/locales/en/agents/sheldon.md +340 -0
  86. package/template/.aioson/locales/en/agents/ux-ui.md +8 -0
  87. package/template/.aioson/locales/es/agents/analyst.md +8 -0
  88. package/template/.aioson/locales/es/agents/architect.md +8 -0
  89. package/template/.aioson/locales/es/agents/dev.md +66 -7
  90. package/template/.aioson/locales/es/agents/deyvin.md +8 -0
  91. package/template/.aioson/locales/es/agents/neo.md +48 -0
  92. package/template/.aioson/locales/es/agents/orchestrator.md +26 -0
  93. package/template/.aioson/locales/es/agents/qa.md +26 -0
  94. package/template/.aioson/locales/es/agents/setup.md +2 -1
  95. package/template/.aioson/locales/es/agents/sheldon.md +192 -0
  96. package/template/.aioson/locales/es/agents/squad.md +63 -0
  97. package/template/.aioson/locales/es/agents/ux-ui.md +8 -0
  98. package/template/.aioson/locales/fr/agents/analyst.md +8 -0
  99. package/template/.aioson/locales/fr/agents/architect.md +8 -0
  100. package/template/.aioson/locales/fr/agents/dev.md +66 -7
  101. package/template/.aioson/locales/fr/agents/deyvin.md +8 -0
  102. package/template/.aioson/locales/fr/agents/neo.md +48 -0
  103. package/template/.aioson/locales/fr/agents/orchestrator.md +26 -0
  104. package/template/.aioson/locales/fr/agents/qa.md +26 -0
  105. package/template/.aioson/locales/fr/agents/setup.md +2 -1
  106. package/template/.aioson/locales/fr/agents/sheldon.md +192 -0
  107. package/template/.aioson/locales/fr/agents/squad.md +63 -0
  108. package/template/.aioson/locales/fr/agents/ux-ui.md +8 -0
  109. package/template/.aioson/locales/pt-BR/agents/analyst.md +19 -0
  110. package/template/.aioson/locales/pt-BR/agents/architect.md +19 -0
  111. package/template/.aioson/locales/pt-BR/agents/dev.md +75 -12
  112. package/template/.aioson/locales/pt-BR/agents/deyvin.md +8 -0
  113. package/template/.aioson/locales/pt-BR/agents/neo.md +147 -0
  114. package/template/.aioson/locales/pt-BR/agents/orchestrator.md +26 -0
  115. package/template/.aioson/locales/pt-BR/agents/product.md +8 -3
  116. package/template/.aioson/locales/pt-BR/agents/qa.md +60 -0
  117. package/template/.aioson/locales/pt-BR/agents/setup.md +2 -1
  118. package/template/.aioson/locales/pt-BR/agents/sheldon.md +192 -0
  119. package/template/.aioson/locales/pt-BR/agents/squad.md +105 -0
  120. package/template/.aioson/locales/pt-BR/agents/ux-ui.md +8 -0
  121. package/template/.aioson/schemas/squad-blueprint.schema.json +21 -0
  122. package/template/.aioson/schemas/squad-manifest.schema.json +178 -1
  123. package/template/.aioson/skills/design/bold-editorial-ui/SKILL.md +205 -0
  124. package/template/.aioson/skills/design/bold-editorial-ui/references/art-direction.md +338 -0
  125. package/template/.aioson/skills/design/bold-editorial-ui/references/components.md +977 -0
  126. package/template/.aioson/skills/design/bold-editorial-ui/references/dashboards.md +218 -0
  127. package/template/.aioson/skills/design/bold-editorial-ui/references/design-tokens.md +326 -0
  128. package/template/.aioson/skills/design/bold-editorial-ui/references/motion.md +461 -0
  129. package/template/.aioson/skills/design/bold-editorial-ui/references/patterns.md +293 -0
  130. package/template/.aioson/skills/design/bold-editorial-ui/references/websites.md +352 -0
  131. package/template/.aioson/skills/design/clean-saas-ui/SKILL.md +210 -0
  132. package/template/.aioson/skills/design/clean-saas-ui/references/art-direction.md +319 -0
  133. package/template/.aioson/skills/design/clean-saas-ui/references/components.md +365 -0
  134. package/template/.aioson/skills/design/clean-saas-ui/references/dashboards.md +196 -0
  135. package/template/.aioson/skills/design/clean-saas-ui/references/design-tokens.md +244 -0
  136. package/template/.aioson/skills/design/clean-saas-ui/references/motion.md +235 -0
  137. package/template/.aioson/skills/design/clean-saas-ui/references/patterns.md +215 -0
  138. package/template/.aioson/skills/design/clean-saas-ui/references/websites.md +295 -0
  139. package/template/.aioson/skills/design/cognitive-core-ui/SKILL.md +55 -9
  140. package/template/.aioson/skills/design/cognitive-core-ui/references/art-direction.md +339 -0
  141. package/template/.aioson/skills/design/cognitive-core-ui/references/components.md +1 -1
  142. package/template/.aioson/skills/design/cognitive-core-ui/references/dashboards.md +100 -0
  143. package/template/.aioson/skills/design/cognitive-core-ui/references/design-tokens.md +43 -9
  144. package/template/.aioson/skills/design/cognitive-core-ui/references/motion.md +40 -0
  145. package/template/.aioson/skills/design/cognitive-core-ui/references/patterns.md +1 -1
  146. package/template/.aioson/skills/design/cognitive-core-ui/references/websites.md +99 -12
  147. package/template/.aioson/skills/design/warm-craft-ui/SKILL.md +209 -0
  148. package/template/.aioson/skills/design/warm-craft-ui/references/art-direction.md +324 -0
  149. package/template/.aioson/skills/design/warm-craft-ui/references/components.md +508 -0
  150. package/template/.aioson/skills/design/warm-craft-ui/references/dashboards.md +223 -0
  151. package/template/.aioson/skills/design/warm-craft-ui/references/design-tokens.md +374 -0
  152. package/template/.aioson/skills/design/warm-craft-ui/references/motion.md +356 -0
  153. package/template/.aioson/skills/design/warm-craft-ui/references/patterns.md +288 -0
  154. package/template/.aioson/skills/design/warm-craft-ui/references/websites.md +289 -0
  155. package/template/.aioson/skills/premium-visual-design/SKILL.md +83 -0
  156. package/template/.aioson/skills/premium-visual-design/components/agent-badge.md +92 -0
  157. package/template/.aioson/skills/premium-visual-design/components/dependency-node.md +102 -0
  158. package/template/.aioson/skills/premium-visual-design/components/mention-autocomplete.md +136 -0
  159. package/template/.aioson/skills/premium-visual-design/components/notification-center.md +136 -0
  160. package/template/.aioson/skills/premium-visual-design/components/review-action-bar.md +188 -0
  161. package/template/.aioson/skills/premium-visual-design/components/team-switcher.md +131 -0
  162. package/template/.aioson/skills/premium-visual-design/patterns/agent-message-thread.md +198 -0
  163. package/template/.aioson/skills/premium-visual-design/patterns/notification-panel.md +275 -0
  164. package/template/.aioson/skills/premium-visual-design/patterns/review-workflow-ui.md +234 -0
  165. package/template/.aioson/skills/premium-visual-design/patterns/task-dependency-graph.md +147 -0
  166. package/template/.aioson/skills/premium-visual-design/tokens/status-extended.md +142 -0
  167. package/template/.aioson/skills/squad/formats/catalog.json +15 -0
  168. package/template/.aioson/skills/squad/formats/content/blog-post.md +47 -0
  169. package/template/.aioson/skills/squad/formats/content/newsletter.md +47 -0
  170. package/template/.aioson/skills/squad/formats/creative/podcast-script.md +43 -0
  171. package/template/.aioson/skills/squad/formats/creative/video-script.md +41 -0
  172. package/template/.aioson/skills/squad/formats/social/instagram-feed.md +42 -0
  173. package/template/.aioson/skills/squad/formats/social/linkedin-post.md +42 -0
  174. package/template/.aioson/skills/squad/formats/social/tiktok.md +39 -0
  175. package/template/.aioson/skills/squad/formats/social/twitter-thread.md +39 -0
  176. package/template/.aioson/skills/squad/formats/social/youtube-long.md +47 -0
  177. package/template/.aioson/skills/squad/formats/social/youtube-shorts.md +39 -0
  178. package/template/.aioson/skills/squad/patterns/multi-platform-pattern.md +108 -0
  179. package/template/.aioson/skills/squad/patterns/persona-based-pattern.md +98 -0
  180. package/template/.aioson/skills/squad/patterns/pipeline-pattern.md +106 -0
  181. package/template/.aioson/skills/squad/patterns/review-loop-pattern.md +81 -0
  182. package/template/.aioson/skills/squad/references/checklist-templates.md +122 -0
  183. package/template/.aioson/skills/squad/references/executor-archetypes.md +123 -0
  184. package/template/.aioson/skills/squad/references/workflow-templates.md +169 -0
  185. package/template/.aioson/skills/static/debugging-protocol.md +42 -0
  186. package/template/.aioson/skills/static/git-worktrees.md +36 -0
  187. package/template/.aioson/tasks/implementation-plan.md +19 -0
  188. package/template/.aioson/tasks/squad-design.md +28 -0
  189. package/template/.aioson/tasks/squad-profile.md +48 -0
  190. package/template/.aioson/tasks/squad-review.md +61 -0
  191. package/template/.aioson/tasks/squad-task-decompose.md +66 -0
  192. package/template/.claude/commands/aioson/agent/neo.md +5 -0
  193. package/template/.claude/commands/aioson/agent/tester.md +5 -0
  194. package/template/.gemini/GEMINI.md +1 -0
  195. package/template/.gemini/commands/aios-neo.toml +4 -0
  196. package/template/.gemini/commands/aios-tester.toml +6 -0
  197. package/template/AGENTS.md +3 -0
  198. package/template/CLAUDE.md +5 -2
  199. package/template/OPENCODE.md +2 -0
@@ -0,0 +1,201 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+
6
+ const SQUADS_DIR = path.join('.aioson', 'squads');
7
+
8
+ // Events that trigger an automatic refresh of the recovery context
9
+ const REFRESH_EVENTS = new Set(['task_completed', 'decision_made', 'handoff']);
10
+
11
+ // Approximate token count (chars / 4)
12
+ function estimateTokens(str) {
13
+ return Math.ceil(str.length / 4);
14
+ }
15
+
16
+ /**
17
+ * Read squad manifest (best-effort, returns {} on failure).
18
+ */
19
+ async function readManifest(projectDir, squadSlug) {
20
+ const p = path.join(projectDir, SQUADS_DIR, squadSlug, 'squad.manifest.json');
21
+ try {
22
+ return JSON.parse(await fs.readFile(p, 'utf8'));
23
+ } catch {
24
+ return {};
25
+ }
26
+ }
27
+
28
+ /**
29
+ * Read recent events from session-log.json (last N entries).
30
+ */
31
+ async function readRecentEvents(projectDir, squadSlug, limit = 10) {
32
+ const p = path.join(projectDir, SQUADS_DIR, squadSlug, 'session-log.json');
33
+ try {
34
+ const raw = JSON.parse(await fs.readFile(p, 'utf8'));
35
+ const entries = Array.isArray(raw) ? raw : (raw.entries || []);
36
+ return entries.slice(-limit);
37
+ } catch {
38
+ return [];
39
+ }
40
+ }
41
+
42
+ /**
43
+ * Read recent tasks from tasks.json (last 5, enriched).
44
+ */
45
+ async function readRecentTasks(projectDir, squadSlug, limit = 5) {
46
+ const p = path.join(projectDir, SQUADS_DIR, squadSlug, 'tasks.json');
47
+ try {
48
+ const raw = JSON.parse(await fs.readFile(p, 'utf8'));
49
+ const tasks = Array.isArray(raw) ? raw : (raw.tasks || []);
50
+ return tasks.slice(-limit);
51
+ } catch {
52
+ return [];
53
+ }
54
+ }
55
+
56
+ /**
57
+ * Read context-monitor.json snapshot for an agent.
58
+ */
59
+ async function readContextSnapshot(projectDir, squadSlug, agentSlug) {
60
+ const p = path.join(projectDir, SQUADS_DIR, squadSlug, 'context-monitor.json');
61
+ try {
62
+ const data = JSON.parse(await fs.readFile(p, 'utf8'));
63
+ if (agentSlug && data.agents && data.agents[agentSlug]) {
64
+ return data.agents[agentSlug];
65
+ }
66
+ return data;
67
+ } catch {
68
+ return null;
69
+ }
70
+ }
71
+
72
+ /**
73
+ * Build the markdown content for recovery-context.md.
74
+ * Target < 2000 tokens.
75
+ */
76
+ function buildRecoveryMarkdown(squadSlug, agentSlug, manifest, tasks, events, ctxSnapshot) {
77
+ const lines = [];
78
+
79
+ lines.push(`# Recovery Context — ${squadSlug} / ${agentSlug}`);
80
+ lines.push(`> Generated: ${new Date().toISOString()}`);
81
+ lines.push('');
82
+
83
+ // Squad goal
84
+ if (manifest.goal) {
85
+ lines.push('## Squad Goal');
86
+ lines.push(manifest.goal);
87
+ lines.push('');
88
+ }
89
+
90
+ // Agent role (from executors array)
91
+ const executor = (manifest.executors || []).find(e => e.slug === agentSlug);
92
+ if (executor) {
93
+ lines.push('## Your Role');
94
+ lines.push(`**${executor.title || agentSlug}**: ${executor.role || ''}`);
95
+ lines.push('');
96
+ }
97
+
98
+ // Recent tasks
99
+ if (tasks.length > 0) {
100
+ lines.push('## Recent Tasks');
101
+ for (const t of tasks) {
102
+ const status = t.status || 'unknown';
103
+ const title = t.title || t.slug || t.id || '(untitled)';
104
+ lines.push(`- [${status}] ${title}`);
105
+ if (t.output && typeof t.output === 'string') {
106
+ // Truncate long outputs
107
+ const out = t.output.length > 200 ? t.output.slice(0, 200) + '…' : t.output;
108
+ lines.push(` Output: ${out}`);
109
+ }
110
+ }
111
+ lines.push('');
112
+ }
113
+
114
+ // Recent events
115
+ if (events.length > 0) {
116
+ lines.push('## Recent Events');
117
+ for (const ev of events) {
118
+ const ts = ev.created_at || ev.timestamp || '';
119
+ const type = ev.event_type || ev.type || 'event';
120
+ const msg = ev.message || ev.summary || '';
121
+ lines.push(`- [${ts}] ${type}: ${msg}`);
122
+ }
123
+ lines.push('');
124
+ }
125
+
126
+ // Context snapshot
127
+ if (ctxSnapshot) {
128
+ lines.push('## Context Window at Last Compact');
129
+ const used = ctxSnapshot.totalUsed || 0;
130
+ const win = ctxSnapshot.windowSize || 0;
131
+ const pct = win > 0 ? Math.round((used / win) * 100) : 0;
132
+ lines.push(`Used: ${used.toLocaleString()} / ${win.toLocaleString()} tokens (${pct}%)`);
133
+ lines.push('');
134
+ }
135
+
136
+ lines.push('---');
137
+ lines.push('*Inject this file at the top of your next session to restore context after a compact.*');
138
+
139
+ const content = lines.join('\n');
140
+
141
+ // Enforce token limit: if over budget, trim events section
142
+ if (estimateTokens(content) > 2000) {
143
+ // Rebuild with fewer events
144
+ return buildRecoveryMarkdown(squadSlug, agentSlug, manifest, tasks, events.slice(-3), ctxSnapshot);
145
+ }
146
+
147
+ return content;
148
+ }
149
+
150
+ /**
151
+ * Generate and write recovery-context.md for an agent.
152
+ * @param {string} projectDir
153
+ * @param {string} squadSlug
154
+ * @param {string} agentSlug
155
+ * @returns {{ ok: boolean, path: string, tokens: number }}
156
+ */
157
+ async function generateRecovery(projectDir, squadSlug, agentSlug) {
158
+ const [manifest, tasks, events, ctxSnapshot] = await Promise.all([
159
+ readManifest(projectDir, squadSlug),
160
+ readRecentTasks(projectDir, squadSlug),
161
+ readRecentEvents(projectDir, squadSlug),
162
+ readContextSnapshot(projectDir, squadSlug, agentSlug)
163
+ ]);
164
+
165
+ const content = buildRecoveryMarkdown(squadSlug, agentSlug, manifest, tasks, events, ctxSnapshot);
166
+ const tokens = estimateTokens(content);
167
+
168
+ const outDir = path.join(projectDir, SQUADS_DIR, squadSlug);
169
+ const outPath = path.join(outDir, `recovery-context.md`);
170
+
171
+ try {
172
+ await fs.mkdir(outDir, { recursive: true });
173
+ await fs.writeFile(outPath, content, 'utf8');
174
+ } catch (err) {
175
+ return { ok: false, error: err.message, path: outPath, tokens };
176
+ }
177
+
178
+ return { ok: true, path: outPath, tokens, squadSlug, agentSlug };
179
+ }
180
+
181
+ /**
182
+ * Read the current recovery-context.md for an agent (returns null if missing).
183
+ */
184
+ async function readRecovery(projectDir, squadSlug) {
185
+ const p = path.join(projectDir, SQUADS_DIR, squadSlug, 'recovery-context.md');
186
+ try {
187
+ return await fs.readFile(p, 'utf8');
188
+ } catch {
189
+ return null;
190
+ }
191
+ }
192
+
193
+ /**
194
+ * Check if a runtime event should trigger a recovery refresh.
195
+ * @param {string} eventType
196
+ */
197
+ function shouldRefreshOnEvent(eventType) {
198
+ return REFRESH_EVENTS.has(eventType);
199
+ }
200
+
201
+ module.exports = { generateRecovery, readRecovery, shouldRefreshOnEvent, REFRESH_EVENTS };
@@ -0,0 +1,114 @@
1
+ 'use strict';
2
+
3
+ const { spawnSync } = require('node:child_process');
4
+ const path = require('node:path');
5
+
6
+ /**
7
+ * Create a git worktree for a squad agent.
8
+ * Branch: aioson/{squadSlug}/{agentSlug}
9
+ * Path: .aioson/squads/{squadSlug}/worktrees/{agentSlug}
10
+ */
11
+ function createWorktree(projectDir, squadSlug, agentSlug) {
12
+ const branchName = `aioson/${squadSlug}/${agentSlug}`;
13
+ const worktreePath = path.join(projectDir, '.aioson', 'squads', squadSlug, 'worktrees', agentSlug);
14
+
15
+ // Try with new branch first
16
+ let result = spawnSync('git', ['worktree', 'add', '-b', branchName, worktreePath], {
17
+ cwd: projectDir,
18
+ encoding: 'utf8'
19
+ });
20
+
21
+ if (result.status !== 0) {
22
+ // Branch may already exist — try without -b
23
+ result = spawnSync('git', ['worktree', 'add', worktreePath, branchName], {
24
+ cwd: projectDir,
25
+ encoding: 'utf8'
26
+ });
27
+ if (result.status !== 0) {
28
+ throw new Error(result.stderr || 'Failed to create worktree');
29
+ }
30
+ }
31
+
32
+ return { ok: true, branchName, worktreePath };
33
+ }
34
+
35
+ /**
36
+ * Merge worktree branch back to the current branch.
37
+ * autoMerge=true → attempt fast-forward. false → report without merging.
38
+ */
39
+ function mergeWorktree(projectDir, squadSlug, agentSlug, autoMerge = false) {
40
+ const branchName = `aioson/${squadSlug}/${agentSlug}`;
41
+
42
+ if (!autoMerge) {
43
+ return { ok: false, reason: 'auto_merge_disabled', branch: branchName };
44
+ }
45
+
46
+ const result = spawnSync('git', ['merge', '--ff-only', branchName], {
47
+ cwd: projectDir,
48
+ encoding: 'utf8'
49
+ });
50
+
51
+ if (result.status === 0) {
52
+ return { ok: true, branch: branchName, merged: true };
53
+ }
54
+
55
+ return {
56
+ ok: false,
57
+ reason: 'merge_conflict',
58
+ branch: branchName,
59
+ detail: (result.stderr || result.stdout || '').trim()
60
+ };
61
+ }
62
+
63
+ /**
64
+ * Remove a worktree (and optionally delete its branch).
65
+ */
66
+ function cleanupWorktree(projectDir, squadSlug, agentSlug, deleteBranch = false) {
67
+ const worktreePath = path.join(projectDir, '.aioson', 'squads', squadSlug, 'worktrees', agentSlug);
68
+ const branchName = `aioson/${squadSlug}/${agentSlug}`;
69
+
70
+ const result = spawnSync('git', ['worktree', 'remove', '--force', worktreePath], {
71
+ cwd: projectDir,
72
+ encoding: 'utf8'
73
+ });
74
+
75
+ const removed = result.status === 0;
76
+
77
+ if (deleteBranch) {
78
+ spawnSync('git', ['branch', '-D', branchName], { cwd: projectDir, encoding: 'utf8' });
79
+ }
80
+
81
+ return { ok: removed, worktreePath, branchName };
82
+ }
83
+
84
+ /**
85
+ * List all squad worktrees (filters by aioson/{squadSlug}/ branch prefix).
86
+ */
87
+ function listWorktrees(projectDir, squadSlug) {
88
+ const result = spawnSync('git', ['worktree', 'list', '--porcelain'], {
89
+ cwd: projectDir,
90
+ encoding: 'utf8'
91
+ });
92
+
93
+ if (result.status !== 0) return [];
94
+
95
+ const entries = [];
96
+ const blocks = result.stdout.trim().split('\n\n');
97
+ for (const block of blocks) {
98
+ if (!block.trim()) continue;
99
+ const entry = {};
100
+ for (const line of block.split('\n')) {
101
+ if (line.startsWith('worktree ')) entry.path = line.slice(9);
102
+ else if (line.startsWith('branch ')) entry.branch = line.slice(7).replace('refs/heads/', '');
103
+ else if (line.startsWith('HEAD ')) entry.head = line.slice(5);
104
+ }
105
+ if (entry.branch && entry.branch.startsWith(`aioson/${squadSlug}/`)) {
106
+ entry.agentSlug = entry.branch.split('/').pop();
107
+ entries.push(entry);
108
+ }
109
+ }
110
+
111
+ return entries;
112
+ }
113
+
114
+ module.exports = { createWorktree, mergeWorktree, cleanupWorktree, listWorktrees };