@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,209 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const { SquadDaemon } = require('../squad-daemon');
6
+ const { openRuntimeDb } = require('../runtime-store');
7
+
8
+ async function handleStart(projectDir, squadSlug, options, { logger, t }) {
9
+ if (!squadSlug) {
10
+ logger.error(t('squad_daemon.squad_required'));
11
+ return { ok: false };
12
+ }
13
+
14
+ let squadConfig = {};
15
+ try {
16
+ const squadJsonPath = path.join(projectDir, '.aioson', 'squads', squadSlug, 'squad.json');
17
+ squadConfig = JSON.parse(await fs.readFile(squadJsonPath, 'utf8'));
18
+ } catch { /* squad.json is optional */ }
19
+
20
+ const daemon = new SquadDaemon(projectDir, squadSlug, {
21
+ port: options.port ? Number(options.port) : 0,
22
+ poll: options.poll ? Number(options.poll) : 10000,
23
+ config: squadConfig
24
+ });
25
+
26
+ try {
27
+ const info = await daemon.start();
28
+ logger.log(t('squad_daemon.started', {
29
+ squad: squadSlug,
30
+ port: info.port,
31
+ workers: info.workers,
32
+ cron: info.cronJobs
33
+ }));
34
+ logger.log(t('squad_daemon.webhook_hint', { port: info.port }));
35
+ logger.log(t('squad_daemon.stop_hint'));
36
+
37
+ // Keep alive until signal
38
+ await new Promise((resolve) => {
39
+ process.on('SIGINT', resolve);
40
+ process.on('SIGTERM', resolve);
41
+ });
42
+
43
+ logger.log(t('squad_daemon.stopping'));
44
+ await daemon.stop();
45
+ return { ok: true, ...info };
46
+ } catch (err) {
47
+ logger.error(t('squad_daemon.start_failed', { error: err.message }));
48
+ return { ok: false, error: err.message };
49
+ }
50
+ }
51
+
52
+ async function handleStatus(projectDir, squadSlug, { logger, t }) {
53
+ if (!squadSlug) {
54
+ // List all daemons
55
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
56
+ if (!handle) {
57
+ logger.error(t('squad_daemon.no_runtime'));
58
+ return { ok: false };
59
+ }
60
+ const { db } = handle;
61
+ try {
62
+ const daemons = db.prepare('SELECT * FROM squad_daemons ORDER BY squad_slug').all();
63
+ if (daemons.length === 0) {
64
+ logger.log(t('squad_daemon.no_daemons'));
65
+ return { ok: true, daemons: [] };
66
+ }
67
+ logger.log(`Daemons (${daemons.length}):`);
68
+ for (const d of daemons) {
69
+ const icon = d.status === 'running' ? '[*]' : '[ ]';
70
+ logger.log(` ${icon} ${d.squad_slug} (${d.status}) port:${d.port || '-'} pid:${d.pid || '-'} heartbeat:${d.last_heartbeat || '-'}`);
71
+ }
72
+ return { ok: true, daemons };
73
+ } finally {
74
+ db.close();
75
+ }
76
+ }
77
+
78
+ // Specific squad daemon status
79
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
80
+ if (!handle) {
81
+ logger.error(t('squad_daemon.no_runtime'));
82
+ return { ok: false };
83
+ }
84
+ const { db } = handle;
85
+ try {
86
+ const record = db.prepare('SELECT * FROM squad_daemons WHERE squad_slug = ?').get(squadSlug);
87
+ if (!record) {
88
+ logger.log(t('squad_daemon.not_found', { squad: squadSlug }));
89
+ return { ok: true, daemon: null };
90
+ }
91
+ logger.log(`Daemon: ${record.squad_slug}`);
92
+ logger.log(` Status: ${record.status}`);
93
+ logger.log(` PID: ${record.pid || '-'}`);
94
+ logger.log(` Port: ${record.port || '-'}`);
95
+ logger.log(` Started: ${record.started_at || '-'}`);
96
+ logger.log(` Heartbeat: ${record.last_heartbeat || '-'}`);
97
+ if (record.error_message) logger.log(` Error: ${record.error_message}`);
98
+ if (record.config_json) {
99
+ try {
100
+ const config = JSON.parse(record.config_json);
101
+ if (config.cronJobs && config.cronJobs.length > 0) {
102
+ logger.log(` Cron Jobs:`);
103
+ for (const job of config.cronJobs) {
104
+ logger.log(` - ${job.worker} (${job.cron})`);
105
+ }
106
+ }
107
+ } catch { /* ignore */ }
108
+ }
109
+ return { ok: true, daemon: record };
110
+ } finally {
111
+ db.close();
112
+ }
113
+ }
114
+
115
+ async function handleStop(projectDir, squadSlug, { logger, t }) {
116
+ if (!squadSlug) {
117
+ logger.error(t('squad_daemon.squad_required'));
118
+ return { ok: false };
119
+ }
120
+
121
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
122
+ if (!handle) {
123
+ logger.error(t('squad_daemon.no_runtime'));
124
+ return { ok: false };
125
+ }
126
+ const { db } = handle;
127
+ try {
128
+ const record = db.prepare('SELECT * FROM squad_daemons WHERE squad_slug = ?').get(squadSlug);
129
+ if (!record || record.status !== 'running') {
130
+ logger.log(t('squad_daemon.not_running', { squad: squadSlug }));
131
+ return { ok: true };
132
+ }
133
+
134
+ // Try to kill the process
135
+ if (record.pid) {
136
+ try {
137
+ process.kill(record.pid, 'SIGTERM');
138
+ logger.log(t('squad_daemon.signal_sent', { squad: squadSlug, pid: record.pid }));
139
+ } catch {
140
+ logger.log(t('squad_daemon.process_gone', { squad: squadSlug }));
141
+ }
142
+ }
143
+
144
+ // Update record
145
+ db.prepare(
146
+ "UPDATE squad_daemons SET status = 'stopped' WHERE squad_slug = ?"
147
+ ).run(squadSlug);
148
+
149
+ return { ok: true };
150
+ } finally {
151
+ db.close();
152
+ }
153
+ }
154
+
155
+ async function handleLogs(projectDir, squadSlug, { logger, t }) {
156
+ if (!squadSlug) {
157
+ logger.error(t('squad_daemon.squad_required'));
158
+ return { ok: false };
159
+ }
160
+
161
+ // Show recent worker runs as daemon logs
162
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
163
+ if (!handle) {
164
+ logger.error(t('squad_daemon.no_runtime'));
165
+ return { ok: false };
166
+ }
167
+ const { db } = handle;
168
+ try {
169
+ const runs = db.prepare(
170
+ 'SELECT * FROM worker_runs WHERE squad_slug = ? ORDER BY created_at DESC LIMIT 30'
171
+ ).all(squadSlug);
172
+ if (runs.length === 0) {
173
+ logger.log(t('squad_daemon.no_logs'));
174
+ return { ok: true, runs: [] };
175
+ }
176
+ logger.log(`Recent daemon activity for "${squadSlug}" (${runs.length}):`);
177
+ for (const r of runs) {
178
+ const icon = r.status === 'completed' ? '[ok]' : r.status === 'failed' ? '[!!]' : '[..]';
179
+ const ms = r.duration_ms ? `${r.duration_ms}ms` : '-';
180
+ logger.log(` ${icon} ${r.created_at} ${r.worker_slug} (${r.trigger_type}) ${ms}`);
181
+ if (r.error_message) logger.log(` ${r.error_message}`);
182
+ }
183
+ return { ok: true, runs };
184
+ } finally {
185
+ db.close();
186
+ }
187
+ }
188
+
189
+ async function runSquadDaemon({ args, options, logger, t }) {
190
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
191
+ const sub = options.sub || 'status';
192
+ const squadSlug = options.squad;
193
+
194
+ switch (sub) {
195
+ case 'start':
196
+ return handleStart(targetDir, squadSlug, options, { logger, t });
197
+ case 'status':
198
+ return handleStatus(targetDir, squadSlug, { logger, t });
199
+ case 'stop':
200
+ return handleStop(targetDir, squadSlug, { logger, t });
201
+ case 'logs':
202
+ return handleLogs(targetDir, squadSlug, { logger, t });
203
+ default:
204
+ logger.error(t('squad_daemon.unknown_sub', { sub }));
205
+ return { ok: false };
206
+ }
207
+ }
208
+
209
+ module.exports = { runSquadDaemon };
@@ -0,0 +1,39 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const { createDashboardServer } = require('../squad-dashboard/server');
5
+
6
+ async function runSquadDashboard({ args, options, logger, t }) {
7
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
8
+ const port = Number(options.port) || 4180;
9
+ const filterSquad = options.squad || null;
10
+
11
+ const dashboard = createDashboardServer(targetDir, { port, squad: filterSquad });
12
+
13
+ try {
14
+ const info = await dashboard.start();
15
+ logger.log(t('squad_dashboard.started', { url: info.url, port: info.port }));
16
+ if (filterSquad) {
17
+ logger.log(t('squad_dashboard.filtered', { squad: filterSquad }));
18
+ }
19
+ logger.log(t('squad_dashboard.stop_hint'));
20
+
21
+ // Keep the process alive until SIGINT/SIGTERM
22
+ await new Promise((resolve) => {
23
+ process.on('SIGINT', resolve);
24
+ process.on('SIGTERM', resolve);
25
+ });
26
+
27
+ logger.log(t('squad_dashboard.stopping'));
28
+ await dashboard.stop();
29
+ return { ok: true, port: info.port, url: info.url };
30
+ } catch (err) {
31
+ if (err.code === 'EADDRINUSE') {
32
+ logger.error(t('squad_dashboard.port_in_use', { port }));
33
+ return { ok: false, error: `Port ${port} already in use` };
34
+ }
35
+ throw err;
36
+ }
37
+ }
38
+
39
+ module.exports = { runSquadDashboard };
@@ -0,0 +1,64 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+
6
+ async function runSquadDeploy({ args, options, logger }) {
7
+ const squadSlug = args[0] || options.squad;
8
+ const projectDir = path.resolve(args[1] || options.path || '.');
9
+ const provider = options.provider || 'cloudpanel';
10
+
11
+ if (!squadSlug) {
12
+ logger.log('Uso: aioson squad:deploy <squad> [dir] --provider=cloudpanel');
13
+ return { ok: false };
14
+ }
15
+
16
+ const squadJsonPath = path.join(projectDir, '.aioson', 'squads', squadSlug, 'squad.json');
17
+ let squadConfig;
18
+ try {
19
+ squadConfig = JSON.parse(await fs.readFile(squadJsonPath, 'utf8'));
20
+ } catch {
21
+ logger.log(`Squad "${squadSlug}" não encontrado em ${squadJsonPath}`);
22
+ return { ok: false, error: 'squad_not_found' };
23
+ }
24
+
25
+ const port = squadConfig.port || 3001;
26
+ const deployDir = path.join(projectDir, '.aioson', 'squads', squadSlug, 'deploy');
27
+ await fs.mkdir(deployDir, { recursive: true });
28
+
29
+ const nginxConf = generateNginxConf(squadSlug, port, provider);
30
+ const outPath = path.join(deployDir, 'nginx.conf');
31
+ await fs.writeFile(outPath, nginxConf);
32
+
33
+ logger.log(`✓ Configuração nginx gerada: ${outPath}`);
34
+ logger.log('');
35
+ logger.log('Instruções CloudPanel:');
36
+ logger.log(' 1. Acesse CloudPanel → Websites → [seu site] → Nginx');
37
+ logger.log(' 2. Cole o conteúdo do arquivo nginx.conf em "Custom Nginx Directives"');
38
+ logger.log(' 3. Salve e aguarde o reload automático do nginx');
39
+ logger.log(` 4. Certifique-se que o daemon está rodando: aioson squad:daemon ${squadSlug} start`);
40
+
41
+ return { ok: true, path: outPath };
42
+ }
43
+
44
+ function generateNginxConf(slug, port, provider) {
45
+ return [
46
+ `# Squad: ${slug} — gerado por aioson squad:deploy`,
47
+ `# Provider: ${provider}`,
48
+ `# Cole em: CloudPanel → Website → Nginx → Custom Nginx Directives`,
49
+ `#`,
50
+ `# Pré-requisito: aioson squad:daemon ${slug} start`,
51
+ ``,
52
+ `location /${slug}/webhook/ {`,
53
+ ` proxy_pass http://127.0.0.1:${port}/webhook/;`,
54
+ ` proxy_http_version 1.1;`,
55
+ ` proxy_set_header Host $host;`,
56
+ ` proxy_set_header X-Real-IP $remote_addr;`,
57
+ ` proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;`,
58
+ ` proxy_read_timeout 30s;`,
59
+ ` proxy_connect_timeout 5s;`,
60
+ `}`,
61
+ ].join('\n');
62
+ }
63
+
64
+ module.exports = { runSquadDeploy, generateNginxConf };
@@ -5,6 +5,7 @@ const path = require('node:path');
5
5
  const { openRuntimeDb } = require('../runtime-store');
6
6
  const { exists } = require('../utils');
7
7
  const { runSquadValidate } = require('./squad-validate');
8
+ const { scoreCompletude, scoreProfundidade, scoreQualidadeEstrutural, scorePotencial, gradeFromScore } = require('./squad-score');
8
9
 
9
10
  function normalizeRel(value) {
10
11
  return String(value || '')
@@ -120,7 +121,7 @@ async function parseSquadPaths(targetDir, slug) {
120
121
  ? path.join(targetDir, normalizeRel(packageInfo.agentsDir || `.aioson/squads/${slug}/agents`))
121
122
  : path.join(targetDir, 'agents', slug),
122
123
  outputDir: path.join(targetDir, normalizeRel(rules.outputsDir || `output/${slug}`)),
123
- logsDir: path.join(targetDir, normalizeRel(rules.logsDir || `aios-logs/${slug}`)),
124
+ logsDir: path.join(targetDir, normalizeRel(rules.logsDir || `aioson-logs/${slug}`)),
124
125
  mediaDir: path.join(targetDir, normalizeRel(rules.mediaDir || `media/${slug}`))
125
126
  };
126
127
  }
@@ -209,6 +210,86 @@ async function runSquadDoctor({ args, options = {}, logger, t }) {
209
210
  checks.push(makeCheck('output_dir', outputExists, outputExists ? 'info' : 'warn', t('squad_doctor.check_output_dir', { path: paths.outputDir })));
210
211
  checks.push(makeCheck('media_dir', mediaExists, mediaExists ? 'info' : 'warn', t('squad_doctor.check_media_dir', { path: paths.mediaDir })));
211
212
 
213
+ // Output strategy checks
214
+ const outputStrategy = manifest.outputStrategy && typeof manifest.outputStrategy === 'object'
215
+ ? manifest.outputStrategy
216
+ : null;
217
+
218
+ if (outputStrategy) {
219
+ const delivery = outputStrategy.delivery && typeof outputStrategy.delivery === 'object'
220
+ ? outputStrategy.delivery
221
+ : {};
222
+ const webhooks = Array.isArray(delivery.webhooks) ? delivery.webhooks : [];
223
+ const hasDelivery = webhooks.length > 0 || delivery.cloudPublish;
224
+
225
+ checks.push(makeCheck(
226
+ 'output_strategy',
227
+ true,
228
+ 'info',
229
+ `Output strategy: mode=${outputStrategy.mode || 'unknown'}, webhooks=${webhooks.length}, cloudPublish=${Boolean(delivery.cloudPublish)}, autoPublish=${Boolean(delivery.autoPublish)}`
230
+ ));
231
+
232
+ if (delivery.autoPublish && !hasDelivery) {
233
+ checks.push(makeCheck(
234
+ 'output_auto_publish',
235
+ false,
236
+ 'warn',
237
+ 'autoPublish is enabled but no delivery targets configured (no webhooks, no cloudPublish)'
238
+ ));
239
+ }
240
+
241
+ for (const wh of webhooks) {
242
+ if (wh.url && wh.url.includes('{{ENV:')) {
243
+ const envMatch = wh.url.match(/\{\{ENV:(\w+)\}\}/);
244
+ if (envMatch && !process.env[envMatch[1]]) {
245
+ checks.push(makeCheck(
246
+ `webhook_env_${wh.slug}`,
247
+ false,
248
+ 'warn',
249
+ `Webhook "${wh.slug}" references unset env var: ${envMatch[1]}`
250
+ ));
251
+ }
252
+ }
253
+ }
254
+ } else {
255
+ checks.push(makeCheck(
256
+ 'output_strategy',
257
+ true,
258
+ 'info',
259
+ 'No output strategy configured (default file output will be used)'
260
+ ));
261
+ }
262
+
263
+ // Webhook production checks (from squad.json, optional file)
264
+ const squadJsonPath = path.join(paths.packageDir, 'squad.json');
265
+ let squadConfig = null;
266
+ try {
267
+ const raw = await fs.readFile(squadJsonPath, 'utf8');
268
+ squadConfig = JSON.parse(raw);
269
+ } catch { /* squad.json is optional */ }
270
+
271
+ if (squadConfig && squadConfig.webhook?.validate_signature) {
272
+ const envKey = squadConfig.webhook.signature_env || 'WEBHOOK_SECRET';
273
+ const defined = Boolean(process.env[envKey]);
274
+ checks.push(makeCheck(
275
+ `webhook_env_${envKey}`,
276
+ defined,
277
+ defined ? 'info' : 'error',
278
+ defined
279
+ ? `Env ${envKey} definida (HMAC validation enabled)`
280
+ : `Env ${envKey} não definida — webhook HMAC validation está ativo mas a variável está ausente`
281
+ ));
282
+ }
283
+
284
+ if (squadConfig && squadConfig.webhook?.public) {
285
+ checks.push(makeCheck(
286
+ 'webhook_public_deploy',
287
+ null,
288
+ 'warn',
289
+ 'Config nginx: execute aioson squad:deploy para gerar nginx.conf'
290
+ ));
291
+ }
292
+
212
293
  const runtimeHandle = await openRuntimeDb(targetDir, { mustExist: true });
213
294
  if (!runtimeHandle) {
214
295
  checks.push(makeCheck('runtime_store', false, 'warn', t('squad_doctor.check_runtime_missing')));
@@ -297,6 +378,27 @@ async function runSquadDoctor({ args, options = {}, logger, t }) {
297
378
  checks.push(makeCheck('formal_validation', true, 'info', 'Manifest formally valid'));
298
379
  }
299
380
 
381
+ // Quality score
382
+ try {
383
+ const d1 = scoreCompletude(manifest);
384
+ const d2 = scoreProfundidade(manifest);
385
+ const d3 = scoreQualidadeEstrutural(manifest);
386
+ const d4 = scorePotencial(manifest);
387
+ const total = d1.score + d2.score + d3.score + d4.score;
388
+ const maxTotal = d1.max + d2.max + d3.max + d4.max;
389
+ const grade = gradeFromScore(total);
390
+ const isLow = total < 50;
391
+ checks.push(
392
+ makeCheck(
393
+ 'quality_score',
394
+ !isLow,
395
+ 'info',
396
+ `Quality score: ${total}/${maxTotal} — ${grade}`,
397
+ { qualityScore: { total, max: maxTotal, grade } }
398
+ )
399
+ );
400
+ } catch { /* scoring not critical */ }
401
+
300
402
  const summary = {
301
403
  failed: checks.filter((check) => check.severity === 'error' && !check.ok).length,
302
404
  warned: checks.filter((check) => check.severity === 'warn' && !check.ok).length,
@@ -0,0 +1,261 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const {
6
+ openRuntimeDb,
7
+ insertInvestigation,
8
+ listInvestigations,
9
+ getInvestigation,
10
+ linkInvestigation
11
+ } = require('../runtime-store');
12
+
13
+ const SEARCHES_DIR = 'squad-searches';
14
+ const DIMENSION_HEADERS = ['D1', 'D2', 'D3', 'D4', 'D5', 'D6', 'D7'];
15
+
16
+ async function pathExists(targetPath) {
17
+ try {
18
+ await fs.access(targetPath);
19
+ return true;
20
+ } catch {
21
+ return false;
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Count how many of the 7 investigation dimensions are present in a report.
27
+ * Looks for headers like "## D1:", "## D2:", ..., "## D7:" in the markdown.
28
+ */
29
+ function countDimensions(content) {
30
+ const text = String(content || '');
31
+ let count = 0;
32
+ for (const dim of DIMENSION_HEADERS) {
33
+ const pattern = new RegExp(`^##\\s+${dim}[:\\s]`, 'm');
34
+ if (pattern.test(text)) count++;
35
+ }
36
+ return count;
37
+ }
38
+
39
+ /**
40
+ * Calculate an investigation completeness score (0-1).
41
+ */
42
+ function scoreCompleteness(content) {
43
+ const covered = countDimensions(content);
44
+ const total = DIMENSION_HEADERS.length;
45
+ return { covered, total, score: Math.round((covered / total) * 100) / 100 };
46
+ }
47
+
48
+ /**
49
+ * Subcommand: list
50
+ * Lists all investigations registered in the runtime SQLite.
51
+ */
52
+ async function handleList(projectDir, { logger, t }) {
53
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
54
+ if (!handle) {
55
+ logger.error(t('squad_investigate.no_runtime'));
56
+ return { investigations: [], count: 0 };
57
+ }
58
+ const { db } = handle;
59
+ try {
60
+ const rows = listInvestigations(db);
61
+ if (rows.length === 0) {
62
+ logger.log(t('squad_investigate.no_investigations'));
63
+ return { investigations: [], count: 0 };
64
+ }
65
+ for (const row of rows) {
66
+ const linked = row.linked_squad_slug ? ` → ${row.linked_squad_slug}` : '';
67
+ const dims = `${row.dimensions_covered}/${row.total_dimensions}`;
68
+ logger.log(` ${row.investigation_slug} [${row.mode}] ${row.domain} ${dims} conf=${row.confidence}${linked}`);
69
+ }
70
+ return { investigations: rows, count: rows.length };
71
+ } finally {
72
+ db.close();
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Subcommand: show <slug>
78
+ * Shows the investigation report content.
79
+ */
80
+ async function handleShow(projectDir, slug, { logger, t }) {
81
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
82
+ if (!handle) {
83
+ logger.error(t('squad_investigate.no_runtime'));
84
+ return { found: false };
85
+ }
86
+ const { db } = handle;
87
+ try {
88
+ const row = getInvestigation(db, slug);
89
+ if (!row) {
90
+ logger.error(t('squad_investigate.not_found', { slug }));
91
+ return { found: false };
92
+ }
93
+ logger.log(`Investigation: ${row.investigation_slug}`);
94
+ logger.log(`Domain: ${row.domain}`);
95
+ logger.log(`Mode: ${row.mode}`);
96
+ logger.log(`Dimensions: ${row.dimensions_covered}/${row.total_dimensions}`);
97
+ logger.log(`Confidence: ${row.confidence}`);
98
+ logger.log(`Report: ${row.report_path || '(none)'}`);
99
+ logger.log(`Linked squad: ${row.linked_squad_slug || '(standalone)'}`);
100
+ logger.log(`Created: ${row.created_at}`);
101
+
102
+ if (row.report_path) {
103
+ const reportFile = path.resolve(projectDir, row.report_path);
104
+ if (await pathExists(reportFile)) {
105
+ const content = await fs.readFile(reportFile, 'utf8');
106
+ logger.log('');
107
+ logger.log(content);
108
+ }
109
+ }
110
+ return { found: true, investigation: row };
111
+ } finally {
112
+ db.close();
113
+ }
114
+ }
115
+
116
+ /**
117
+ * Subcommand: score <slug>
118
+ * Calculates the completeness score for an investigation report.
119
+ */
120
+ async function handleScore(projectDir, slug, { logger, t }) {
121
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
122
+ if (!handle) {
123
+ logger.error(t('squad_investigate.no_runtime'));
124
+ return { found: false, score: 0 };
125
+ }
126
+ const { db } = handle;
127
+ try {
128
+ const row = getInvestigation(db, slug);
129
+ if (!row) {
130
+ logger.error(t('squad_investigate.not_found', { slug }));
131
+ return { found: false, score: 0 };
132
+ }
133
+ if (!row.report_path) {
134
+ logger.error(t('squad_investigate.no_report', { slug }));
135
+ return { found: true, score: 0 };
136
+ }
137
+ const reportFile = path.resolve(projectDir, row.report_path);
138
+ if (!(await pathExists(reportFile))) {
139
+ logger.error(t('squad_investigate.report_missing', { path: row.report_path }));
140
+ return { found: true, score: 0 };
141
+ }
142
+ const content = await fs.readFile(reportFile, 'utf8');
143
+ const result = scoreCompleteness(content);
144
+ logger.log(`Completeness: ${result.covered}/${result.total} dimensions (${result.score})`);
145
+ return { found: true, ...result };
146
+ } finally {
147
+ db.close();
148
+ }
149
+ }
150
+
151
+ /**
152
+ * Subcommand: link <investigation-slug> <squad-slug>
153
+ * Associates an investigation with a squad.
154
+ */
155
+ async function handleLink(projectDir, invSlug, squadSlug, { logger, t }) {
156
+ if (!invSlug || !squadSlug) {
157
+ logger.error(t('squad_investigate.link_usage'));
158
+ return { linked: false };
159
+ }
160
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
161
+ if (!handle) {
162
+ logger.error(t('squad_investigate.no_runtime'));
163
+ return { linked: false };
164
+ }
165
+ const { db } = handle;
166
+ try {
167
+ const success = linkInvestigation(db, invSlug, squadSlug);
168
+ if (success) {
169
+ logger.log(t('squad_investigate.linked', { investigation: invSlug, squad: squadSlug }));
170
+ } else {
171
+ logger.error(t('squad_investigate.not_found', { slug: invSlug }));
172
+ }
173
+ return { linked: success };
174
+ } finally {
175
+ db.close();
176
+ }
177
+ }
178
+
179
+ /**
180
+ * Subcommand: register
181
+ * Registers an existing investigation report file into the runtime SQLite.
182
+ */
183
+ async function handleRegister(projectDir, reportPath, options, { logger, t }) {
184
+ const handle = await openRuntimeDb(projectDir, { mustExist: true });
185
+ if (!handle) {
186
+ logger.error(t('squad_investigate.no_runtime'));
187
+ return { registered: false };
188
+ }
189
+ const { db } = handle;
190
+ try {
191
+ const absPath = path.resolve(projectDir, reportPath);
192
+ if (!(await pathExists(absPath))) {
193
+ logger.error(t('squad_investigate.report_missing', { path: reportPath }));
194
+ return { registered: false };
195
+ }
196
+ const content = await fs.readFile(absPath, 'utf8');
197
+ const { covered, total, score } = scoreCompleteness(content);
198
+ const relPath = path.relative(projectDir, absPath);
199
+ const slug = insertInvestigation(db, {
200
+ investigationSlug: options.slug || undefined,
201
+ domain: options.domain || path.basename(reportPath, '.md'),
202
+ mode: options.mode || 'full',
203
+ dimensionsCovered: covered,
204
+ totalDimensions: total,
205
+ confidence: options.confidence ? Number(options.confidence) : score,
206
+ reportPath: relPath,
207
+ linkedSquadSlug: options.squad || null
208
+ });
209
+ logger.log(t('squad_investigate.registered', { slug, path: relPath }));
210
+ return { registered: true, slug };
211
+ } finally {
212
+ db.close();
213
+ }
214
+ }
215
+
216
+ async function runSquadInvestigate({ args = [], options = {}, logger = console, t = (k) => k } = {}) {
217
+ const projectDir = path.resolve(process.cwd(), args[0] || '.');
218
+ const sub = options.sub || args[1] || 'list';
219
+
220
+ if (sub === 'list') {
221
+ return handleList(projectDir, { logger, t });
222
+ }
223
+
224
+ if (sub === 'show') {
225
+ const slug = options.investigation || args[2];
226
+ if (!slug) {
227
+ logger.error(t('squad_investigate.show_usage'));
228
+ return { found: false };
229
+ }
230
+ return handleShow(projectDir, slug, { logger, t });
231
+ }
232
+
233
+ if (sub === 'score') {
234
+ const slug = options.investigation || args[2];
235
+ if (!slug) {
236
+ logger.error(t('squad_investigate.score_usage'));
237
+ return { found: false, score: 0 };
238
+ }
239
+ return handleScore(projectDir, slug, { logger, t });
240
+ }
241
+
242
+ if (sub === 'link') {
243
+ const invSlug = options.investigation || args[2];
244
+ const squadSlug = options.squad || args[3];
245
+ return handleLink(projectDir, invSlug, squadSlug, { logger, t });
246
+ }
247
+
248
+ if (sub === 'register') {
249
+ const reportPath = options.report || args[2];
250
+ if (!reportPath) {
251
+ logger.error(t('squad_investigate.register_usage'));
252
+ return { registered: false };
253
+ }
254
+ return handleRegister(projectDir, reportPath, options, { logger, t });
255
+ }
256
+
257
+ logger.error(t('squad_investigate.unknown_sub', { sub }));
258
+ return { error: `Unknown subcommand: ${sub}` };
259
+ }
260
+
261
+ module.exports = { runSquadInvestigate, scoreCompleteness, countDimensions };