@jaimevalasek/aioson 1.3.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 (330) hide show
  1. package/CHANGELOG.md +31 -1
  2. package/LICENSE +661 -21
  3. package/README.md +22 -3
  4. package/docs/en/squad-dashboard.md +372 -0
  5. package/docs/openclaw-bridge.md +308 -0
  6. package/docs/pt/README.md +62 -2
  7. package/docs/pt/advisor-spec.md +5 -5
  8. package/docs/pt/agentes-customizados.md +670 -0
  9. package/docs/pt/agentes.md +235 -23
  10. package/docs/pt/automacao-squads.md +407 -0
  11. package/docs/pt/cenarios.md +49 -5
  12. package/docs/pt/clientes-ai.md +62 -0
  13. package/docs/pt/comandos-cli.md +226 -17
  14. package/docs/pt/deyvin.md +115 -0
  15. package/docs/pt/genome-3.0-spec.md +11 -11
  16. package/docs/pt/inicio-rapido.md +63 -2
  17. package/docs/pt/memoria-contexto.md +255 -0
  18. package/docs/pt/output-strategy-delivery.md +655 -0
  19. package/docs/pt/profiler-system.md +17 -17
  20. package/docs/pt/runtime-observability.md +5 -1
  21. package/docs/pt/skills.md +175 -0
  22. package/docs/pt/squad-dashboard.md +373 -0
  23. package/docs/pt/{squad-genoma.md → squad-genome.md} +81 -75
  24. package/docs/testing/genome-2.0-matrix.md +5 -5
  25. package/docs/testing/genome-2.0-rollout.md +10 -10
  26. package/package.json +4 -4
  27. package/src/agents.js +21 -5
  28. package/src/backup-local.js +74 -0
  29. package/src/backup-provider.js +303 -0
  30. package/src/cli.js +276 -2
  31. package/src/commands/agents.js +22 -4
  32. package/src/commands/backup-local-cmd.js +25 -0
  33. package/src/commands/backup.js +533 -0
  34. package/src/commands/cloud.js +17 -17
  35. package/src/commands/context-pack.js +45 -0
  36. package/src/commands/implementation-plan.js +340 -0
  37. package/src/commands/learning.js +134 -0
  38. package/src/commands/live.js +1583 -0
  39. package/src/commands/runtime.js +1075 -2
  40. package/src/commands/scan-project.js +288 -24
  41. package/src/commands/setup-context.js +30 -2
  42. package/src/commands/skill.js +558 -0
  43. package/src/commands/squad-agent-create.js +788 -0
  44. package/src/commands/squad-daemon.js +209 -0
  45. package/src/commands/squad-dashboard.js +39 -0
  46. package/src/commands/squad-deploy.js +64 -0
  47. package/src/commands/squad-doctor.js +103 -1
  48. package/src/commands/squad-investigate.js +261 -0
  49. package/src/commands/squad-learning.js +209 -0
  50. package/src/commands/squad-mcp.js +270 -0
  51. package/src/commands/squad-pipeline.js +247 -1
  52. package/src/commands/squad-plan.js +329 -0
  53. package/src/commands/squad-processes.js +56 -0
  54. package/src/commands/squad-recovery.js +42 -0
  55. package/src/commands/squad-roi.js +291 -0
  56. package/src/commands/squad-score.js +250 -0
  57. package/src/commands/squad-status.js +38 -2
  58. package/src/commands/squad-validate.js +118 -1
  59. package/src/commands/squad-webhook.js +160 -0
  60. package/src/commands/squad-worker.js +191 -0
  61. package/src/commands/squad-worktrees.js +75 -0
  62. package/src/commands/test-agents.js +6 -1
  63. package/src/commands/web-map.js +70 -0
  64. package/src/commands/web-scrape.js +71 -0
  65. package/src/commands/workflow-next.js +8 -1
  66. package/src/commands/workflow-status.js +250 -0
  67. package/src/constants.js +88 -16
  68. package/src/context-memory.js +837 -0
  69. package/src/context-writer.js +47 -1
  70. package/src/delivery-runner.js +319 -0
  71. package/src/genome-files.js +1 -1
  72. package/src/genome-format.js +1 -1
  73. package/src/i18n/messages/en.js +333 -8
  74. package/src/i18n/messages/es.js +240 -6
  75. package/src/i18n/messages/fr.js +239 -5
  76. package/src/i18n/messages/pt-BR.js +330 -12
  77. package/src/installer.js +30 -2
  78. package/src/lib/genomes/compat.js +1 -1
  79. package/src/lib/webhook-server.js +328 -0
  80. package/src/mcp-connectors/registry.js +602 -0
  81. package/src/runtime-store.js +1037 -42
  82. package/src/session-handoff.js +77 -0
  83. package/src/squad/external-session.js +180 -0
  84. package/src/squad/inter-squad.js +74 -0
  85. package/src/squad/recovery-context.js +201 -0
  86. package/src/squad/worktree-manager.js +114 -0
  87. package/src/squad-daemon.js +490 -0
  88. package/src/squad-dashboard/api.js +223 -0
  89. package/src/squad-dashboard/attachment-handler.js +93 -0
  90. package/src/squad-dashboard/context-monitor.js +157 -0
  91. package/src/squad-dashboard/execution-logs.js +115 -0
  92. package/src/squad-dashboard/hunk-review.js +209 -0
  93. package/src/squad-dashboard/metrics.js +133 -0
  94. package/src/squad-dashboard/process-monitor.js +125 -0
  95. package/src/squad-dashboard/renderer.js +858 -0
  96. package/src/squad-dashboard/server.js +232 -0
  97. package/src/squad-dashboard/styles.js +525 -0
  98. package/src/squad-dashboard/token-tracker.js +99 -0
  99. package/src/web.js +284 -0
  100. package/src/worker-runner.js +339 -0
  101. package/template/.aioson/agents/analyst.md +40 -9
  102. package/template/.aioson/agents/architect.md +24 -5
  103. package/template/.aioson/agents/dev.md +254 -25
  104. package/template/.aioson/agents/deyvin.md +174 -0
  105. package/template/.aioson/agents/discovery-design-doc.md +25 -1
  106. package/template/.aioson/agents/{genoma.md → genome.md} +20 -20
  107. package/template/.aioson/agents/neo.md +152 -0
  108. package/template/.aioson/agents/orache.md +388 -0
  109. package/template/.aioson/agents/orchestrator.md +63 -2
  110. package/template/.aioson/agents/pair.md +5 -0
  111. package/template/.aioson/agents/pm.md +17 -5
  112. package/template/.aioson/agents/product.md +113 -29
  113. package/template/.aioson/agents/profiler-enricher.md +1 -1
  114. package/template/.aioson/agents/profiler-forge.md +9 -9
  115. package/template/.aioson/agents/profiler-researcher.md +1 -1
  116. package/template/.aioson/agents/qa.md +18 -5
  117. package/template/.aioson/agents/setup.md +138 -18
  118. package/template/.aioson/agents/sheldon.md +603 -0
  119. package/template/.aioson/agents/squad.md +866 -28
  120. package/template/.aioson/agents/tester.md +254 -0
  121. package/template/.aioson/agents/ux-ui.md +289 -34
  122. package/template/.aioson/config.md +181 -0
  123. package/template/.aioson/context/spec.md.template +17 -0
  124. package/template/.aioson/genomes/.gitkeep +0 -0
  125. package/template/.aioson/installed-skills/.gitkeep +0 -0
  126. package/template/.aioson/locales/en/agents/analyst.md +34 -4
  127. package/template/.aioson/locales/en/agents/architect.md +18 -0
  128. package/template/.aioson/locales/en/agents/dev.md +155 -11
  129. package/template/.aioson/locales/en/agents/deyvin.md +137 -0
  130. package/template/.aioson/locales/en/agents/{genoma.md → genome.md} +14 -14
  131. package/template/.aioson/locales/en/agents/neo.md +8 -0
  132. package/template/.aioson/locales/en/agents/orchestrator.md +62 -2
  133. package/template/.aioson/locales/en/agents/pair.md +5 -0
  134. package/template/.aioson/locales/en/agents/pm.md +7 -0
  135. package/template/.aioson/locales/en/agents/product.md +35 -17
  136. package/template/.aioson/locales/en/agents/qa.md +56 -0
  137. package/template/.aioson/locales/en/agents/setup.md +53 -6
  138. package/template/.aioson/locales/en/agents/sheldon.md +340 -0
  139. package/template/.aioson/locales/en/agents/squad.md +203 -15
  140. package/template/.aioson/locales/en/agents/ux-ui.md +383 -35
  141. package/template/.aioson/locales/es/agents/analyst.md +24 -4
  142. package/template/.aioson/locales/es/agents/architect.md +18 -0
  143. package/template/.aioson/locales/es/agents/dev.md +136 -9
  144. package/template/.aioson/locales/es/agents/deyvin.md +97 -0
  145. package/template/.aioson/locales/es/agents/{genoma.md → genome.md} +13 -13
  146. package/template/.aioson/locales/es/agents/neo.md +48 -0
  147. package/template/.aioson/locales/es/agents/orache.md +103 -0
  148. package/template/.aioson/locales/es/agents/orchestrator.md +62 -2
  149. package/template/.aioson/locales/es/agents/pair.md +5 -0
  150. package/template/.aioson/locales/es/agents/pm.md +7 -0
  151. package/template/.aioson/locales/es/agents/product.md +13 -3
  152. package/template/.aioson/locales/es/agents/qa.md +33 -0
  153. package/template/.aioson/locales/es/agents/setup.md +30 -6
  154. package/template/.aioson/locales/es/agents/sheldon.md +192 -0
  155. package/template/.aioson/locales/es/agents/squad.md +284 -15
  156. package/template/.aioson/locales/es/agents/ux-ui.md +34 -25
  157. package/template/.aioson/locales/fr/agents/analyst.md +24 -4
  158. package/template/.aioson/locales/fr/agents/architect.md +18 -0
  159. package/template/.aioson/locales/fr/agents/dev.md +136 -9
  160. package/template/.aioson/locales/fr/agents/deyvin.md +97 -0
  161. package/template/.aioson/locales/fr/agents/{genoma.md → genome.md} +7 -7
  162. package/template/.aioson/locales/fr/agents/neo.md +48 -0
  163. package/template/.aioson/locales/fr/agents/orache.md +104 -0
  164. package/template/.aioson/locales/fr/agents/orchestrator.md +62 -2
  165. package/template/.aioson/locales/fr/agents/pair.md +5 -0
  166. package/template/.aioson/locales/fr/agents/pm.md +7 -0
  167. package/template/.aioson/locales/fr/agents/product.md +13 -3
  168. package/template/.aioson/locales/fr/agents/qa.md +33 -0
  169. package/template/.aioson/locales/fr/agents/setup.md +30 -6
  170. package/template/.aioson/locales/fr/agents/sheldon.md +192 -0
  171. package/template/.aioson/locales/fr/agents/squad.md +279 -10
  172. package/template/.aioson/locales/fr/agents/ux-ui.md +34 -25
  173. package/template/.aioson/locales/pt-BR/agents/analyst.md +45 -4
  174. package/template/.aioson/locales/pt-BR/agents/architect.md +29 -0
  175. package/template/.aioson/locales/pt-BR/agents/dev.md +167 -15
  176. package/template/.aioson/locales/pt-BR/agents/deyvin.md +137 -0
  177. package/template/.aioson/locales/pt-BR/agents/{genoma.md → genome.md} +49 -49
  178. package/template/.aioson/locales/pt-BR/agents/neo.md +147 -0
  179. package/template/.aioson/locales/pt-BR/agents/orache.md +137 -0
  180. package/template/.aioson/locales/pt-BR/agents/orchestrator.md +62 -2
  181. package/template/.aioson/locales/pt-BR/agents/pair.md +5 -0
  182. package/template/.aioson/locales/pt-BR/agents/pm.md +7 -0
  183. package/template/.aioson/locales/pt-BR/agents/product.md +43 -20
  184. package/template/.aioson/locales/pt-BR/agents/qa.md +67 -0
  185. package/template/.aioson/locales/pt-BR/agents/setup.md +53 -6
  186. package/template/.aioson/locales/pt-BR/agents/sheldon.md +192 -0
  187. package/template/.aioson/locales/pt-BR/agents/squad.md +591 -47
  188. package/template/.aioson/locales/pt-BR/agents/ux-ui.md +369 -22
  189. package/template/.aioson/my-agents/.gitkeep +0 -0
  190. package/template/.aioson/rules/.gitkeep +0 -0
  191. package/template/.aioson/rules/squad/.gitkeep +0 -0
  192. package/template/.aioson/rules/squad/README.md +50 -0
  193. package/template/.aioson/schemas/genome-meta.schema.json +1 -1
  194. package/template/.aioson/schemas/genome.schema.json +1 -1
  195. package/template/.aioson/schemas/squad-blueprint.schema.json +32 -0
  196. package/template/.aioson/schemas/squad-manifest.schema.json +434 -1
  197. package/template/.aioson/skills/design/bold-editorial-ui/SKILL.md +205 -0
  198. package/template/.aioson/skills/design/bold-editorial-ui/references/art-direction.md +338 -0
  199. package/template/.aioson/skills/design/bold-editorial-ui/references/components.md +977 -0
  200. package/template/.aioson/skills/design/bold-editorial-ui/references/dashboards.md +218 -0
  201. package/template/.aioson/skills/design/bold-editorial-ui/references/design-tokens.md +326 -0
  202. package/template/.aioson/skills/design/bold-editorial-ui/references/motion.md +461 -0
  203. package/template/.aioson/skills/design/bold-editorial-ui/references/patterns.md +293 -0
  204. package/template/.aioson/skills/design/bold-editorial-ui/references/websites.md +352 -0
  205. package/template/.aioson/skills/design/clean-saas-ui/SKILL.md +210 -0
  206. package/template/.aioson/skills/design/clean-saas-ui/references/art-direction.md +319 -0
  207. package/template/.aioson/skills/design/clean-saas-ui/references/components.md +365 -0
  208. package/template/.aioson/skills/design/clean-saas-ui/references/dashboards.md +196 -0
  209. package/template/.aioson/skills/design/clean-saas-ui/references/design-tokens.md +244 -0
  210. package/template/.aioson/skills/design/clean-saas-ui/references/motion.md +235 -0
  211. package/template/.aioson/skills/design/clean-saas-ui/references/patterns.md +215 -0
  212. package/template/.aioson/skills/design/clean-saas-ui/references/websites.md +295 -0
  213. package/template/.aioson/skills/design/cognitive-core-ui/SKILL.md +203 -0
  214. package/template/.aioson/skills/design/cognitive-core-ui/references/art-direction.md +339 -0
  215. package/template/.aioson/skills/design/cognitive-core-ui/references/components.md +407 -0
  216. package/template/.aioson/skills/design/cognitive-core-ui/references/dashboards.md +272 -0
  217. package/template/.aioson/skills/design/cognitive-core-ui/references/design-tokens.md +524 -0
  218. package/template/.aioson/skills/design/cognitive-core-ui/references/motion.md +277 -0
  219. package/template/.aioson/skills/design/cognitive-core-ui/references/patterns.md +289 -0
  220. package/template/.aioson/skills/design/cognitive-core-ui/references/websites.md +437 -0
  221. package/template/.aioson/skills/design/interface-design/SKILL.md +47 -0
  222. package/template/.aioson/skills/design/interface-design/references/components-and-states.md +105 -0
  223. package/template/.aioson/skills/design/interface-design/references/design-directions.md +101 -0
  224. package/template/.aioson/skills/design/interface-design/references/handoff-and-quality.md +71 -0
  225. package/template/.aioson/skills/design/interface-design/references/intent-and-domain.md +74 -0
  226. package/template/.aioson/skills/design/interface-design/references/tokens-and-depth.md +173 -0
  227. package/template/.aioson/skills/design/premium-command-center-ui/SKILL.md +62 -0
  228. package/template/.aioson/skills/design/premium-command-center-ui/references/operations.md +74 -0
  229. package/template/.aioson/skills/design/premium-command-center-ui/references/patterns.md +116 -0
  230. package/template/.aioson/skills/design/premium-command-center-ui/references/validation.md +47 -0
  231. package/template/.aioson/skills/design/premium-command-center-ui/references/visual-system.md +215 -0
  232. package/template/.aioson/skills/design/warm-craft-ui/SKILL.md +209 -0
  233. package/template/.aioson/skills/design/warm-craft-ui/references/art-direction.md +324 -0
  234. package/template/.aioson/skills/design/warm-craft-ui/references/components.md +508 -0
  235. package/template/.aioson/skills/design/warm-craft-ui/references/dashboards.md +223 -0
  236. package/template/.aioson/skills/design/warm-craft-ui/references/design-tokens.md +374 -0
  237. package/template/.aioson/skills/design/warm-craft-ui/references/motion.md +356 -0
  238. package/template/.aioson/skills/design/warm-craft-ui/references/patterns.md +288 -0
  239. package/template/.aioson/skills/design/warm-craft-ui/references/websites.md +289 -0
  240. package/template/.aioson/skills/design-system/SKILL.md +92 -0
  241. package/template/.aioson/skills/design-system/cognitive-core-ui.skill +0 -0
  242. package/template/.aioson/skills/design-system/components/SKILL.md +274 -0
  243. package/template/.aioson/skills/design-system/components/SKILL.md:Zone.Identifier +0 -0
  244. package/template/.aioson/skills/design-system/dashboards/SKILL.md +184 -0
  245. package/template/.aioson/skills/design-system/dashboards/SKILL.md:Zone.Identifier +0 -0
  246. package/template/.aioson/skills/design-system/foundations/SKILL.md +250 -0
  247. package/template/.aioson/skills/design-system/foundations/SKILL.md:Zone.Identifier +0 -0
  248. package/template/.aioson/skills/design-system/motion/SKILL.md +197 -0
  249. package/template/.aioson/skills/design-system/motion/SKILL.md:Zone.Identifier +0 -0
  250. package/template/.aioson/skills/design-system/patterns/SKILL.md +231 -0
  251. package/template/.aioson/skills/design-system/patterns/SKILL.md:Zone.Identifier +0 -0
  252. package/template/.aioson/skills/premium-visual-design/SKILL.md +83 -0
  253. package/template/.aioson/skills/premium-visual-design/components/agent-badge.md +92 -0
  254. package/template/.aioson/skills/premium-visual-design/components/dependency-node.md +102 -0
  255. package/template/.aioson/skills/premium-visual-design/components/mention-autocomplete.md +136 -0
  256. package/template/.aioson/skills/premium-visual-design/components/notification-center.md +136 -0
  257. package/template/.aioson/skills/premium-visual-design/components/review-action-bar.md +188 -0
  258. package/template/.aioson/skills/premium-visual-design/components/team-switcher.md +131 -0
  259. package/template/.aioson/skills/premium-visual-design/patterns/agent-message-thread.md +198 -0
  260. package/template/.aioson/skills/premium-visual-design/patterns/notification-panel.md +275 -0
  261. package/template/.aioson/skills/premium-visual-design/patterns/review-workflow-ui.md +234 -0
  262. package/template/.aioson/skills/premium-visual-design/patterns/task-dependency-graph.md +147 -0
  263. package/template/.aioson/skills/premium-visual-design/tokens/status-extended.md +142 -0
  264. package/template/.aioson/skills/squad/SKILL.md +58 -0
  265. package/template/.aioson/skills/squad/domains/.gitkeep +0 -0
  266. package/template/.aioson/skills/squad/formats/.gitkeep +0 -0
  267. package/template/.aioson/skills/squad/formats/catalog.json +15 -0
  268. package/template/.aioson/skills/squad/formats/content/blog-post.md +47 -0
  269. package/template/.aioson/skills/squad/formats/content/newsletter.md +47 -0
  270. package/template/.aioson/skills/squad/formats/creative/podcast-script.md +43 -0
  271. package/template/.aioson/skills/squad/formats/creative/video-script.md +41 -0
  272. package/template/.aioson/skills/squad/formats/social/instagram-feed.md +42 -0
  273. package/template/.aioson/skills/squad/formats/social/linkedin-post.md +42 -0
  274. package/template/.aioson/skills/squad/formats/social/tiktok.md +39 -0
  275. package/template/.aioson/skills/squad/formats/social/twitter-thread.md +39 -0
  276. package/template/.aioson/skills/squad/formats/social/youtube-long.md +47 -0
  277. package/template/.aioson/skills/squad/formats/social/youtube-shorts.md +39 -0
  278. package/template/.aioson/skills/squad/patterns/.gitkeep +0 -0
  279. package/template/.aioson/skills/squad/patterns/multi-platform-pattern.md +108 -0
  280. package/template/.aioson/skills/squad/patterns/persona-based-pattern.md +98 -0
  281. package/template/.aioson/skills/squad/patterns/pipeline-pattern.md +106 -0
  282. package/template/.aioson/skills/squad/patterns/review-loop-pattern.md +81 -0
  283. package/template/.aioson/skills/squad/references/.gitkeep +0 -0
  284. package/template/.aioson/skills/squad/references/checklist-templates.md +122 -0
  285. package/template/.aioson/skills/squad/references/executor-archetypes.md +123 -0
  286. package/template/.aioson/skills/squad/references/workflow-templates.md +169 -0
  287. package/template/.aioson/skills/static/debugging-protocol.md +42 -0
  288. package/template/.aioson/skills/static/git-worktrees.md +36 -0
  289. package/template/.aioson/tasks/implementation-plan.md +307 -0
  290. package/template/.aioson/tasks/squad-create.md +1 -1
  291. package/template/.aioson/tasks/squad-design.md +28 -0
  292. package/template/.aioson/tasks/squad-execution-plan.md +279 -0
  293. package/template/.aioson/tasks/squad-export.md +1 -1
  294. package/template/.aioson/tasks/squad-investigate.md +44 -0
  295. package/template/.aioson/tasks/squad-learning-review.md +44 -0
  296. package/template/.aioson/tasks/squad-output-config.md +177 -0
  297. package/template/.aioson/tasks/squad-profile.md +48 -0
  298. package/template/.aioson/tasks/squad-review.md +61 -0
  299. package/template/.aioson/tasks/squad-task-decompose.md +66 -0
  300. package/template/.aioson/tasks/squad-validate.md +1 -1
  301. package/template/.claude/commands/aioson/agent/deyvin.md +5 -0
  302. package/template/.claude/commands/aioson/agent/discovery-design-doc.md +5 -0
  303. package/template/.claude/commands/aioson/agent/genome.md +5 -0
  304. package/template/.claude/commands/aioson/agent/neo.md +5 -0
  305. package/template/.claude/commands/aioson/agent/product.md +5 -0
  306. package/template/.claude/commands/aioson/agent/profiler-enricher.md +5 -0
  307. package/template/.claude/commands/aioson/agent/profiler-forge.md +5 -0
  308. package/template/.claude/commands/aioson/agent/profiler-researcher.md +5 -0
  309. package/template/.claude/commands/aioson/agent/squad.md +5 -0
  310. package/template/.claude/commands/aioson/agent/tester.md +5 -0
  311. package/template/.gemini/GEMINI.md +3 -0
  312. package/template/.gemini/commands/aios-deyvin.toml +6 -0
  313. package/template/.gemini/commands/aios-neo.toml +4 -0
  314. package/template/.gemini/commands/aios-pair.toml +6 -0
  315. package/template/.gemini/commands/aios-tester.toml +6 -0
  316. package/template/AGENTS.md +37 -6
  317. package/template/CLAUDE.md +34 -4
  318. package/template/OPENCODE.md +8 -2
  319. package/template/squad-searches/.gitkeep +0 -0
  320. package/template/.aioson/skills/static/interface-design.md +0 -372
  321. package/template/.aioson/skills/static/premium-command-center-ui.md +0 -190
  322. /package/template/.aioson/{genomas → docs}/.gitkeep +0 -0
  323. /package/template/.claude/commands/aioson/{analyst.md → agent/analyst.md} +0 -0
  324. /package/template/.claude/commands/aioson/{architect.md → agent/architect.md} +0 -0
  325. /package/template/.claude/commands/aioson/{dev.md → agent/dev.md} +0 -0
  326. /package/template/.claude/commands/aioson/{orchestrator.md → agent/orchestrator.md} +0 -0
  327. /package/template/.claude/commands/aioson/{pm.md → agent/pm.md} +0 -0
  328. /package/template/.claude/commands/aioson/{qa.md → agent/qa.md} +0 -0
  329. /package/template/.claude/commands/aioson/{setup.md → agent/setup.md} +0 -0
  330. /package/template/.claude/commands/aioson/{ux-ui.md → agent/ux-ui.md} +0 -0
@@ -0,0 +1,77 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const { exists, ensureDir } = require('./utils');
6
+
7
+ const HANDOFF_RELATIVE_PATH = '.aioson/context/last-handoff.json';
8
+
9
+ async function writeHandoff(targetDir, payload) {
10
+ const handoffPath = path.join(targetDir, HANDOFF_RELATIVE_PATH);
11
+ await ensureDir(path.dirname(handoffPath));
12
+ const handoff = {
13
+ version: 1,
14
+ session_ended_at: new Date().toISOString(),
15
+ last_agent: payload.lastAgent || null,
16
+ last_stage: payload.lastStage || null,
17
+ what_was_done: payload.whatWasDone || null,
18
+ what_comes_next: payload.whatComesNext || null,
19
+ next_agent: payload.nextAgent || null,
20
+ open_decisions: Array.isArray(payload.openDecisions) ? payload.openDecisions : [],
21
+ context_files_updated: Array.isArray(payload.contextFilesUpdated) ? payload.contextFilesUpdated : [],
22
+ workflow_mode: payload.workflowMode || null,
23
+ classification: payload.classification || null,
24
+ feature_slug: payload.featureSlug || null
25
+ };
26
+ await fs.writeFile(handoffPath, `${JSON.stringify(handoff, null, 2)}\n`, 'utf8');
27
+ return { handoffPath: HANDOFF_RELATIVE_PATH, handoff };
28
+ }
29
+
30
+ async function readHandoff(targetDir) {
31
+ const handoffPath = path.join(targetDir, HANDOFF_RELATIVE_PATH);
32
+ if (!(await exists(handoffPath))) return null;
33
+ try {
34
+ const content = await fs.readFile(handoffPath, 'utf8');
35
+ return JSON.parse(content);
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+
41
+ function buildWorkflowHandoff(state, completedStage, nextAgent) {
42
+ const agentLabel = completedStage ? `@${completedStage}` : null;
43
+ const nextLabel = nextAgent ? `@${nextAgent}` : null;
44
+
45
+ return {
46
+ lastAgent: agentLabel,
47
+ lastStage: completedStage || null,
48
+ whatWasDone: completedStage
49
+ ? `Stage ${agentLabel} completed.`
50
+ : 'Workflow state updated.',
51
+ whatComesNext: nextLabel
52
+ ? `Next stage: ${nextLabel}`
53
+ : 'Workflow is complete. No pending stages.',
54
+ nextAgent: nextLabel,
55
+ workflowMode: state.mode || null,
56
+ classification: state.classification || null,
57
+ featureSlug: state.featureSlug || null
58
+ };
59
+ }
60
+
61
+ function buildRuntimeLogHandoff(agentName, message, summary) {
62
+ return {
63
+ lastAgent: agentName ? `@${agentName.replace(/^@/, '')}` : null,
64
+ lastStage: null,
65
+ whatWasDone: summary || message || 'Agent session completed.',
66
+ whatComesNext: null,
67
+ nextAgent: null
68
+ };
69
+ }
70
+
71
+ module.exports = {
72
+ HANDOFF_RELATIVE_PATH,
73
+ writeHandoff,
74
+ readHandoff,
75
+ buildWorkflowHandoff,
76
+ buildRuntimeLogHandoff
77
+ };
@@ -0,0 +1,180 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+
6
+ const SESSIONS_DIR = path.join('.aioson', 'sessions');
7
+ const DEFAULT_TTL_HOURS = 24;
8
+
9
+ function sanitizeSessionId(sessionId) {
10
+ // Replace anything that isn't alphanumeric, hyphen, or underscore with a dash
11
+ return String(sessionId)
12
+ .replace(/[^a-zA-Z0-9_-]/g, '-')
13
+ .replace(/-{2,}/g, '-')
14
+ .replace(/^-|-$/g, '')
15
+ .slice(0, 128);
16
+ }
17
+
18
+ function sessionPath(projectDir, sessionId) {
19
+ return path.join(projectDir, SESSIONS_DIR, `${sanitizeSessionId(sessionId)}.json`);
20
+ }
21
+
22
+ async function ensureSessionsDir(projectDir) {
23
+ const dir = path.join(projectDir, SESSIONS_DIR);
24
+ await fs.mkdir(dir, { recursive: true });
25
+ return dir;
26
+ }
27
+
28
+ /**
29
+ * Load a session by ID. Returns null if not found or expired.
30
+ * @param {string} projectDir
31
+ * @param {string} sessionId
32
+ * @param {number} [ttlHours]
33
+ * @returns {Promise<object|null>}
34
+ */
35
+ async function loadSession(projectDir, sessionId, ttlHours = DEFAULT_TTL_HOURS) {
36
+ const filePath = sessionPath(projectDir, sessionId);
37
+ let raw;
38
+ try {
39
+ raw = await fs.readFile(filePath, 'utf8');
40
+ } catch {
41
+ return null;
42
+ }
43
+
44
+ let session;
45
+ try {
46
+ session = JSON.parse(raw);
47
+ } catch {
48
+ return null;
49
+ }
50
+
51
+ // Check TTL
52
+ if (ttlHours > 0 && session.last_active) {
53
+ const idleMs = Date.now() - new Date(session.last_active).getTime();
54
+ if (idleMs > ttlHours * 3_600_000) {
55
+ // Expired — delete and return null
56
+ await fs.unlink(filePath).catch(() => {});
57
+ return null;
58
+ }
59
+ }
60
+
61
+ return session;
62
+ }
63
+
64
+ /**
65
+ * Save (overwrite) a session file atomically.
66
+ * @param {string} projectDir
67
+ * @param {object} session Must have session.session_id
68
+ */
69
+ async function saveSession(projectDir, session) {
70
+ await ensureSessionsDir(projectDir);
71
+ const filePath = sessionPath(projectDir, session.session_id);
72
+ const tmpPath = filePath + '.tmp';
73
+ await fs.writeFile(tmpPath, JSON.stringify(session, null, 2), 'utf8');
74
+ await fs.rename(tmpPath, filePath);
75
+ }
76
+
77
+ /**
78
+ * Append a turn to a session. Creates the session file if it doesn't exist.
79
+ * @param {string} projectDir
80
+ * @param {string} sessionId
81
+ * @param {'user'|'assistant'} role
82
+ * @param {string} content
83
+ * @param {object} [metadata] Merged into session.metadata on first creation
84
+ * @param {number} [ttlHours]
85
+ * @returns {Promise<object>} Updated session
86
+ */
87
+ async function appendTurn(projectDir, sessionId, role, content, metadata = {}, ttlHours = DEFAULT_TTL_HOURS) {
88
+ let session = await loadSession(projectDir, sessionId, ttlHours);
89
+ const now = new Date().toISOString();
90
+
91
+ if (!session) {
92
+ session = {
93
+ session_id: sessionId,
94
+ channel: metadata.channel || 'webhook',
95
+ created_at: now,
96
+ last_active: now,
97
+ turns: [],
98
+ metadata: { ...metadata }
99
+ };
100
+ }
101
+
102
+ session.last_active = now;
103
+ session.turns.push({ role, content, ts: now });
104
+
105
+ // Merge any new metadata fields without overwriting existing ones
106
+ if (metadata && typeof metadata === 'object') {
107
+ for (const [k, v] of Object.entries(metadata)) {
108
+ if (session.metadata[k] === undefined) session.metadata[k] = v;
109
+ }
110
+ }
111
+
112
+ await saveSession(projectDir, session);
113
+ return session;
114
+ }
115
+
116
+ /**
117
+ * Build a context string from session history to inject into the squad input.
118
+ * @param {object} session
119
+ * @param {string} currentInput
120
+ * @returns {string}
121
+ */
122
+ function buildContextualInput(session, currentInput) {
123
+ if (!session || !session.turns || session.turns.length === 0) {
124
+ return currentInput;
125
+ }
126
+
127
+ const history = session.turns
128
+ .map(t => `${t.role}: ${t.content}`)
129
+ .join('\n');
130
+
131
+ return `[Conversation history]\n${history}\n\n[Current message]\n${currentInput}`;
132
+ }
133
+
134
+ /**
135
+ * Delete all session files that have been inactive for longer than ttlHours.
136
+ * @param {string} projectDir
137
+ * @param {number} [ttlHours]
138
+ * @returns {Promise<number>} Number of sessions deleted
139
+ */
140
+ async function cleanExpiredSessions(projectDir, ttlHours = DEFAULT_TTL_HOURS) {
141
+ const dir = path.join(projectDir, SESSIONS_DIR);
142
+ let entries;
143
+ try {
144
+ entries = await fs.readdir(dir, { withFileTypes: true });
145
+ } catch {
146
+ return 0;
147
+ }
148
+
149
+ let deleted = 0;
150
+ const cutoff = Date.now() - ttlHours * 3_600_000;
151
+
152
+ for (const entry of entries) {
153
+ if (!entry.isFile() || !entry.name.endsWith('.json')) continue;
154
+ const filePath = path.join(dir, entry.name);
155
+ try {
156
+ const raw = await fs.readFile(filePath, 'utf8');
157
+ const session = JSON.parse(raw);
158
+ const lastActive = session.last_active ? new Date(session.last_active).getTime() : 0;
159
+ if (lastActive < cutoff) {
160
+ await fs.unlink(filePath);
161
+ deleted++;
162
+ }
163
+ } catch {
164
+ // Corrupt file — remove it
165
+ await fs.unlink(filePath).catch(() => {});
166
+ deleted++;
167
+ }
168
+ }
169
+
170
+ return deleted;
171
+ }
172
+
173
+ module.exports = {
174
+ loadSession,
175
+ saveSession,
176
+ appendTurn,
177
+ buildContextualInput,
178
+ cleanExpiredSessions,
179
+ sanitizeSessionId
180
+ };
@@ -0,0 +1,74 @@
1
+ 'use strict';
2
+ const { randomUUID } = require('node:crypto');
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const { openRuntimeDb, insertWorkerRun } = require('../runtime-store');
6
+
7
+ const INBOX_DIR = (projectDir, squadSlug) =>
8
+ path.join(projectDir, '.aioson', 'squads', squadSlug, 'inbox');
9
+
10
+ async function callSquad({ projectDir, from, to, worker, payload, conversationId, depth = 0 }) {
11
+ // 1. Cascade guard
12
+ if (depth > 5) return { ok: false, error: 'cascade_guard' };
13
+
14
+ conversationId = conversationId || randomUUID();
15
+
16
+ // 2. Resolver porta do squad destino no SQLite (manter DB aberto para log)
17
+ const handle = await openRuntimeDb(projectDir);
18
+ const db = handle?.db;
19
+ const port = db
20
+ ?.prepare("SELECT port FROM squad_daemons WHERE squad_slug = ? AND status = 'running'")
21
+ .get(to)?.port;
22
+
23
+ let result;
24
+
25
+ // 3. Tentar chamada direta
26
+ if (port) {
27
+ try {
28
+ const res = await fetch(`http://127.0.0.1:${port}/webhook/${worker}`, {
29
+ method: 'POST',
30
+ headers: { 'Content-Type': 'application/json' },
31
+ body: JSON.stringify({ ...payload, _inter_squad: { from, conversationId, depth: depth + 1 } }),
32
+ signal: AbortSignal.timeout(10000)
33
+ });
34
+ const json = await res.json();
35
+ result = { ok: res.ok, result: json, conversationId };
36
+ } catch {
37
+ // cai para inbox
38
+ }
39
+ }
40
+
41
+ // 4. Enfileirar na inbox do squad destino
42
+ if (!result) {
43
+ const inboxDir = INBOX_DIR(projectDir, to);
44
+ await fs.mkdir(inboxDir, { recursive: true });
45
+ const id = randomUUID();
46
+ await fs.writeFile(
47
+ path.join(inboxDir, `${id}.json`),
48
+ JSON.stringify({ id, from, to, worker, payload, conversationId, depth, created_at: new Date().toISOString() })
49
+ );
50
+ result = { ok: false, error: 'offline_queued', conversationId };
51
+ }
52
+
53
+ // 5. Gravar chamada emitida no runtime store (trigger_type = 'inter-squad')
54
+ if (db) {
55
+ try {
56
+ insertWorkerRun(db, {
57
+ squadSlug: from,
58
+ workerSlug: worker,
59
+ triggerType: 'inter-squad',
60
+ inputJson: JSON.stringify({ to, payload, conversationId }),
61
+ outputJson: result.ok ? JSON.stringify(result.result) : null,
62
+ status: result.ok ? 'completed' : 'failed',
63
+ errorMessage: result.ok ? null : result.error,
64
+ durationMs: 0,
65
+ attempt: 1
66
+ });
67
+ } catch { /* ignore */ }
68
+ db.close();
69
+ }
70
+
71
+ return result;
72
+ }
73
+
74
+ module.exports = { callSquad, INBOX_DIR };
@@ -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 };