@jaimevalasek/aioson 1.6.0 → 1.7.0

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 (252) hide show
  1. package/CHANGELOG.md +49 -0
  2. package/README.md +729 -232
  3. package/docs/design-previews/pt.squarespace.com-homepage.html +889 -0
  4. package/docs/integrations/sdlc-genius-boundary.md +76 -0
  5. package/docs/integrations/sdlc-genius-eval-matrix.md +75 -0
  6. package/docs/integrations/sdlc-genius-install-checklist.md +93 -0
  7. package/docs/integrations/sdlc-genius-review-samples.md +86 -0
  8. package/docs/pt/README.md +3 -0
  9. package/docs/pt/agentes.md +1 -0
  10. package/docs/pt/comandos-cli.md +888 -2
  11. package/docs/pt/design-hybrid-forge.md +255 -6
  12. package/docs/pt/devlog-pipeline.md +270 -0
  13. package/docs/pt/fluxo-artefatos.md +178 -0
  14. package/docs/pt/hooks-session-guard.md +454 -0
  15. package/docs/pt/monitor-de-contexto.md +59 -5
  16. package/docs/pt/sdd-automation-scripts.md +557 -0
  17. package/docs/pt/site-forge.md +309 -0
  18. package/docs/pt/spec-learnings-pipeline.md +265 -0
  19. package/package.json +1 -1
  20. package/src/a2a/client.js +165 -0
  21. package/src/a2a/server.js +223 -0
  22. package/src/cli.js +235 -1
  23. package/src/commands/agent-audit.js +397 -0
  24. package/src/commands/agent-export-skill.js +229 -0
  25. package/src/commands/artifact-validate.js +189 -0
  26. package/src/commands/brief-gen.js +405 -0
  27. package/src/commands/brief-validate.js +65 -0
  28. package/src/commands/classify.js +256 -0
  29. package/src/commands/context-compact.js +49 -0
  30. package/src/commands/context-health.js +175 -0
  31. package/src/commands/context-monitor.js +71 -0
  32. package/src/commands/context-trim.js +177 -0
  33. package/src/commands/detect-test-runner.js +55 -0
  34. package/src/commands/devlog-export-brains.js +27 -0
  35. package/src/commands/devlog-process.js +292 -0
  36. package/src/commands/devlog-watch.js +131 -0
  37. package/src/commands/feature-close.js +165 -0
  38. package/src/commands/gate-check.js +228 -0
  39. package/src/commands/hooks-emit.js +253 -0
  40. package/src/commands/hooks-install.js +347 -0
  41. package/src/commands/learning-auto-promote.js +195 -0
  42. package/src/commands/learning-evolve.js +18 -9
  43. package/src/commands/learning-export.js +103 -0
  44. package/src/commands/learning-rollback.js +164 -0
  45. package/src/commands/live.js +25 -1
  46. package/src/commands/pattern-detect.js +33 -0
  47. package/src/commands/preflight-context.js +30 -0
  48. package/src/commands/preflight.js +208 -0
  49. package/src/commands/pulse-update.js +130 -0
  50. package/src/commands/runner-daemon.js +274 -0
  51. package/src/commands/runner-plan.js +70 -0
  52. package/src/commands/runner-queue-from-plan.js +166 -0
  53. package/src/commands/runner-queue.js +189 -0
  54. package/src/commands/runner-run.js +129 -0
  55. package/src/commands/runtime.js +47 -1
  56. package/src/commands/self-implement-loop.js +256 -0
  57. package/src/commands/session-guard.js +218 -0
  58. package/src/commands/sizing.js +165 -0
  59. package/src/commands/skill.js +65 -0
  60. package/src/commands/spec-checkpoint.js +177 -0
  61. package/src/commands/spec-status.js +79 -0
  62. package/src/commands/spec-sync.js +190 -0
  63. package/src/commands/spec-tasks.js +288 -0
  64. package/src/commands/squad-autorun.js +1220 -0
  65. package/src/commands/squad-bus.js +217 -0
  66. package/src/commands/squad-card.js +149 -0
  67. package/src/commands/squad-daemon.js +134 -0
  68. package/src/commands/squad-dependency-graph.js +164 -0
  69. package/src/commands/squad-review.js +106 -0
  70. package/src/commands/squad-scaffold.js +55 -0
  71. package/src/commands/squad-tool-register.js +157 -0
  72. package/src/commands/state-save.js +122 -0
  73. package/src/commands/update.js +2 -0
  74. package/src/commands/verify-gate.js +572 -0
  75. package/src/commands/workflow-execute.js +241 -0
  76. package/src/constants.js +9 -0
  77. package/src/install-profile.js +2 -2
  78. package/src/install-wizard.js +3 -2
  79. package/src/installer.js +6 -0
  80. package/src/lib/health-check.js +158 -0
  81. package/src/lib/hook-protocol.js +76 -0
  82. package/src/mcp/apps/squad-dashboard/app.js +163 -0
  83. package/src/mcp/apps/squad-dashboard/index.html +261 -0
  84. package/src/mcp/apps/squad-dashboard/mcp-manifest.json +23 -0
  85. package/src/mcp/resources/squad-state.js +130 -0
  86. package/src/preflight-engine.js +443 -0
  87. package/src/runner/cascade.js +97 -0
  88. package/src/runner/cli-launcher.js +109 -0
  89. package/src/runner/plan-importer.js +63 -0
  90. package/src/runner/queue-store.js +159 -0
  91. package/src/runtime-store.js +61 -3
  92. package/src/squad/agent-teams-adapter.js +264 -0
  93. package/src/squad/brief-validator.js +350 -0
  94. package/src/squad/bus-bridge.js +140 -0
  95. package/src/squad/context-compactor.js +265 -0
  96. package/src/squad/cross-ai-synthesizer.js +250 -0
  97. package/src/squad/hooks-generator.js +196 -0
  98. package/src/squad/inter-squad-events.js +175 -0
  99. package/src/squad/intra-bus.js +345 -0
  100. package/src/squad/learning-extractor.js +213 -0
  101. package/src/squad/pattern-detector.js +365 -0
  102. package/src/squad/preflight-context.js +296 -0
  103. package/src/squad/recovery-context.js +242 -71
  104. package/src/squad/reflection.js +365 -0
  105. package/src/squad/squad-scaffold.js +177 -0
  106. package/src/squad/state-manager.js +310 -0
  107. package/src/squad/task-decomposer.js +652 -0
  108. package/src/squad/verify-gate.js +303 -0
  109. package/src/updater.js +4 -5
  110. package/src/worker-runner.js +186 -1
  111. package/template/.aioson/agents/analyst.md +62 -1
  112. package/template/.aioson/agents/architect.md +61 -1
  113. package/template/.aioson/agents/design-hybrid-forge.md +14 -0
  114. package/template/.aioson/agents/dev.md +242 -24
  115. package/template/.aioson/agents/deyvin.md +66 -8
  116. package/template/.aioson/agents/discovery-design-doc.md +44 -0
  117. package/template/.aioson/agents/genome.md +14 -0
  118. package/template/.aioson/agents/neo.md +78 -1
  119. package/template/.aioson/agents/orache.md +50 -4
  120. package/template/.aioson/agents/orchestrator.md +197 -1
  121. package/template/.aioson/agents/pm.md +35 -0
  122. package/template/.aioson/agents/product.md +50 -5
  123. package/template/.aioson/agents/profiler-enricher.md +14 -0
  124. package/template/.aioson/agents/profiler-forge.md +14 -0
  125. package/template/.aioson/agents/profiler-researcher.md +14 -0
  126. package/template/.aioson/agents/qa.md +172 -21
  127. package/template/.aioson/agents/setup.md +79 -9
  128. package/template/.aioson/agents/sheldon.md +131 -6
  129. package/template/.aioson/agents/site-forge.md +1753 -0
  130. package/template/.aioson/agents/squad.md +162 -0
  131. package/template/.aioson/agents/tester.md +53 -0
  132. package/template/.aioson/agents/ux-ui.md +34 -1
  133. package/template/.aioson/brains/README.md +128 -0
  134. package/template/.aioson/brains/_index.json +16 -0
  135. package/template/.aioson/brains/scripts/query.js +103 -0
  136. package/template/.aioson/brains/site-forge/visual-patterns.brain.json +205 -0
  137. package/template/.aioson/config.md +143 -13
  138. package/template/.aioson/constitution.md +33 -0
  139. package/template/.aioson/context/project-pulse.md +34 -0
  140. package/template/.aioson/docs/LAYERS.md +79 -0
  141. package/template/.aioson/docs/README.md +76 -0
  142. package/template/.aioson/docs/example-external-api-context.md +72 -0
  143. package/template/.aioson/locales/en/agents/architect.md +17 -0
  144. package/template/.aioson/locales/en/agents/dev.md +79 -13
  145. package/template/.aioson/locales/en/agents/orache.md +6 -0
  146. package/template/.aioson/locales/en/agents/orchestrator.md +24 -0
  147. package/template/.aioson/locales/en/agents/product.md +50 -0
  148. package/template/.aioson/locales/en/agents/sheldon.md +115 -0
  149. package/template/.aioson/locales/en/agents/squad.md +14 -0
  150. package/template/.aioson/locales/en/agents/tester.md +6 -0
  151. package/template/.aioson/locales/es/agents/analyst.md +2 -0
  152. package/template/.aioson/locales/es/agents/architect.md +19 -0
  153. package/template/.aioson/locales/es/agents/dev.md +64 -4
  154. package/template/.aioson/locales/es/agents/deyvin.md +2 -0
  155. package/template/.aioson/locales/es/agents/discovery-design-doc.md +2 -0
  156. package/template/.aioson/locales/es/agents/genome.md +2 -0
  157. package/template/.aioson/locales/es/agents/neo.md +2 -0
  158. package/template/.aioson/locales/es/agents/orache.md +2 -0
  159. package/template/.aioson/locales/es/agents/orchestrator.md +26 -0
  160. package/template/.aioson/locales/es/agents/pair.md +2 -0
  161. package/template/.aioson/locales/es/agents/pm.md +2 -0
  162. package/template/.aioson/locales/es/agents/product.md +52 -0
  163. package/template/.aioson/locales/es/agents/profiler-enricher.md +2 -0
  164. package/template/.aioson/locales/es/agents/profiler-forge.md +2 -0
  165. package/template/.aioson/locales/es/agents/profiler-researcher.md +2 -0
  166. package/template/.aioson/locales/es/agents/qa.md +2 -0
  167. package/template/.aioson/locales/es/agents/setup.md +2 -0
  168. package/template/.aioson/locales/es/agents/sheldon.md +117 -0
  169. package/template/.aioson/locales/es/agents/squad.md +16 -0
  170. package/template/.aioson/locales/es/agents/tester.md +9 -0
  171. package/template/.aioson/locales/es/agents/ux-ui.md +2 -0
  172. package/template/.aioson/locales/fr/agents/analyst.md +2 -0
  173. package/template/.aioson/locales/fr/agents/architect.md +19 -0
  174. package/template/.aioson/locales/fr/agents/dev.md +64 -4
  175. package/template/.aioson/locales/fr/agents/deyvin.md +2 -0
  176. package/template/.aioson/locales/fr/agents/discovery-design-doc.md +2 -0
  177. package/template/.aioson/locales/fr/agents/genome.md +2 -0
  178. package/template/.aioson/locales/fr/agents/neo.md +2 -0
  179. package/template/.aioson/locales/fr/agents/orache.md +2 -0
  180. package/template/.aioson/locales/fr/agents/orchestrator.md +26 -0
  181. package/template/.aioson/locales/fr/agents/pair.md +2 -0
  182. package/template/.aioson/locales/fr/agents/pm.md +2 -0
  183. package/template/.aioson/locales/fr/agents/product.md +52 -0
  184. package/template/.aioson/locales/fr/agents/profiler-enricher.md +2 -0
  185. package/template/.aioson/locales/fr/agents/profiler-forge.md +2 -0
  186. package/template/.aioson/locales/fr/agents/profiler-researcher.md +2 -0
  187. package/template/.aioson/locales/fr/agents/qa.md +2 -0
  188. package/template/.aioson/locales/fr/agents/setup.md +2 -0
  189. package/template/.aioson/locales/fr/agents/sheldon.md +117 -0
  190. package/template/.aioson/locales/fr/agents/squad.md +16 -0
  191. package/template/.aioson/locales/fr/agents/tester.md +9 -0
  192. package/template/.aioson/locales/fr/agents/ux-ui.md +2 -0
  193. package/template/.aioson/locales/pt-BR/agents/analyst.md +64 -3
  194. package/template/.aioson/locales/pt-BR/agents/architect.md +42 -0
  195. package/template/.aioson/locales/pt-BR/agents/dev.md +147 -14
  196. package/template/.aioson/locales/pt-BR/agents/deyvin.md +47 -0
  197. package/template/.aioson/locales/pt-BR/agents/neo.md +62 -1
  198. package/template/.aioson/locales/pt-BR/agents/orchestrator.md +158 -2
  199. package/template/.aioson/locales/pt-BR/agents/pm.md +95 -1
  200. package/template/.aioson/locales/pt-BR/agents/product.md +145 -18
  201. package/template/.aioson/locales/pt-BR/agents/qa.md +16 -0
  202. package/template/.aioson/locales/pt-BR/agents/setup.md +101 -18
  203. package/template/.aioson/locales/pt-BR/agents/sheldon.md +132 -1
  204. package/template/.aioson/locales/pt-BR/agents/squad.md +14 -0
  205. package/template/.aioson/locales/pt-BR/agents/tester.md +449 -0
  206. package/template/.aioson/rules/README.md +69 -0
  207. package/template/.aioson/rules/data-format-convention.md +136 -0
  208. package/template/.aioson/rules/example-monetary-values.md +30 -0
  209. package/template/.aioson/schemas/squad-manifest.schema.json +124 -3
  210. package/template/.aioson/skills/design/pt.squarespace.com/.skill-meta.json +31 -0
  211. package/template/.aioson/skills/design/pt.squarespace.com/SKILL.md +66 -0
  212. package/template/.aioson/skills/design/pt.squarespace.com/references/components.md +368 -0
  213. package/template/.aioson/skills/design/pt.squarespace.com/references/design-tokens.md +150 -0
  214. package/template/.aioson/skills/design/pt.squarespace.com/references/motion.md +270 -0
  215. package/template/.aioson/skills/design/pt.squarespace.com/references/patterns.md +189 -0
  216. package/template/.aioson/skills/design/pt.squarespace.com/references/websites.md +165 -0
  217. package/template/.aioson/skills/process/aioson-spec-driven/SKILL.md +1 -0
  218. package/template/.aioson/skills/process/aioson-spec-driven/references/analyst.md +30 -0
  219. package/template/.aioson/skills/process/aioson-spec-driven/references/architect.md +23 -0
  220. package/template/.aioson/skills/process/aioson-spec-driven/references/dev.md +47 -0
  221. package/template/.aioson/skills/process/aioson-spec-driven/references/deyvin.md +27 -0
  222. package/template/.aioson/skills/process/aioson-spec-driven/references/maintenance-and-state.md +35 -0
  223. package/template/.aioson/skills/process/aioson-spec-driven/references/product.md +25 -0
  224. package/template/.aioson/skills/process/aioson-spec-driven/references/qa.md +30 -0
  225. package/template/.aioson/skills/process/aioson-spec-driven/references/sheldon.md +25 -0
  226. package/template/.aioson/skills/process/design-hybrid-forge/SKILL.md +4 -1
  227. package/template/.aioson/skills/process/design-hybrid-forge/references/output-contract.md +15 -0
  228. package/template/.aioson/skills/process/design-hybrid-forge/references/pair-compatibility.md +32 -0
  229. package/template/.aioson/skills/process/design-hybrid-forge/references/quality-gates.md +20 -0
  230. package/template/.aioson/skills/process/simplify/SKILL.md +173 -0
  231. package/template/.aioson/skills/static/context-budget-guide.md +46 -0
  232. package/template/.aioson/skills/static/harness-sensors.md +74 -0
  233. package/template/.aioson/skills/static/multi-agent-patterns.md +43 -0
  234. package/template/.aioson/skills/static/react-motion-patterns.md +22 -0
  235. package/template/.aioson/skills/static/static-html-patterns/checklists.md +43 -0
  236. package/template/.aioson/skills/static/static-html-patterns/css-tokens.md +609 -0
  237. package/template/.aioson/skills/static/static-html-patterns/motion.md +193 -0
  238. package/template/.aioson/skills/static/static-html-patterns/premium.md +711 -0
  239. package/template/.aioson/skills/static/static-html-patterns/structure.md +209 -0
  240. package/template/.aioson/skills/static/static-html-patterns/utilities.md +190 -0
  241. package/template/.aioson/skills/static/static-html-patterns.md +58 -1913
  242. package/template/.aioson/skills/static/threejs-patterns.md +929 -0
  243. package/template/.aioson/skills/static/web-research-cache.md +112 -0
  244. package/template/.aioson/tasks/implementation-plan.md +21 -1
  245. package/template/.claude/commands/aioson/agent/design-hybrid-forge.md +5 -0
  246. package/template/.claude/commands/aioson/agent/orache.md +5 -0
  247. package/template/.claude/commands/aioson/agent/sheldon.md +5 -0
  248. package/template/.claude/commands/aioson/agent/site-forge.md +5 -0
  249. package/template/AGENTS.md +55 -3
  250. package/template/CLAUDE.md +30 -0
  251. package/template/OPENCODE.md +4 -0
  252. package/template/researchs/.gitkeep +0 -0
@@ -0,0 +1,217 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * aioson squad:bus — Intra-squad message bus CLI
5
+ *
6
+ * Subcommands:
7
+ * post — Post a message to the bus
8
+ * read — Read messages from the bus
9
+ * watch — Watch for new messages in real time (polls)
10
+ * summary — Show bus activity summary for a session
11
+ * clear — Delete the bus file for a session
12
+ * list — List sessions with active bus files
13
+ *
14
+ * Usage:
15
+ * aioson squad:bus . --squad=content-team post --from=researcher --type=finding --content="..."
16
+ * aioson squad:bus . --squad=content-team read --session=SESSION_ID [--to=writer] [--type=finding]
17
+ * aioson squad:bus . --squad=content-team watch --session=SESSION_ID [--to=writer]
18
+ * aioson squad:bus . --squad=content-team summary --session=SESSION_ID
19
+ * aioson squad:bus . --squad=content-team clear --session=SESSION_ID
20
+ * aioson squad:bus . --squad=content-team list
21
+ */
22
+
23
+ const path = require('node:path');
24
+ const bus = require('../squad/intra-bus');
25
+
26
+ function formatMessage(msg, { compact = false } = {}) {
27
+ if (compact) {
28
+ return `[${msg.ts.slice(0, 19)}] ${msg.from}→${msg.to} (${msg.type}): ${String(msg.content).slice(0, 120)}`;
29
+ }
30
+ return [
31
+ `id: ${msg.id}`,
32
+ `from: ${msg.from}`,
33
+ `to: ${msg.to}`,
34
+ `type: ${msg.type}`,
35
+ `ts: ${msg.ts}`,
36
+ `content: ${msg.content}`,
37
+ msg.metadata && Object.keys(msg.metadata).length > 0
38
+ ? `meta: ${JSON.stringify(msg.metadata)}`
39
+ : null
40
+ ].filter(Boolean).join('\n');
41
+ }
42
+
43
+ async function runSquadBus({ args, options = {}, logger }) {
44
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
45
+ const sub = String(options.sub || args[1] || 'read').trim();
46
+ const squadSlug = String(options.squad || options.s || '').trim();
47
+ const sessionId = String(options.session || '').trim();
48
+
49
+ if (!squadSlug) {
50
+ logger.error('Error: --squad is required');
51
+ return { ok: false, error: 'missing_squad' };
52
+ }
53
+
54
+ // ── POST ──────────────────────────────────────────────────────────────────
55
+ if (sub === 'post') {
56
+ const from = String(options.from || '').trim();
57
+ const to = String(options.to || '*').trim();
58
+ const type = String(options.type || 'finding').trim();
59
+ const content = String(options.content || options.message || '').trim();
60
+ const sid = sessionId || String(options.s || 'default').trim();
61
+
62
+ if (!from) { logger.error('Error: --from is required for post'); return { ok: false }; }
63
+ if (!content) { logger.error('Error: --content is required for post'); return { ok: false }; }
64
+
65
+ let metadata = {};
66
+ if (options.meta) {
67
+ try { metadata = JSON.parse(options.meta); } catch { /* ignore */ }
68
+ }
69
+
70
+ const msg = await bus.post(targetDir, squadSlug, sid, { from, to, type, content, metadata });
71
+
72
+ if (options.json) return { ok: true, message: msg };
73
+
74
+ logger.log(`✓ Posted to bus [${sid}]`);
75
+ logger.log(formatMessage(msg));
76
+ return { ok: true, message: msg };
77
+ }
78
+
79
+ // ── READ ──────────────────────────────────────────────────────────────────
80
+ if (sub === 'read') {
81
+ if (!sessionId) { logger.error('Error: --session is required for read'); return { ok: false }; }
82
+
83
+ const filters = {
84
+ from: options.from || undefined,
85
+ to: options.to || undefined,
86
+ type: options.type || undefined,
87
+ since: options.since || undefined,
88
+ last: options.last ? Number(options.last) : undefined
89
+ };
90
+
91
+ const messages = await bus.read(targetDir, squadSlug, sessionId, filters);
92
+
93
+ if (options.json) return { ok: true, messages };
94
+
95
+ if (messages.length === 0) {
96
+ logger.log('No messages found.');
97
+ return { ok: true, messages: [] };
98
+ }
99
+
100
+ logger.log(`Bus [${sessionId}] — ${messages.length} message(s):`);
101
+ logger.log('─'.repeat(60));
102
+ for (const msg of messages) {
103
+ logger.log(formatMessage(msg, { compact: !!options.compact }));
104
+ logger.log('─'.repeat(60));
105
+ }
106
+ return { ok: true, messages };
107
+ }
108
+
109
+ // ── WATCH ─────────────────────────────────────────────────────────────────
110
+ if (sub === 'watch') {
111
+ if (!sessionId) { logger.error('Error: --session is required for watch'); return { ok: false }; }
112
+
113
+ const pollMs = options.poll ? Number(options.poll) : 1500;
114
+ const timeoutMs = options.timeout ? Number(options.timeout) * 1000 : 10 * 60 * 1000; // 10m default
115
+
116
+ logger.log(`Watching bus [${sessionId}] (Ctrl+C to stop, timeout ${Math.round(timeoutMs / 60000)}m)...`);
117
+
118
+ const filters = {
119
+ to: options.to || undefined,
120
+ type: options.type || undefined
121
+ };
122
+
123
+ let count = 0;
124
+ const stop = bus.watch(targetDir, squadSlug, sessionId, (msg) => {
125
+ count++;
126
+ logger.log(formatMessage(msg, { compact: false }));
127
+ logger.log('─'.repeat(60));
128
+ }, { pollMs, timeoutMs, ...filters });
129
+
130
+ await new Promise((resolve) => {
131
+ process.on('SIGINT', resolve);
132
+ process.on('SIGTERM', resolve);
133
+ setTimeout(resolve, timeoutMs);
134
+ });
135
+
136
+ stop();
137
+ logger.log(`Watch ended. ${count} message(s) received.`);
138
+ return { ok: true, count };
139
+ }
140
+
141
+ // ── SUMMARY ───────────────────────────────────────────────────────────────
142
+ if (sub === 'summary') {
143
+ if (!sessionId) { logger.error('Error: --session is required for summary'); return { ok: false }; }
144
+
145
+ const s = await bus.summary(targetDir, squadSlug, sessionId);
146
+
147
+ if (options.json) return { ok: true, summary: s };
148
+
149
+ logger.log(`Bus summary [${sessionId}]`);
150
+ logger.log('─'.repeat(50));
151
+ logger.log(`Total messages : ${s.total}`);
152
+ if (s.total === 0) {
153
+ logger.log('No messages yet.');
154
+ return { ok: true, summary: s };
155
+ }
156
+ logger.log(`First message : ${s.first_ts}`);
157
+ logger.log(`Last message : ${s.last_ts}`);
158
+ logger.log('');
159
+ logger.log('By type:');
160
+ for (const [type, count] of Object.entries(s.by_type)) {
161
+ logger.log(` ${type.padEnd(12)} ${count}`);
162
+ }
163
+ logger.log('');
164
+ logger.log('By executor:');
165
+ for (const [exec, count] of Object.entries(s.by_executor)) {
166
+ logger.log(` ${exec.padEnd(20)} ${count}`);
167
+ }
168
+ if (s.blocks.length > 0) {
169
+ logger.log('');
170
+ logger.log(`⚠ Blocks (${s.blocks.length}):`);
171
+ for (const b of s.blocks) {
172
+ logger.log(` [${b.ts.slice(0, 19)}] ${b.from}: ${b.content}`);
173
+ }
174
+ }
175
+ return { ok: true, summary: s };
176
+ }
177
+
178
+ // ── CLEAR ─────────────────────────────────────────────────────────────────
179
+ if (sub === 'clear') {
180
+ if (!sessionId) { logger.error('Error: --session is required for clear'); return { ok: false }; }
181
+
182
+ const result = await bus.clear(targetDir, squadSlug, sessionId);
183
+
184
+ if (options.json) return result;
185
+
186
+ if (result.ok) {
187
+ logger.log(`✓ Bus cleared for session [${sessionId}]`);
188
+ } else {
189
+ logger.log(`No bus file found for session [${sessionId}]`);
190
+ }
191
+ return result;
192
+ }
193
+
194
+ // ── LIST ──────────────────────────────────────────────────────────────────
195
+ if (sub === 'list') {
196
+ const sessions = await bus.listSessions(targetDir, squadSlug);
197
+
198
+ if (options.json) return { ok: true, sessions };
199
+
200
+ if (sessions.length === 0) {
201
+ logger.log('No bus sessions found.');
202
+ return { ok: true, sessions: [] };
203
+ }
204
+
205
+ logger.log(`Bus sessions for squad "${squadSlug}" (${sessions.length}):`);
206
+ logger.log('─'.repeat(60));
207
+ for (const s of sessions) {
208
+ logger.log(` ${s.session_id.padEnd(36)} ${(s.size_bytes / 1024).toFixed(1)}KB ${s.modified_at.slice(0, 19)}`);
209
+ }
210
+ return { ok: true, sessions };
211
+ }
212
+
213
+ logger.error(`Unknown subcommand: ${sub}. Valid: post, read, watch, summary, clear, list`);
214
+ return { ok: false, error: 'unknown_subcommand' };
215
+ }
216
+
217
+ module.exports = { runSquadBus };
@@ -0,0 +1,149 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * aioson squad:card — Generate A2A Agent Card from squad manifest
5
+ *
6
+ * Creates a Google A2A v1.0 compatible Agent Card that describes a squad's
7
+ * capabilities, enabling discovery by external A2A-compatible agents.
8
+ *
9
+ * Usage:
10
+ * aioson squad:card . --squad=content-team
11
+ * aioson squad:card . --squad=content-team --output=.well-known/agent.json
12
+ * aioson squad:card . --squad=content-team --port=3847 --json
13
+ */
14
+
15
+ const fs = require('node:fs/promises');
16
+ const path = require('node:path');
17
+
18
+ const SQUADS_DIR = path.join('.aioson', 'squads');
19
+
20
+ /**
21
+ * Read squad manifest.
22
+ */
23
+ async function readManifest(projectDir, squadSlug) {
24
+ const manifestPath = path.join(projectDir, SQUADS_DIR, squadSlug, 'squad.manifest.json');
25
+ return JSON.parse(await fs.readFile(manifestPath, 'utf8'));
26
+ }
27
+
28
+ /**
29
+ * Generate an A2A Agent Card from a squad manifest.
30
+ *
31
+ * A2A Agent Card spec (v1.0):
32
+ * - name, description, url, version
33
+ * - capabilities: streaming, pushNotifications
34
+ * - skills[]: id, name, description
35
+ * - defaultInputModes, defaultOutputModes
36
+ */
37
+ function generateAgentCard(manifest, options = {}) {
38
+ const { port = 3847, host = 'localhost' } = options;
39
+ const baseUrl = `http://${host}:${port}/a2a/${manifest.slug}`;
40
+
41
+ // Map executors to A2A skills
42
+ const skills = (manifest.executors || []).map((executor) => ({
43
+ id: executor.slug,
44
+ name: executor.title || executor.slug,
45
+ description: executor.role || `Executor: ${executor.slug}`
46
+ }));
47
+
48
+ // Add workflow-level skills
49
+ if (manifest.workflows) {
50
+ for (const wf of manifest.workflows) {
51
+ skills.push({
52
+ id: `workflow-${wf.slug}`,
53
+ name: wf.title || wf.slug,
54
+ description: `Workflow: ${wf.title || wf.slug}`
55
+ });
56
+ }
57
+ }
58
+
59
+ const card = {
60
+ name: manifest.name || manifest.slug,
61
+ description: manifest.mission || manifest.goal || `AIOSON Squad: ${manifest.slug}`,
62
+ url: baseUrl,
63
+ version: manifest.schemaVersion || '1.0.0',
64
+ provider: {
65
+ organization: 'AIOSON',
66
+ url: 'https://aiosforge.dev'
67
+ },
68
+ capabilities: {
69
+ streaming: true,
70
+ pushNotifications: true,
71
+ stateTransitionHistory: true
72
+ },
73
+ authentication: {
74
+ schemes: ['none']
75
+ },
76
+ skills,
77
+ defaultInputModes: ['text/plain', 'application/json'],
78
+ defaultOutputModes: ['text/plain', 'application/json']
79
+ };
80
+
81
+ // Add port information
82
+ if (manifest.ports) {
83
+ if (manifest.ports.inputs) {
84
+ card.inputPorts = manifest.ports.inputs.map((p) => ({
85
+ key: p.key,
86
+ dataType: p.dataType || 'any',
87
+ description: p.description,
88
+ required: p.required || false
89
+ }));
90
+ }
91
+ if (manifest.ports.outputs) {
92
+ card.outputPorts = manifest.ports.outputs.map((p) => ({
93
+ key: p.key,
94
+ dataType: p.dataType || 'any',
95
+ description: p.description
96
+ }));
97
+ }
98
+ }
99
+
100
+ return card;
101
+ }
102
+
103
+ /**
104
+ * CLI handler.
105
+ */
106
+ async function runSquadCard({ args, options = {}, logger }) {
107
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
108
+ const squadSlug = String(options.squad || options.s || '').trim();
109
+
110
+ if (!squadSlug) {
111
+ logger.error('Error: --squad is required');
112
+ return { ok: false, error: 'missing_squad' };
113
+ }
114
+
115
+ let manifest;
116
+ try {
117
+ manifest = await readManifest(targetDir, squadSlug);
118
+ } catch (err) {
119
+ logger.error(`Error reading manifest: ${err.message}`);
120
+ return { ok: false, error: 'manifest_not_found' };
121
+ }
122
+
123
+ const port = Number(options.port || 3847);
124
+ const host = options.host || 'localhost';
125
+ const card = generateAgentCard(manifest, { port, host });
126
+
127
+ // Write to output path
128
+ const outputPath = options.output
129
+ ? path.resolve(targetDir, options.output)
130
+ : path.join(targetDir, '.well-known', `agent-${squadSlug}.json`);
131
+
132
+ await fs.mkdir(path.dirname(outputPath), { recursive: true });
133
+ await fs.writeFile(outputPath, JSON.stringify(card, null, 2), 'utf8');
134
+
135
+ if (options.json) return card;
136
+
137
+ logger.log(`A2A Agent Card for "${manifest.name || squadSlug}":`);
138
+ logger.log(` URL: ${card.url}`);
139
+ logger.log(` Skills: ${card.skills.length}`);
140
+ for (const s of card.skills) {
141
+ logger.log(` - ${s.id}: ${s.description}`);
142
+ }
143
+ logger.log('');
144
+ logger.log(`Output: ${path.relative(targetDir, outputPath)}`);
145
+
146
+ return { ok: true, card, outputPath: path.relative(targetDir, outputPath) };
147
+ }
148
+
149
+ module.exports = { runSquadCard, generateAgentCard };
@@ -4,6 +4,7 @@ const fs = require('node:fs/promises');
4
4
  const path = require('node:path');
5
5
  const { SquadDaemon } = require('../squad-daemon');
6
6
  const { openRuntimeDb } = require('../runtime-store');
7
+ const { consume: consumeInterSquadEvents } = require('../squad/inter-squad-events');
7
8
 
8
9
  async function handleStart(projectDir, squadSlug, options, { logger, t }) {
9
10
  if (!squadSlug) {
@@ -186,11 +187,144 @@ async function handleLogs(projectDir, squadSlug, { logger, t }) {
186
187
  }
187
188
  }
188
189
 
190
+ // ─── Persistent Execution Loop (4.3) ─────────────────────────────────────────
191
+
192
+ /**
193
+ * Parse a loop-delay string like '30s', '2m', '1h' into milliseconds.
194
+ */
195
+ function parseLoopDelay(str, defaultMs = 30_000) {
196
+ const s = String(str || '').trim().toLowerCase();
197
+ const match = s.match(/^(\d+)(s|m|h)?$/);
198
+ if (!match) return defaultMs;
199
+ const val = parseInt(match[1], 10);
200
+ const unit = match[2] || 's';
201
+ if (unit === 'h') return val * 3_600_000;
202
+ if (unit === 'm') return val * 60_000;
203
+ return val * 1_000;
204
+ }
205
+
206
+ /**
207
+ * Persistent daemon loop for a squad.
208
+ *
209
+ * Each iteration:
210
+ * 1. Check inter_squad_events for this squad (via manifest subscriptions)
211
+ * 2. If pending events: trigger squad:autorun to process them
212
+ * 3. Write daemon-alive.json heartbeat
213
+ * 4. Sleep loop-delay
214
+ *
215
+ * Exits gracefully on SIGTERM (waits for current iteration to finish).
216
+ */
217
+ async function handlePersistent(projectDir, squadSlug, options, { logger }) {
218
+ if (!squadSlug) {
219
+ logger.error('Error: --squad is required for --persistent');
220
+ return { ok: false, error: 'missing_squad' };
221
+ }
222
+
223
+ const loopDelayMs = parseLoopDelay(options['loop-delay'] || options.loopDelay, 30_000);
224
+
225
+ // Load manifest for subscriptions
226
+ let manifest = {};
227
+ try {
228
+ const manifestPath = path.join(projectDir, '.aioson', 'squads', squadSlug, 'squad.manifest.json');
229
+ manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8'));
230
+ } catch { /* manifest optional */ }
231
+
232
+ const subscriptions = [
233
+ ...(manifest.subscriptions || []),
234
+ ...(manifest.depends_on || []).map((d) => d.event).filter(Boolean)
235
+ ];
236
+
237
+ const aliveJsonPath = path.join(projectDir, '.aioson', 'squads', squadSlug, 'daemon-alive.json');
238
+
239
+ logger.log(`Persistent daemon — squad: ${squadSlug}`);
240
+ logger.log(`Loop delay: ${loopDelayMs / 1000}s`);
241
+ logger.log(`Subscriptions: ${subscriptions.length > 0 ? subscriptions.join(', ') : '(none)'}`);
242
+ logger.log('Press Ctrl+C to stop');
243
+ logger.log('');
244
+
245
+ let running = true;
246
+ process.on('SIGTERM', () => { running = false; });
247
+ process.on('SIGINT', () => { running = false; });
248
+
249
+ let iteration = 0;
250
+
251
+ while (running) {
252
+ iteration++;
253
+ const now = new Date().toISOString();
254
+
255
+ // Write heartbeat
256
+ await fs.mkdir(path.dirname(aliveJsonPath), { recursive: true });
257
+ await fs.writeFile(aliveJsonPath, JSON.stringify({
258
+ squad: squadSlug,
259
+ iteration,
260
+ last_check: now,
261
+ subscriptions,
262
+ pid: process.pid
263
+ }, null, 2), 'utf8').catch(() => {});
264
+
265
+ // Check inter-squad events
266
+ if (subscriptions.length > 0) {
267
+ const events = await consumeInterSquadEvents(projectDir, {
268
+ toSquad: squadSlug,
269
+ subscriptions
270
+ }).catch(() => []);
271
+
272
+ if (events.length > 0) {
273
+ logger.log(`[${now.slice(11, 19)}] ${events.length} inter-squad event(s) received:`);
274
+ for (const ev of events) {
275
+ logger.log(` ← [${ev.fromSquad}] ${ev.event}`);
276
+ }
277
+ logger.log(` → Triggering squad:autorun for "${squadSlug}"...`);
278
+
279
+ // Trigger autorun via CLI subprocess
280
+ const { spawnSync } = require('node:child_process');
281
+ const goal = events.map((e) => `Process event: ${e.event} from ${e.fromSquad}`).join('; ');
282
+ const autorunResult = spawnSync('aioson', [
283
+ 'squad:autorun', projectDir,
284
+ `--squad=${squadSlug}`,
285
+ `--goal=${goal}`,
286
+ '--reflect'
287
+ ], {
288
+ encoding: 'utf8',
289
+ timeout: 300_000, // 5 min per autorun
290
+ stdio: 'pipe'
291
+ });
292
+
293
+ if (autorunResult.status === 0) {
294
+ logger.log(' ✓ Autorun completed');
295
+ } else {
296
+ logger.log(` ✗ Autorun exited ${autorunResult.status}: ${(autorunResult.stderr || '').trim().slice(0, 100)}`);
297
+ }
298
+ } else {
299
+ logger.log(`[${now.slice(11, 19)}] No pending events for "${squadSlug}" — sleeping ${loopDelayMs / 1000}s`);
300
+ }
301
+ } else {
302
+ logger.log(`[${now.slice(11, 19)}] Iteration ${iteration} — no subscriptions configured, heartbeat only`);
303
+ }
304
+
305
+ // Sleep loop-delay (interruptible)
306
+ if (running) {
307
+ await new Promise((resolve) => setTimeout(resolve, loopDelayMs));
308
+ }
309
+ }
310
+
311
+ logger.log('');
312
+ logger.log(`Persistent daemon stopped (${iteration} iteration(s) completed)`);
313
+ await fs.unlink(aliveJsonPath).catch(() => {});
314
+
315
+ return { ok: true, iterations: iteration };
316
+ }
317
+
189
318
  async function runSquadDaemon({ args, options, logger, t }) {
190
319
  const targetDir = path.resolve(process.cwd(), args[0] || '.');
191
320
  const sub = options.sub || 'status';
192
321
  const squadSlug = options.squad;
193
322
 
323
+ // --persistent flag triggers persistent loop mode regardless of sub
324
+ if (options.persistent) {
325
+ return handlePersistent(targetDir, squadSlug, options, { logger });
326
+ }
327
+
194
328
  switch (sub) {
195
329
  case 'start':
196
330
  return handleStart(targetDir, squadSlug, options, { logger, t });
@@ -0,0 +1,164 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * aioson squad:dependency-graph [projectDir]
5
+ *
6
+ * Reads squad manifests and renders a dependency graph showing which squads
7
+ * depend on outputs from other squads via `depends_on[]` in their manifest.
8
+ *
9
+ * Usage:
10
+ * aioson squad:dependency-graph .
11
+ * aioson squad:dependency-graph . --format=mermaid
12
+ * aioson squad:dependency-graph . --json
13
+ *
14
+ * Flags:
15
+ * --format Output format: ascii (default) | mermaid
16
+ * --json Return JSON with squads and edges
17
+ */
18
+
19
+ const fs = require('node:fs/promises');
20
+ const path = require('node:path');
21
+
22
+ async function loadSquadManifests(projectDir) {
23
+ const squadsDir = path.join(projectDir, '.aioson', 'squads');
24
+ let entries;
25
+ try {
26
+ entries = await fs.readdir(squadsDir, { withFileTypes: true });
27
+ } catch {
28
+ return [];
29
+ }
30
+
31
+ const manifests = [];
32
+ for (const entry of entries) {
33
+ if (!entry.isDirectory()) continue;
34
+ const manifestPath = path.join(squadsDir, entry.name, 'squad.manifest.json');
35
+ try {
36
+ const raw = JSON.parse(await fs.readFile(manifestPath, 'utf8'));
37
+ manifests.push(raw);
38
+ } catch { /* skip invalid or missing manifests */ }
39
+ }
40
+ return manifests;
41
+ }
42
+
43
+ function buildEdges(manifests) {
44
+ const edges = [];
45
+ for (const manifest of manifests) {
46
+ for (const dep of manifest.depends_on || []) {
47
+ edges.push({
48
+ from: dep.squad,
49
+ to: manifest.slug,
50
+ event: dep.event || '*',
51
+ inputMapping: dep.input_mapping || null
52
+ });
53
+ }
54
+ }
55
+ return edges;
56
+ }
57
+
58
+ function renderAscii(manifests, edges) {
59
+ const lines = [];
60
+ lines.push('Inter-Squad Dependency Graph');
61
+ lines.push('─'.repeat(44));
62
+
63
+ if (edges.length === 0) {
64
+ lines.push('(No inter-squad dependencies declared)');
65
+ lines.push('');
66
+ lines.push('Declare dependencies in your squad manifest:');
67
+ lines.push(' "depends_on": [');
68
+ lines.push(' { "squad": "content-team", "event": "episode.created",');
69
+ lines.push(' "input_mapping": { "file": "tasks[0].context.episode_file" } }');
70
+ lines.push(' ]');
71
+ } else {
72
+ for (const edge of edges) {
73
+ const mapping = edge.inputMapping
74
+ ? ` [maps: ${JSON.stringify(edge.inputMapping)}]`
75
+ : '';
76
+ lines.push(` [${edge.from}] ──(${edge.event})──▶ [${edge.to}]${mapping}`);
77
+ }
78
+ }
79
+
80
+ const involvedSlugs = new Set([
81
+ ...edges.map((e) => e.from),
82
+ ...edges.map((e) => e.to)
83
+ ]);
84
+ const isolated = manifests.filter((m) => !involvedSlugs.has(m.slug));
85
+ if (isolated.length > 0) {
86
+ lines.push('');
87
+ lines.push('Isolated squads (no declared dependencies):');
88
+ for (const m of isolated) {
89
+ lines.push(` [${m.slug}] ${m.mission ? '— ' + m.mission : ''}`);
90
+ }
91
+ }
92
+
93
+ return lines.join('\n');
94
+ }
95
+
96
+ function renderMermaid(manifests, edges) {
97
+ const lines = ['graph LR'];
98
+ const slugs = new Set(manifests.map((m) => m.slug));
99
+
100
+ // Add squad nodes with labels
101
+ for (const m of manifests) {
102
+ lines.push(` ${m.slug}["${m.name || m.slug}"]`);
103
+ }
104
+
105
+ // Add dependency nodes that might not have manifests in this project
106
+ for (const edge of edges) {
107
+ if (!slugs.has(edge.from)) {
108
+ lines.push(` ${edge.from}["${edge.from} (external)"]`);
109
+ }
110
+ }
111
+
112
+ lines.push('');
113
+
114
+ // Add edges
115
+ for (const edge of edges) {
116
+ lines.push(` ${edge.from} -->|"${edge.event}"| ${edge.to}`);
117
+ }
118
+
119
+ return lines.join('\n');
120
+ }
121
+
122
+ async function runSquadDependencyGraph({ args, options = {}, logger }) {
123
+ const projectDir = path.resolve(process.cwd(), args[0] || '.');
124
+ const format = String(options.format || 'ascii').trim().toLowerCase();
125
+
126
+ const manifests = await loadSquadManifests(projectDir);
127
+
128
+ if (manifests.length === 0) {
129
+ logger.log('No squad manifests found in .aioson/squads/');
130
+ logger.log('Create a squad first: aioson squad:create .');
131
+ return { ok: true, squads: 0, edges: [] };
132
+ }
133
+
134
+ const edges = buildEdges(manifests);
135
+
136
+ if (options.json) {
137
+ return {
138
+ ok: true,
139
+ squads: manifests.map((m) => ({ slug: m.slug, name: m.name, dependsOn: m.depends_on || [] })),
140
+ edges
141
+ };
142
+ }
143
+
144
+ logger.log('');
145
+
146
+ if (format === 'mermaid') {
147
+ logger.log(renderMermaid(manifests, edges));
148
+ } else {
149
+ logger.log(renderAscii(manifests, edges));
150
+ }
151
+
152
+ logger.log('');
153
+ logger.log(`${manifests.length} squad(s) · ${edges.length} dependency edge(s)`);
154
+ logger.log('');
155
+ if (edges.length > 0) {
156
+ logger.log('Tips:');
157
+ logger.log(' • Squads publish events via: aioson inter-squad:publish . --from=<squad> --event=<name> --payload=\'{"key":"val"}\'');
158
+ logger.log(' • Squads consume events automatically at the start of squad:autorun');
159
+ }
160
+
161
+ return { ok: true, squads: manifests.length, edges };
162
+ }
163
+
164
+ module.exports = { runSquadDependencyGraph };