@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,163 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Squad Dashboard App Logic — Plan 81, Phase 4.1
5
+ *
6
+ * Provides data fetching and rendering logic for the MCP App dashboard.
7
+ * Can run standalone (via HTTP API) or embedded in MCP App context.
8
+ */
9
+
10
+ const fs = require('node:fs/promises');
11
+ const path = require('node:path');
12
+
13
+ const SQUADS_DIR = path.join('.aioson', 'squads');
14
+
15
+ // ─── State readers ───────────────────────────────────────────────────────────
16
+
17
+ async function readSquadState(projectDir, squadSlug) {
18
+ const statePath = path.join(projectDir, SQUADS_DIR, squadSlug, 'STATE.md');
19
+ try {
20
+ const content = await fs.readFile(statePath, 'utf8');
21
+ // Parse frontmatter
22
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
23
+ if (!match) return { raw: content };
24
+
25
+ const meta = {};
26
+ for (const line of match[1].split('\n')) {
27
+ const kv = line.match(/^(\w[\w_]*):\s*(.+)$/);
28
+ if (kv) {
29
+ const val = kv[2].trim();
30
+ if (/^\d+(\.\d+)?$/.test(val)) meta[kv[1]] = Number(val);
31
+ else meta[kv[1]] = val.replace(/^["']|["']$/g, '');
32
+ }
33
+ }
34
+
35
+ return { ...meta, raw: content };
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+
41
+ async function readBusState(projectDir, squadSlug) {
42
+ // Find the most recent session
43
+ const sessionsDir = path.join(projectDir, SQUADS_DIR, squadSlug, 'sessions');
44
+ try {
45
+ const entries = await fs.readdir(sessionsDir, { withFileTypes: true });
46
+ const sessions = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort().reverse();
47
+
48
+ if (sessions.length === 0) return { messages: [], total: 0 };
49
+
50
+ const latestSession = sessions[0];
51
+ const busPath = path.join(sessionsDir, latestSession, 'bus.jsonl');
52
+ const raw = await fs.readFile(busPath, 'utf8');
53
+ const messages = raw.split('\n')
54
+ .filter(Boolean)
55
+ .map((line) => { try { return JSON.parse(line); } catch { return null; } })
56
+ .filter(Boolean);
57
+
58
+ // Classify
59
+ const blocks = messages.filter((m) => m.type === 'block');
60
+ const results = messages.filter((m) => m.type === 'result');
61
+ const byType = {};
62
+ const byExecutor = {};
63
+ for (const m of messages) {
64
+ byType[m.type] = (byType[m.type] || 0) + 1;
65
+ byExecutor[m.from] = (byExecutor[m.from] || 0) + 1;
66
+ }
67
+
68
+ return {
69
+ sessionId: latestSession,
70
+ total: messages.length,
71
+ blocks: blocks.length,
72
+ results: results.length,
73
+ byType,
74
+ byExecutor,
75
+ lastMessages: messages.slice(-20)
76
+ };
77
+ } catch {
78
+ return { messages: [], total: 0 };
79
+ }
80
+ }
81
+
82
+ async function readBudgetState(projectDir, squadSlug) {
83
+ const manifestPath = path.join(projectDir, SQUADS_DIR, squadSlug, 'squad.manifest.json');
84
+ try {
85
+ const manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8'));
86
+ return manifest.budget || { max_tokens_per_session: null, max_tokens_per_task: null };
87
+ } catch {
88
+ return {};
89
+ }
90
+ }
91
+
92
+ async function readWavesState(projectDir, squadSlug) {
93
+ const sessionsDir = path.join(projectDir, SQUADS_DIR, squadSlug, 'sessions');
94
+ try {
95
+ const entries = await fs.readdir(sessionsDir, { withFileTypes: true });
96
+ const sessions = entries.filter((e) => e.isDirectory()).map((e) => e.name).sort().reverse();
97
+
98
+ if (sessions.length === 0) return { waves: [] };
99
+
100
+ const planPath = path.join(sessionsDir, sessions[0], 'plan.json');
101
+ const plan = JSON.parse(await fs.readFile(planPath, 'utf8'));
102
+
103
+ const waves = [];
104
+ const groups = plan.parallel_groups || {};
105
+
106
+ for (const [groupNum, taskIds] of Object.entries(groups)) {
107
+ const waveTasks = taskIds.map((id) => {
108
+ const task = plan.tasks.find((t) => t.id === id);
109
+ return task ? {
110
+ id: task.id,
111
+ title: task.title,
112
+ executor: task.executor,
113
+ status: task.status || 'pending'
114
+ } : { id, status: 'unknown' };
115
+ });
116
+
117
+ const allDone = waveTasks.every((t) => t.status === 'completed');
118
+ const anyRunning = waveTasks.some((t) => t.status === 'in_progress');
119
+ const anyBlocked = waveTasks.some((t) => t.status === 'escalated' || t.status === 'failed');
120
+
121
+ waves.push({
122
+ wave: Number(groupNum),
123
+ status: allDone ? 'DONE' : anyRunning ? 'RUNNING' : anyBlocked ? 'BLOCKED' : 'PENDING',
124
+ tasks: waveTasks
125
+ });
126
+ }
127
+
128
+ return { sessionId: sessions[0], waves, goal: plan.goal };
129
+ } catch {
130
+ return { waves: [] };
131
+ }
132
+ }
133
+
134
+ // ─── Composite dashboard data ────────────────────────────────────────────────
135
+
136
+ /**
137
+ * Get complete dashboard data for a squad.
138
+ */
139
+ async function getDashboardData(projectDir, squadSlug) {
140
+ const [state, busState, budget, wavesState] = await Promise.all([
141
+ readSquadState(projectDir, squadSlug),
142
+ readBusState(projectDir, squadSlug),
143
+ readBudgetState(projectDir, squadSlug),
144
+ readWavesState(projectDir, squadSlug)
145
+ ]);
146
+
147
+ return {
148
+ squad: squadSlug,
149
+ timestamp: new Date().toISOString(),
150
+ state,
151
+ bus: busState,
152
+ budget,
153
+ waves: wavesState
154
+ };
155
+ }
156
+
157
+ module.exports = {
158
+ getDashboardData,
159
+ readSquadState,
160
+ readBusState,
161
+ readBudgetState,
162
+ readWavesState
163
+ };
@@ -0,0 +1,261 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Squad Dashboard — AIOSON</title>
7
+ <style>
8
+ :root {
9
+ --bg: #1a1a2e;
10
+ --surface: #16213e;
11
+ --border: #0f3460;
12
+ --text: #e0e0e0;
13
+ --muted: #888;
14
+ --green: #4ade80;
15
+ --yellow: #fbbf24;
16
+ --red: #f87171;
17
+ --blue: #60a5fa;
18
+ }
19
+ * { margin: 0; padding: 0; box-sizing: border-box; }
20
+ body {
21
+ font-family: 'SF Mono', 'Fira Code', monospace;
22
+ background: var(--bg);
23
+ color: var(--text);
24
+ font-size: 13px;
25
+ padding: 12px;
26
+ }
27
+ .header {
28
+ display: flex;
29
+ justify-content: space-between;
30
+ align-items: center;
31
+ padding: 8px 12px;
32
+ background: var(--surface);
33
+ border: 1px solid var(--border);
34
+ border-radius: 6px;
35
+ margin-bottom: 12px;
36
+ }
37
+ .header .squad-name { font-size: 15px; font-weight: 600; }
38
+ .header .session { color: var(--muted); font-size: 11px; }
39
+ .status-dot {
40
+ display: inline-block;
41
+ width: 8px; height: 8px;
42
+ border-radius: 50%;
43
+ margin-right: 6px;
44
+ }
45
+ .status-dot.active { background: var(--green); }
46
+ .status-dot.warning { background: var(--yellow); }
47
+ .status-dot.error { background: var(--red); }
48
+ .waves-container {
49
+ display: flex;
50
+ gap: 8px;
51
+ margin-bottom: 12px;
52
+ overflow-x: auto;
53
+ }
54
+ .wave {
55
+ flex: 1;
56
+ min-width: 120px;
57
+ background: var(--surface);
58
+ border: 1px solid var(--border);
59
+ border-radius: 6px;
60
+ padding: 8px;
61
+ }
62
+ .wave-header {
63
+ font-size: 11px;
64
+ text-transform: uppercase;
65
+ letter-spacing: 0.5px;
66
+ margin-bottom: 6px;
67
+ padding-bottom: 4px;
68
+ border-bottom: 1px solid var(--border);
69
+ }
70
+ .wave-header.done { color: var(--green); }
71
+ .wave-header.running { color: var(--blue); }
72
+ .wave-header.blocked { color: var(--red); }
73
+ .wave-header.pending { color: var(--muted); }
74
+ .task {
75
+ font-size: 12px;
76
+ padding: 2px 0;
77
+ display: flex;
78
+ align-items: center;
79
+ gap: 4px;
80
+ }
81
+ .task-icon { font-size: 10px; }
82
+ .task-icon.completed { color: var(--green); }
83
+ .task-icon.in_progress { color: var(--blue); }
84
+ .task-icon.failed { color: var(--red); }
85
+ .task-icon.pending { color: var(--muted); }
86
+ .stats-bar {
87
+ display: flex;
88
+ gap: 8px;
89
+ background: var(--surface);
90
+ border: 1px solid var(--border);
91
+ border-radius: 6px;
92
+ padding: 8px 12px;
93
+ margin-bottom: 12px;
94
+ }
95
+ .stat {
96
+ display: flex;
97
+ align-items: center;
98
+ gap: 4px;
99
+ }
100
+ .stat-label { color: var(--muted); font-size: 11px; }
101
+ .stat-value { font-weight: 600; }
102
+ .stat-divider {
103
+ width: 1px;
104
+ background: var(--border);
105
+ align-self: stretch;
106
+ }
107
+ .bus-feed {
108
+ background: var(--surface);
109
+ border: 1px solid var(--border);
110
+ border-radius: 6px;
111
+ padding: 8px;
112
+ max-height: 120px;
113
+ overflow-y: auto;
114
+ }
115
+ .bus-msg {
116
+ font-size: 11px;
117
+ padding: 2px 0;
118
+ border-bottom: 1px solid rgba(255,255,255,0.05);
119
+ }
120
+ .bus-msg .from { color: var(--blue); }
121
+ .bus-msg .type { color: var(--muted); }
122
+ .bus-msg .type.block { color: var(--red); }
123
+ .bus-msg .type.result { color: var(--green); }
124
+ #loading { text-align: center; padding: 40px; color: var(--muted); }
125
+ </style>
126
+ </head>
127
+ <body>
128
+ <div id="loading">Loading dashboard data...</div>
129
+ <div id="dashboard" style="display:none">
130
+ <div class="header">
131
+ <div>
132
+ <span class="status-dot active" id="status-dot"></span>
133
+ <span class="squad-name" id="squad-name">—</span>
134
+ </div>
135
+ <div class="session" id="session-info">—</div>
136
+ </div>
137
+
138
+ <div class="waves-container" id="waves"></div>
139
+
140
+ <div class="stats-bar">
141
+ <div class="stat">
142
+ <span class="stat-label">Bus:</span>
143
+ <span class="stat-value" id="bus-total">0</span>
144
+ <span class="stat-label">msgs</span>
145
+ </div>
146
+ <div class="stat-divider"></div>
147
+ <div class="stat">
148
+ <span class="stat-label">Blocks:</span>
149
+ <span class="stat-value" id="bus-blocks">0</span>
150
+ </div>
151
+ <div class="stat-divider"></div>
152
+ <div class="stat">
153
+ <span class="stat-label">Budget:</span>
154
+ <span class="stat-value" id="budget-info">—</span>
155
+ </div>
156
+ <div class="stat-divider"></div>
157
+ <div class="stat">
158
+ <span class="stat-label">Sessions:</span>
159
+ <span class="stat-value" id="sessions-count">0</span>
160
+ </div>
161
+ </div>
162
+
163
+ <div class="bus-feed" id="bus-feed"></div>
164
+ </div>
165
+
166
+ <script>
167
+ const TASK_ICONS = {
168
+ completed: '\u2713',
169
+ in_progress: '\u25B6',
170
+ failed: '\u2717',
171
+ escalated: '\u26A0',
172
+ pending: '\u25CB',
173
+ skipped: '\u2013'
174
+ };
175
+
176
+ function renderWaves(wavesData) {
177
+ const container = document.getElementById('waves');
178
+ container.innerHTML = '';
179
+
180
+ for (const wave of (wavesData.waves || [])) {
181
+ const el = document.createElement('div');
182
+ el.className = 'wave';
183
+ const statusClass = wave.status.toLowerCase();
184
+
185
+ el.innerHTML = `
186
+ <div class="wave-header ${statusClass}">Wave ${wave.wave} [${wave.status}]</div>
187
+ ${wave.tasks.map(t => `
188
+ <div class="task">
189
+ <span class="task-icon ${t.status}">${TASK_ICONS[t.status] || '?'}</span>
190
+ <span>${t.executor || t.title || t.id}</span>
191
+ </div>
192
+ `).join('')}
193
+ `;
194
+ container.appendChild(el);
195
+ }
196
+ }
197
+
198
+ function renderBusFeed(busData) {
199
+ const feed = document.getElementById('bus-feed');
200
+ const messages = busData.lastMessages || [];
201
+ feed.innerHTML = messages.slice(-10).reverse().map(m => `
202
+ <div class="bus-msg">
203
+ <span class="from">${m.from}</span>
204
+ <span class="type ${m.type}">[${m.type}]</span>
205
+ ${m.content ? m.content.slice(0, 80) : ''}
206
+ </div>
207
+ `).join('');
208
+ }
209
+
210
+ function renderDashboard(data) {
211
+ document.getElementById('loading').style.display = 'none';
212
+ document.getElementById('dashboard').style.display = 'block';
213
+
214
+ document.getElementById('squad-name').textContent = `Squad: ${data.squad}`;
215
+ document.getElementById('session-info').textContent =
216
+ data.waves.sessionId ? `Session: ${data.waves.sessionId.slice(0, 8)}` : '';
217
+
218
+ if (data.state) {
219
+ document.getElementById('sessions-count').textContent =
220
+ data.state.sessions_completed || 0;
221
+ }
222
+
223
+ document.getElementById('bus-total').textContent = data.bus.total || 0;
224
+ document.getElementById('bus-blocks').textContent = data.bus.blocks || 0;
225
+
226
+ if (data.budget && data.budget.max_tokens_per_session) {
227
+ document.getElementById('budget-info').textContent =
228
+ `${(data.budget.max_tokens_per_session / 1000).toFixed(0)}k`;
229
+ }
230
+
231
+ const dot = document.getElementById('status-dot');
232
+ if (data.bus.blocks > 0) {
233
+ dot.className = 'status-dot warning';
234
+ } else {
235
+ dot.className = 'status-dot active';
236
+ }
237
+
238
+ renderWaves(data.waves);
239
+ renderBusFeed(data.bus);
240
+ }
241
+
242
+ // MCP App data injection point
243
+ if (window.__MCP_DATA__) {
244
+ renderDashboard(window.__MCP_DATA__);
245
+ }
246
+
247
+ // Polling fallback for standalone mode
248
+ if (window.__DASHBOARD_API__) {
249
+ async function poll() {
250
+ try {
251
+ const res = await fetch(window.__DASHBOARD_API__);
252
+ const data = await res.json();
253
+ renderDashboard(data);
254
+ } catch { /* retry */ }
255
+ setTimeout(poll, 3000);
256
+ }
257
+ poll();
258
+ }
259
+ </script>
260
+ </body>
261
+ </html>
@@ -0,0 +1,23 @@
1
+ {
2
+ "name": "aioson-squad-dashboard",
3
+ "version": "1.0.0",
4
+ "description": "Real-time squad execution dashboard for AIOSON",
5
+ "type": "mcp-app",
6
+ "display": {
7
+ "title": "Squad Dashboard",
8
+ "icon": "grid",
9
+ "width": 600,
10
+ "height": 400
11
+ },
12
+ "entry": "index.html",
13
+ "resources": [
14
+ "aioson://squad/{slug}/state",
15
+ "aioson://squad/{slug}/bus",
16
+ "aioson://squad/{slug}/budget",
17
+ "aioson://squad/{slug}/waves"
18
+ ],
19
+ "permissions": [
20
+ "read:squad-state",
21
+ "read:squad-bus"
22
+ ]
23
+ }
@@ -0,0 +1,130 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * MCP Resources: Squad State — Plan 81, Phase 4.2
5
+ *
6
+ * Exposes squad execution state as MCP resources that can be consumed
7
+ * by MCP Apps (dashboard) and MCP clients.
8
+ *
9
+ * Resources:
10
+ * aioson://squad/{slug}/state → STATE.md parsed as JSON
11
+ * aioson://squad/{slug}/bus → last 20 bus messages
12
+ * aioson://squad/{slug}/budget → token budget status
13
+ * aioson://squad/{slug}/waves → execution wave status
14
+ */
15
+
16
+ const { readState } = require('../../squad/state-manager');
17
+ const {
18
+ getDashboardData,
19
+ readBusState,
20
+ readBudgetState,
21
+ readWavesState
22
+ } = require('../apps/squad-dashboard/app');
23
+
24
+ /**
25
+ * MCP resource URI pattern.
26
+ */
27
+ const RESOURCE_PATTERNS = [
28
+ { pattern: /^aioson:\/\/squad\/([a-z0-9-]+)\/state$/, handler: 'state' },
29
+ { pattern: /^aioson:\/\/squad\/([a-z0-9-]+)\/bus$/, handler: 'bus' },
30
+ { pattern: /^aioson:\/\/squad\/([a-z0-9-]+)\/budget$/, handler: 'budget' },
31
+ { pattern: /^aioson:\/\/squad\/([a-z0-9-]+)\/waves$/, handler: 'waves' },
32
+ { pattern: /^aioson:\/\/squad\/([a-z0-9-]+)\/dashboard$/, handler: 'dashboard' }
33
+ ];
34
+
35
+ /**
36
+ * Resolve an MCP resource URI to its data.
37
+ *
38
+ * @param {string} uri — MCP resource URI
39
+ * @param {string} projectDir — Project root
40
+ * @returns {Promise<object|null>} — Resource data or null if not found
41
+ */
42
+ async function resolveResource(uri, projectDir) {
43
+ for (const { pattern, handler } of RESOURCE_PATTERNS) {
44
+ const match = uri.match(pattern);
45
+ if (!match) continue;
46
+
47
+ const squadSlug = match[1];
48
+
49
+ switch (handler) {
50
+ case 'state':
51
+ return readState(projectDir, squadSlug);
52
+
53
+ case 'bus':
54
+ return readBusState(projectDir, squadSlug);
55
+
56
+ case 'budget':
57
+ return readBudgetState(projectDir, squadSlug);
58
+
59
+ case 'waves':
60
+ return readWavesState(projectDir, squadSlug);
61
+
62
+ case 'dashboard':
63
+ return getDashboardData(projectDir, squadSlug);
64
+ }
65
+ }
66
+
67
+ return null;
68
+ }
69
+
70
+ /**
71
+ * List available MCP resources for a project.
72
+ *
73
+ * @param {string} projectDir
74
+ * @returns {Promise<object[]>} — Array of { uri, name, description, mimeType }
75
+ */
76
+ async function listResources(projectDir) {
77
+ const fs = require('node:fs/promises');
78
+ const path = require('node:path');
79
+ const squadsDir = path.join(projectDir, '.aioson', 'squads');
80
+ const resources = [];
81
+
82
+ try {
83
+ const entries = await fs.readdir(squadsDir, { withFileTypes: true });
84
+ for (const entry of entries) {
85
+ if (!entry.isDirectory()) continue;
86
+ const slug = entry.name;
87
+
88
+ resources.push(
89
+ {
90
+ uri: `aioson://squad/${slug}/state`,
91
+ name: `Squad ${slug} — State`,
92
+ description: `Cross-session state for squad "${slug}"`,
93
+ mimeType: 'application/json'
94
+ },
95
+ {
96
+ uri: `aioson://squad/${slug}/bus`,
97
+ name: `Squad ${slug} — Bus`,
98
+ description: `Recent intra-squad bus messages for "${slug}"`,
99
+ mimeType: 'application/json'
100
+ },
101
+ {
102
+ uri: `aioson://squad/${slug}/budget`,
103
+ name: `Squad ${slug} — Budget`,
104
+ description: `Token budget status for "${slug}"`,
105
+ mimeType: 'application/json'
106
+ },
107
+ {
108
+ uri: `aioson://squad/${slug}/waves`,
109
+ name: `Squad ${slug} — Waves`,
110
+ description: `Execution wave status for "${slug}"`,
111
+ mimeType: 'application/json'
112
+ },
113
+ {
114
+ uri: `aioson://squad/${slug}/dashboard`,
115
+ name: `Squad ${slug} — Dashboard`,
116
+ description: `Complete dashboard data for "${slug}"`,
117
+ mimeType: 'application/json'
118
+ }
119
+ );
120
+ }
121
+ } catch { /* no squads dir */ }
122
+
123
+ return resources;
124
+ }
125
+
126
+ module.exports = {
127
+ resolveResource,
128
+ listResources,
129
+ RESOURCE_PATTERNS
130
+ };