@jaimevalasek/aioson 1.3.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 (288) hide show
  1. package/CHANGELOG.md +456 -0
  2. package/CODE_OF_CONDUCT.md +12 -0
  3. package/CONTRIBUTING.md +13 -0
  4. package/LICENSE +21 -0
  5. package/README.md +254 -0
  6. package/bin/aioson.js +4 -0
  7. package/docs/en/cli-reference.md +398 -0
  8. package/docs/en/i18n.md +52 -0
  9. package/docs/en/json-schemas.md +41 -0
  10. package/docs/en/mcp.md +56 -0
  11. package/docs/en/parallel.md +82 -0
  12. package/docs/en/qa-browser.md +339 -0
  13. package/docs/en/release-flow.md +22 -0
  14. package/docs/en/release-notes-template.md +41 -0
  15. package/docs/en/release.md +28 -0
  16. package/docs/en/schemas/agent-prompt.schema.json +17 -0
  17. package/docs/en/schemas/agents.schema.json +32 -0
  18. package/docs/en/schemas/context-validate.schema.json +36 -0
  19. package/docs/en/schemas/doctor.schema.json +89 -0
  20. package/docs/en/schemas/error.schema.json +24 -0
  21. package/docs/en/schemas/i18n-add.schema.json +15 -0
  22. package/docs/en/schemas/index.json +116 -0
  23. package/docs/en/schemas/info.schema.json +39 -0
  24. package/docs/en/schemas/init.schema.json +48 -0
  25. package/docs/en/schemas/install.schema.json +60 -0
  26. package/docs/en/schemas/locale-apply.schema.json +30 -0
  27. package/docs/en/schemas/mcp-doctor.schema.json +95 -0
  28. package/docs/en/schemas/mcp-init.schema.json +122 -0
  29. package/docs/en/schemas/package-test.schema.json +24 -0
  30. package/docs/en/schemas/parallel-assign.schema.json +57 -0
  31. package/docs/en/schemas/parallel-doctor.schema.json +86 -0
  32. package/docs/en/schemas/parallel-init.schema.json +53 -0
  33. package/docs/en/schemas/parallel-status.schema.json +94 -0
  34. package/docs/en/schemas/setup-context.schema.json +39 -0
  35. package/docs/en/schemas/smoke.schema.json +23 -0
  36. package/docs/en/schemas/update.schema.json +48 -0
  37. package/docs/en/schemas/workflow-plan.schema.json +30 -0
  38. package/docs/en/web3.md +54 -0
  39. package/docs/pt/README.md +46 -0
  40. package/docs/pt/advisor-spec.md +335 -0
  41. package/docs/pt/agentes.md +453 -0
  42. package/docs/pt/cenarios.md +1230 -0
  43. package/docs/pt/clientes-ai.md +224 -0
  44. package/docs/pt/comandos-cli.md +511 -0
  45. package/docs/pt/genome-3.0-spec.md +296 -0
  46. package/docs/pt/guia-engineer.md +226 -0
  47. package/docs/pt/inicio-rapido.md +138 -0
  48. package/docs/pt/profiler-system.md +214 -0
  49. package/docs/pt/runtime-observability.md +72 -0
  50. package/docs/pt/squad-genoma.md +777 -0
  51. package/docs/pt/web3.md +797 -0
  52. package/docs/testing/genome-2.0-manual-regression.md +23 -0
  53. package/docs/testing/genome-2.0-matrix.md +36 -0
  54. package/docs/testing/genome-2.0-rollout.md +184 -0
  55. package/package.json +50 -0
  56. package/src/agents.js +56 -0
  57. package/src/cli.js +497 -0
  58. package/src/commands/agents.js +142 -0
  59. package/src/commands/cloud.js +1767 -0
  60. package/src/commands/config.js +90 -0
  61. package/src/commands/context-validate.js +91 -0
  62. package/src/commands/doctor.js +123 -0
  63. package/src/commands/genome-doctor.js +41 -0
  64. package/src/commands/genome-migrate.js +49 -0
  65. package/src/commands/i18n-add.js +56 -0
  66. package/src/commands/info.js +41 -0
  67. package/src/commands/init.js +75 -0
  68. package/src/commands/install.js +68 -0
  69. package/src/commands/locale-apply.js +51 -0
  70. package/src/commands/locale-diff.js +126 -0
  71. package/src/commands/mcp-doctor.js +406 -0
  72. package/src/commands/mcp-init.js +379 -0
  73. package/src/commands/package-e2e.js +273 -0
  74. package/src/commands/parallel-assign.js +403 -0
  75. package/src/commands/parallel-doctor.js +437 -0
  76. package/src/commands/parallel-init.js +249 -0
  77. package/src/commands/parallel-status.js +290 -0
  78. package/src/commands/qa-doctor.js +185 -0
  79. package/src/commands/qa-init.js +161 -0
  80. package/src/commands/qa-report.js +58 -0
  81. package/src/commands/qa-run.js +873 -0
  82. package/src/commands/qa-scan.js +337 -0
  83. package/src/commands/runtime.js +948 -0
  84. package/src/commands/scan-project.js +1107 -0
  85. package/src/commands/setup-context.js +650 -0
  86. package/src/commands/smoke.js +426 -0
  87. package/src/commands/squad-doctor.js +358 -0
  88. package/src/commands/squad-export.js +46 -0
  89. package/src/commands/squad-pipeline.js +97 -0
  90. package/src/commands/squad-repair-genomes.js +39 -0
  91. package/src/commands/squad-status.js +424 -0
  92. package/src/commands/squad-validate.js +230 -0
  93. package/src/commands/test-agents.js +194 -0
  94. package/src/commands/update.js +55 -0
  95. package/src/commands/workflow-next.js +594 -0
  96. package/src/commands/workflow-plan.js +108 -0
  97. package/src/constants.js +314 -0
  98. package/src/context-parse-reason.js +22 -0
  99. package/src/context-writer.js +150 -0
  100. package/src/context.js +217 -0
  101. package/src/detector.js +261 -0
  102. package/src/doctor.js +289 -0
  103. package/src/execution-gateway.js +461 -0
  104. package/src/genome-files.js +198 -0
  105. package/src/genome-format.js +442 -0
  106. package/src/genome-schema.js +215 -0
  107. package/src/genomes/bindings.js +281 -0
  108. package/src/genomes.js +467 -0
  109. package/src/i18n/index.js +103 -0
  110. package/src/i18n/messages/en.js +784 -0
  111. package/src/i18n/messages/es.js +718 -0
  112. package/src/i18n/messages/fr.js +725 -0
  113. package/src/i18n/messages/pt-BR.js +818 -0
  114. package/src/i18n/scaffold.js +64 -0
  115. package/src/installer.js +232 -0
  116. package/src/lib/genomes/compat.js +206 -0
  117. package/src/lib/genomes/migrate.js +90 -0
  118. package/src/lib/squads/genome-repair.js +49 -0
  119. package/src/locales.js +84 -0
  120. package/src/onboarding.js +305 -0
  121. package/src/parser.js +53 -0
  122. package/src/prompt-tool.js +20 -0
  123. package/src/qa-html-report.js +472 -0
  124. package/src/runtime-store.js +1527 -0
  125. package/src/squads/apply-genome.js +21 -0
  126. package/src/squads/genome-binding-service.js +154 -0
  127. package/src/updater.js +32 -0
  128. package/src/utils.js +46 -0
  129. package/src/version.js +50 -0
  130. package/template/.aioson/advisors/.gitkeep +1 -0
  131. package/template/.aioson/agents/analyst.md +225 -0
  132. package/template/.aioson/agents/architect.md +221 -0
  133. package/template/.aioson/agents/dev.md +201 -0
  134. package/template/.aioson/agents/discovery-design-doc.md +196 -0
  135. package/template/.aioson/agents/genoma.md +300 -0
  136. package/template/.aioson/agents/orchestrator.md +107 -0
  137. package/template/.aioson/agents/pm.md +89 -0
  138. package/template/.aioson/agents/product.md +361 -0
  139. package/template/.aioson/agents/profiler-enricher.md +266 -0
  140. package/template/.aioson/agents/profiler-forge.md +188 -0
  141. package/template/.aioson/agents/profiler-researcher.md +245 -0
  142. package/template/.aioson/agents/qa.md +344 -0
  143. package/template/.aioson/agents/setup.md +381 -0
  144. package/template/.aioson/agents/squad.md +837 -0
  145. package/template/.aioson/agents/ux-ui.md +416 -0
  146. package/template/.aioson/config.md +56 -0
  147. package/template/.aioson/context/.gitkeep +0 -0
  148. package/template/.aioson/context/parallel/.gitkeep +0 -0
  149. package/template/.aioson/context/spec.md.template +37 -0
  150. package/template/.aioson/genomas/.gitkeep +0 -0
  151. package/template/.aioson/locales/en/agents/analyst.md +214 -0
  152. package/template/.aioson/locales/en/agents/architect.md +210 -0
  153. package/template/.aioson/locales/en/agents/dev.md +187 -0
  154. package/template/.aioson/locales/en/agents/discovery-design-doc.md +27 -0
  155. package/template/.aioson/locales/en/agents/genoma.md +212 -0
  156. package/template/.aioson/locales/en/agents/orchestrator.md +105 -0
  157. package/template/.aioson/locales/en/agents/pm.md +77 -0
  158. package/template/.aioson/locales/en/agents/product.md +310 -0
  159. package/template/.aioson/locales/en/agents/profiler-enricher.md +5 -0
  160. package/template/.aioson/locales/en/agents/profiler-forge.md +5 -0
  161. package/template/.aioson/locales/en/agents/profiler-researcher.md +5 -0
  162. package/template/.aioson/locales/en/agents/qa.md +214 -0
  163. package/template/.aioson/locales/en/agents/setup.md +342 -0
  164. package/template/.aioson/locales/en/agents/squad.md +247 -0
  165. package/template/.aioson/locales/en/agents/ux-ui.md +320 -0
  166. package/template/.aioson/locales/es/agents/analyst.md +203 -0
  167. package/template/.aioson/locales/es/agents/architect.md +208 -0
  168. package/template/.aioson/locales/es/agents/dev.md +183 -0
  169. package/template/.aioson/locales/es/agents/discovery-design-doc.md +19 -0
  170. package/template/.aioson/locales/es/agents/genoma.md +102 -0
  171. package/template/.aioson/locales/es/agents/orchestrator.md +108 -0
  172. package/template/.aioson/locales/es/agents/pm.md +81 -0
  173. package/template/.aioson/locales/es/agents/product.md +310 -0
  174. package/template/.aioson/locales/es/agents/profiler-enricher.md +5 -0
  175. package/template/.aioson/locales/es/agents/profiler-forge.md +5 -0
  176. package/template/.aioson/locales/es/agents/profiler-researcher.md +5 -0
  177. package/template/.aioson/locales/es/agents/qa.md +163 -0
  178. package/template/.aioson/locales/es/agents/setup.md +347 -0
  179. package/template/.aioson/locales/es/agents/squad.md +247 -0
  180. package/template/.aioson/locales/es/agents/ux-ui.md +201 -0
  181. package/template/.aioson/locales/fr/agents/analyst.md +203 -0
  182. package/template/.aioson/locales/fr/agents/architect.md +208 -0
  183. package/template/.aioson/locales/fr/agents/dev.md +183 -0
  184. package/template/.aioson/locales/fr/agents/discovery-design-doc.md +19 -0
  185. package/template/.aioson/locales/fr/agents/genoma.md +102 -0
  186. package/template/.aioson/locales/fr/agents/orchestrator.md +108 -0
  187. package/template/.aioson/locales/fr/agents/pm.md +81 -0
  188. package/template/.aioson/locales/fr/agents/product.md +310 -0
  189. package/template/.aioson/locales/fr/agents/profiler-enricher.md +5 -0
  190. package/template/.aioson/locales/fr/agents/profiler-forge.md +5 -0
  191. package/template/.aioson/locales/fr/agents/profiler-researcher.md +5 -0
  192. package/template/.aioson/locales/fr/agents/qa.md +163 -0
  193. package/template/.aioson/locales/fr/agents/setup.md +347 -0
  194. package/template/.aioson/locales/fr/agents/squad.md +247 -0
  195. package/template/.aioson/locales/fr/agents/ux-ui.md +201 -0
  196. package/template/.aioson/locales/pt-BR/agents/analyst.md +217 -0
  197. package/template/.aioson/locales/pt-BR/agents/architect.md +213 -0
  198. package/template/.aioson/locales/pt-BR/agents/dev.md +198 -0
  199. package/template/.aioson/locales/pt-BR/agents/discovery-design-doc.md +198 -0
  200. package/template/.aioson/locales/pt-BR/agents/genoma.md +297 -0
  201. package/template/.aioson/locales/pt-BR/agents/orchestrator.md +108 -0
  202. package/template/.aioson/locales/pt-BR/agents/pm.md +81 -0
  203. package/template/.aioson/locales/pt-BR/agents/product.md +316 -0
  204. package/template/.aioson/locales/pt-BR/agents/profiler-enricher.md +5 -0
  205. package/template/.aioson/locales/pt-BR/agents/profiler-forge.md +5 -0
  206. package/template/.aioson/locales/pt-BR/agents/profiler-researcher.md +5 -0
  207. package/template/.aioson/locales/pt-BR/agents/qa.md +217 -0
  208. package/template/.aioson/locales/pt-BR/agents/setup.md +371 -0
  209. package/template/.aioson/locales/pt-BR/agents/squad.md +772 -0
  210. package/template/.aioson/locales/pt-BR/agents/ux-ui.md +322 -0
  211. package/template/.aioson/mcp/servers.md +24 -0
  212. package/template/.aioson/profiler-reports/.gitkeep +1 -0
  213. package/template/.aioson/schemas/content-blueprint.schema.json +30 -0
  214. package/template/.aioson/schemas/genome-meta.schema.json +150 -0
  215. package/template/.aioson/schemas/genome.schema.json +115 -0
  216. package/template/.aioson/schemas/readiness.schema.json +27 -0
  217. package/template/.aioson/schemas/squad-blueprint.schema.json +172 -0
  218. package/template/.aioson/schemas/squad-manifest.schema.json +276 -0
  219. package/template/.aioson/skills/dynamic/README.md +30 -0
  220. package/template/.aioson/skills/dynamic/cardano-docs.md +16 -0
  221. package/template/.aioson/skills/dynamic/ethereum-docs.md +17 -0
  222. package/template/.aioson/skills/dynamic/flux-ui-docs.md +13 -0
  223. package/template/.aioson/skills/dynamic/laravel-docs.md +41 -0
  224. package/template/.aioson/skills/dynamic/npm-packages.md +16 -0
  225. package/template/.aioson/skills/dynamic/solana-docs.md +16 -0
  226. package/template/.aioson/skills/references/premium-command-center-ui/master-application-prompt.md +79 -0
  227. package/template/.aioson/skills/references/premium-command-center-ui/operational-ux-playbook.md +253 -0
  228. package/template/.aioson/skills/references/premium-command-center-ui/quality-validation-checklist.md +82 -0
  229. package/template/.aioson/skills/references/premium-command-center-ui/visual-system-and-component-patterns.md +270 -0
  230. package/template/.aioson/skills/static/django-patterns.md +342 -0
  231. package/template/.aioson/skills/static/fastapi-patterns.md +344 -0
  232. package/template/.aioson/skills/static/filament-patterns.md +267 -0
  233. package/template/.aioson/skills/static/flux-ui-components.md +262 -0
  234. package/template/.aioson/skills/static/git-conventions.md +227 -0
  235. package/template/.aioson/skills/static/interface-design.md +372 -0
  236. package/template/.aioson/skills/static/jetstream-setup.md +200 -0
  237. package/template/.aioson/skills/static/laravel-conventions.md +491 -0
  238. package/template/.aioson/skills/static/nextjs-patterns.md +321 -0
  239. package/template/.aioson/skills/static/node-express-patterns.md +317 -0
  240. package/template/.aioson/skills/static/node-typescript-patterns.md +282 -0
  241. package/template/.aioson/skills/static/premium-command-center-ui.md +190 -0
  242. package/template/.aioson/skills/static/rails-conventions.md +307 -0
  243. package/template/.aioson/skills/static/react-motion-patterns.md +577 -0
  244. package/template/.aioson/skills/static/static-html-patterns.md +1935 -0
  245. package/template/.aioson/skills/static/tall-stack-patterns.md +286 -0
  246. package/template/.aioson/skills/static/ui-ux-modern.md +75 -0
  247. package/template/.aioson/skills/static/web3-cardano-patterns.md +337 -0
  248. package/template/.aioson/skills/static/web3-ethereum-patterns.md +310 -0
  249. package/template/.aioson/skills/static/web3-security-checklist.md +284 -0
  250. package/template/.aioson/skills/static/web3-solana-patterns.md +324 -0
  251. package/template/.aioson/squads/.artisan/.gitkeep +0 -0
  252. package/template/.aioson/squads/.gitkeep +0 -0
  253. package/template/.aioson/squads/memory.md +5 -0
  254. package/template/.aioson/tasks/squad-analyze.md +83 -0
  255. package/template/.aioson/tasks/squad-create.md +99 -0
  256. package/template/.aioson/tasks/squad-design.md +100 -0
  257. package/template/.aioson/tasks/squad-export.md +20 -0
  258. package/template/.aioson/tasks/squad-extend.md +68 -0
  259. package/template/.aioson/tasks/squad-pipeline.md +122 -0
  260. package/template/.aioson/tasks/squad-repair.md +85 -0
  261. package/template/.aioson/tasks/squad-validate.md +58 -0
  262. package/template/.aioson/templates/squads/content-basic/template.json +21 -0
  263. package/template/.aioson/templates/squads/media-channel/template.json +24 -0
  264. package/template/.aioson/templates/squads/research-analysis/template.json +22 -0
  265. package/template/.aioson/templates/squads/software-delivery/template.json +21 -0
  266. package/template/.claude/commands/aioson/analyst.md +5 -0
  267. package/template/.claude/commands/aioson/architect.md +5 -0
  268. package/template/.claude/commands/aioson/dev.md +5 -0
  269. package/template/.claude/commands/aioson/orchestrator.md +5 -0
  270. package/template/.claude/commands/aioson/pm.md +5 -0
  271. package/template/.claude/commands/aioson/qa.md +5 -0
  272. package/template/.claude/commands/aioson/setup.md +5 -0
  273. package/template/.claude/commands/aioson/ux-ui.md +5 -0
  274. package/template/.gemini/GEMINI.md +10 -0
  275. package/template/.gemini/commands/aios-analyst.toml +4 -0
  276. package/template/.gemini/commands/aios-architect.toml +7 -0
  277. package/template/.gemini/commands/aios-dev.toml +8 -0
  278. package/template/.gemini/commands/aios-discovery-design-doc.toml +4 -0
  279. package/template/.gemini/commands/aios-orchestrator.toml +8 -0
  280. package/template/.gemini/commands/aios-pm.toml +8 -0
  281. package/template/.gemini/commands/aios-product.toml +4 -0
  282. package/template/.gemini/commands/aios-qa.toml +6 -0
  283. package/template/.gemini/commands/aios-setup.toml +3 -0
  284. package/template/.gemini/commands/aios-ux-ui.toml +8 -0
  285. package/template/AGENTS.md +67 -0
  286. package/template/CLAUDE.md +31 -0
  287. package/template/OPENCODE.md +24 -0
  288. package/template/aioson-models.json +40 -0
@@ -0,0 +1,290 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const { exists } = require('../utils');
6
+ const { recordRuntimeOperation } = require('../execution-gateway');
7
+
8
+ const KNOWN_STATUSES = ['pending', 'in_progress', 'completed', 'blocked'];
9
+
10
+ function parseLaneIndex(fileName) {
11
+ const match = String(fileName || '').match(/^agent-(\d+)\.status\.md$/);
12
+ if (!match) return null;
13
+ const value = Number(match[1]);
14
+ if (!Number.isFinite(value) || value <= 0) return null;
15
+ return Math.floor(value);
16
+ }
17
+
18
+ function extractMetadata(content, key, fallback = '') {
19
+ const regex = new RegExp(`^-\\s*${key}:\\s*(.*)$`, 'im');
20
+ const match = String(content || '').match(regex);
21
+ if (!match) return fallback;
22
+ return String(match[1] || '').trim();
23
+ }
24
+
25
+ function extractSectionLines(content, title) {
26
+ const lines = String(content || '').split(/\r?\n/);
27
+ const start = lines.findIndex((line) => line.trim() === `## ${title}`);
28
+ if (start === -1) return [];
29
+
30
+ const output = [];
31
+ for (let i = start + 1; i < lines.length; i += 1) {
32
+ const line = lines[i];
33
+ if (line.startsWith('## ')) break;
34
+ output.push(line);
35
+ }
36
+ return output;
37
+ }
38
+
39
+ function extractSectionBullets(content, title) {
40
+ const lines = extractSectionLines(content, title);
41
+ const items = [];
42
+ for (const line of lines) {
43
+ const match = line.match(/^\s*-\s+(.*)$/);
44
+ if (!match) continue;
45
+ items.push(String(match[1] || '').trim());
46
+ }
47
+ return items;
48
+ }
49
+
50
+ function parseDeliverables(content) {
51
+ const lines = extractSectionLines(content, 'Deliverables');
52
+ let total = 0;
53
+ let completed = 0;
54
+ for (const line of lines) {
55
+ const match = line.match(/^\s*-\s+\[([ xX])\]\s+(.+)$/);
56
+ if (!match) continue;
57
+ total += 1;
58
+ if (match[1].toLowerCase() === 'x') completed += 1;
59
+ }
60
+ return { completed, total };
61
+ }
62
+
63
+ function normalizeStatus(value) {
64
+ const normalized = String(value || '')
65
+ .trim()
66
+ .toLowerCase();
67
+ if (KNOWN_STATUSES.includes(normalized)) return normalized;
68
+ return normalized || 'other';
69
+ }
70
+
71
+ function sanitizeScopeItems(items) {
72
+ return (items || []).filter((item) => {
73
+ const value = String(item || '').trim().toLowerCase();
74
+ if (!value) return false;
75
+ if (value === '[define module or feature boundary]') return false;
76
+ if (value === '[unassigned]') return false;
77
+ return true;
78
+ });
79
+ }
80
+
81
+ function sanitizeBlockerItems(items) {
82
+ return (items || []).filter((item) => {
83
+ const value = String(item || '').trim().toLowerCase();
84
+ if (!value) return false;
85
+ if (value === '[none]') return false;
86
+ return true;
87
+ });
88
+ }
89
+
90
+ function countDecisionRows(content) {
91
+ const lines = String(content || '').split(/\r?\n/);
92
+ return lines.filter((line) => {
93
+ const trimmed = line.trim();
94
+ if (!trimmed.startsWith('|')) return false;
95
+ if (trimmed.includes('| time | decision | rationale | impact |')) return false;
96
+ if (/^\|\-+\|/.test(trimmed)) return false;
97
+ return true;
98
+ }).length;
99
+ }
100
+
101
+ function createStatusCounts() {
102
+ return {
103
+ pending: 0,
104
+ in_progress: 0,
105
+ completed: 0,
106
+ blocked: 0,
107
+ other: 0
108
+ };
109
+ }
110
+
111
+ function formatStatusLabel(status, t) {
112
+ const normalized = String(status || '')
113
+ .trim()
114
+ .toLowerCase();
115
+ if (normalized === 'pending') return t('parallel_status.status_pending');
116
+ if (normalized === 'in_progress') return t('parallel_status.status_in_progress');
117
+ if (normalized === 'completed') return t('parallel_status.status_completed');
118
+ if (normalized === 'blocked') return t('parallel_status.status_blocked');
119
+ return t('parallel_status.status_other');
120
+ }
121
+
122
+ async function parseLaneFile(parallelDir, index) {
123
+ const fileName = `agent-${index}.status.md`;
124
+ const absPath = path.join(parallelDir, fileName);
125
+ const content = await fs.readFile(absPath, 'utf8');
126
+ const status = normalizeStatus(extractMetadata(content, 'status', 'pending'));
127
+ const owner = extractMetadata(content, 'owner', '[unassigned]');
128
+ const priority = extractMetadata(content, 'priority', 'medium');
129
+ const updatedAt = extractMetadata(content, 'updated_at', '');
130
+ const scopeItems = sanitizeScopeItems(extractSectionBullets(content, 'Scope'));
131
+ const blockerItems = sanitizeBlockerItems(extractSectionBullets(content, 'Blockers'));
132
+ const deliverables = parseDeliverables(content);
133
+
134
+ return {
135
+ lane: index,
136
+ file: path.join('.aioson/context/parallel', fileName).replace(/\\/g, '/'),
137
+ status,
138
+ owner,
139
+ priority,
140
+ updatedAt,
141
+ scopeCount: scopeItems.length,
142
+ blockerCount: blockerItems.length,
143
+ deliverables,
144
+ scopeItems,
145
+ blockerItems
146
+ };
147
+ }
148
+
149
+ async function runParallelStatus({ args, options = {}, logger, t }) {
150
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
151
+ const parallelDir = path.join(targetDir, '.aioson/context/parallel');
152
+
153
+ if (!(await exists(parallelDir))) {
154
+ throw new Error(t('parallel_status.parallel_missing', { path: parallelDir }));
155
+ }
156
+
157
+ const entries = await fs.readdir(parallelDir);
158
+ const laneIndices = entries
159
+ .map(parseLaneIndex)
160
+ .filter((value) => value !== null)
161
+ .sort((a, b) => a - b);
162
+
163
+ if (laneIndices.length === 0) {
164
+ throw new Error(t('parallel_status.no_lanes'));
165
+ }
166
+
167
+ const lanes = [];
168
+ for (const index of laneIndices) {
169
+ lanes.push(await parseLaneFile(parallelDir, index));
170
+ }
171
+
172
+ const statusCounts = createStatusCounts();
173
+ let scopeCount = 0;
174
+ let blockerCount = 0;
175
+ let deliverablesCompleted = 0;
176
+ let deliverablesTotal = 0;
177
+
178
+ for (const lane of lanes) {
179
+ const key = Object.prototype.hasOwnProperty.call(statusCounts, lane.status) ? lane.status : 'other';
180
+ statusCounts[key] += 1;
181
+ scopeCount += lane.scopeCount;
182
+ blockerCount += lane.blockerCount;
183
+ deliverablesCompleted += lane.deliverables.completed;
184
+ deliverablesTotal += lane.deliverables.total;
185
+ }
186
+
187
+ const sharedPath = path.join(parallelDir, 'shared-decisions.md');
188
+ const sharedExists = await exists(sharedPath);
189
+ const sharedDecisionEntries = sharedExists
190
+ ? countDecisionRows(await fs.readFile(sharedPath, 'utf8'))
191
+ : 0;
192
+
193
+ const output = {
194
+ ok: true,
195
+ targetDir,
196
+ parallelDir,
197
+ laneCount: lanes.length,
198
+ statusCounts,
199
+ scopeCount,
200
+ blockerCount,
201
+ deliverables: {
202
+ completed: deliverablesCompleted,
203
+ total: deliverablesTotal
204
+ },
205
+ sharedDecisions: {
206
+ exists: sharedExists,
207
+ entries: sharedDecisionEntries
208
+ },
209
+ lanes: lanes.map((lane) => ({
210
+ lane: lane.lane,
211
+ file: lane.file,
212
+ status: lane.status,
213
+ owner: lane.owner,
214
+ priority: lane.priority,
215
+ updatedAt: lane.updatedAt,
216
+ scopeCount: lane.scopeCount,
217
+ blockerCount: lane.blockerCount,
218
+ deliverables: lane.deliverables
219
+ }))
220
+ };
221
+
222
+ output.runtime = await recordRuntimeOperation(targetDir, {
223
+ agentName: 'orchestrator',
224
+ source: 'orchestration',
225
+ sessionKey: 'parallel:workspace',
226
+ title: 'Parallel orchestration workspace',
227
+ goal: 'Prepare and manage parallel development lanes',
228
+ runTitle: 'parallel:status',
229
+ message: 'Parallel status inspection started',
230
+ summary: `Parallel status inspected for ${output.laneCount} lanes`,
231
+ eventType: 'parallel.status_reported',
232
+ phase: 'parallel',
233
+ payload: {
234
+ command: 'parallel:status',
235
+ laneCount: output.laneCount,
236
+ statusCounts,
237
+ scopeCount,
238
+ blockerCount,
239
+ deliverables: output.deliverables,
240
+ sharedDecisions: output.sharedDecisions
241
+ }
242
+ });
243
+
244
+ if (options.json) {
245
+ return output;
246
+ }
247
+
248
+ logger.log(t('parallel_status.title', { path: targetDir }));
249
+ logger.log(t('parallel_status.lanes_count', { count: output.laneCount }));
250
+ logger.log(t('parallel_status.statuses_title'));
251
+ for (const key of Object.keys(statusCounts)) {
252
+ logger.log(
253
+ t('parallel_status.status_line', {
254
+ status: formatStatusLabel(key, t),
255
+ count: statusCounts[key]
256
+ })
257
+ );
258
+ }
259
+ logger.log(t('parallel_status.scopes_count', { count: scopeCount }));
260
+ logger.log(
261
+ t('parallel_status.deliverables_progress', {
262
+ completed: deliverablesCompleted,
263
+ total: deliverablesTotal
264
+ })
265
+ );
266
+ logger.log(t('parallel_status.blockers_count', { count: blockerCount }));
267
+ logger.log(
268
+ t('parallel_status.shared_decisions', {
269
+ count: sharedDecisionEntries
270
+ })
271
+ );
272
+ for (const lane of output.lanes) {
273
+ logger.log(
274
+ t('parallel_status.lane_line', {
275
+ lane: lane.lane,
276
+ status: formatStatusLabel(lane.status, t),
277
+ scope: lane.scopeCount,
278
+ blockers: lane.blockerCount
279
+ })
280
+ );
281
+ }
282
+
283
+ return output;
284
+ }
285
+
286
+ module.exports = {
287
+ runParallelStatus,
288
+ parseLaneIndex,
289
+ countDecisionRows
290
+ };
@@ -0,0 +1,185 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const { readTextIfExists, exists } = require('../utils');
5
+ const { validateProjectContextFile } = require('../context');
6
+
7
+ function makeCheck(id, ok, severity, message, hint = '') {
8
+ return { id, ok: Boolean(ok), severity, message: String(message || ''), hint: String(hint || '') };
9
+ }
10
+
11
+ function summarizeChecks(checks) {
12
+ const passed = checks.filter((c) => c.ok).length;
13
+ const failed = checks.filter((c) => !c.ok && c.severity === 'error').length;
14
+ const warnings = checks.filter((c) => !c.ok && c.severity === 'warn').length;
15
+ return { total: checks.length, passed, failed, warnings };
16
+ }
17
+
18
+ function formatPrefix(check, t) {
19
+ if (check.ok) return t('qa_doctor.prefix_ok');
20
+ if (check.severity === 'warn') return t('qa_doctor.prefix_warn');
21
+ return t('qa_doctor.prefix_fail');
22
+ }
23
+
24
+ function requirePlaywright() {
25
+ try {
26
+ return require('playwright');
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+
32
+ async function checkTargetUrl(url) {
33
+ if (!url) return { reachable: false, error: 'no_url' };
34
+ try {
35
+ const http = url.startsWith('https') ? require('node:https') : require('node:http');
36
+ await new Promise((resolve, reject) => {
37
+ const req = http.get(url, { timeout: 5000 }, (res) => {
38
+ res.destroy();
39
+ resolve(res.statusCode);
40
+ });
41
+ req.on('error', reject);
42
+ req.on('timeout', () => { req.destroy(); reject(new Error('timeout')); });
43
+ });
44
+ return { reachable: true, error: '' };
45
+ } catch (err) {
46
+ return { reachable: false, error: err.message };
47
+ }
48
+ }
49
+
50
+ function countAcItems(prdContent) {
51
+ if (!prdContent) return 0;
52
+ const tableMatches = prdContent.matchAll(/\|\s*(AC-\d+)\s*\|/g);
53
+ return [...tableMatches].length;
54
+ }
55
+
56
+ async function runQaDoctor({ args, options = {}, logger, t }) {
57
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
58
+ const configPath = path.join(targetDir, 'aios-qa.config.json');
59
+ const prdPath = path.join(targetDir, '.aioson/context/prd.md');
60
+ const checks = [];
61
+
62
+ // Check 1 — Playwright installed
63
+ const pw = requirePlaywright();
64
+ checks.push(makeCheck(
65
+ 'playwright.installed',
66
+ Boolean(pw),
67
+ 'error',
68
+ pw ? t('qa_doctor.playwright_ok') : t('qa_doctor.playwright_missing'),
69
+ pw ? '' : t('qa_doctor.playwright_missing_hint')
70
+ ));
71
+
72
+ // Check 2 — Chromium binary
73
+ if (pw) {
74
+ let chromiumOk = false;
75
+ try {
76
+ const execPath = pw.chromium.executablePath();
77
+ chromiumOk = Boolean(execPath) && await exists(execPath);
78
+ } catch {
79
+ chromiumOk = false;
80
+ }
81
+ checks.push(makeCheck(
82
+ 'chromium.binary',
83
+ chromiumOk,
84
+ 'error',
85
+ chromiumOk ? t('qa_doctor.chromium_ok') : t('qa_doctor.chromium_missing'),
86
+ chromiumOk ? '' : t('qa_doctor.chromium_missing_hint')
87
+ ));
88
+ }
89
+
90
+ // Check 3 — Config file
91
+ const configExists = await exists(configPath);
92
+ let config = null;
93
+ let configParsed = false;
94
+ let configError = '';
95
+
96
+ if (configExists) {
97
+ try {
98
+ const raw = await require('node:fs/promises').readFile(configPath, 'utf8');
99
+ config = JSON.parse(raw);
100
+ configParsed = true;
101
+ } catch (err) {
102
+ configError = err.message;
103
+ }
104
+ }
105
+
106
+ if (!configExists) {
107
+ checks.push(makeCheck('config.exists', false, 'error', t('qa_doctor.config_missing'), t('qa_doctor.config_missing_hint')));
108
+ } else if (!configParsed) {
109
+ checks.push(makeCheck('config.parsed', false, 'error', t('qa_doctor.config_invalid', { error: configError }), t('qa_doctor.config_missing_hint')));
110
+ } else {
111
+ checks.push(makeCheck('config.exists', true, 'info', t('qa_doctor.config_ok')));
112
+ }
113
+
114
+ // Check 4 — Target URL reachable
115
+ const configUrl = config && config.url ? config.url : '';
116
+ if (!configUrl) {
117
+ checks.push(makeCheck('url.reachable', false, 'warn', t('qa_doctor.url_missing'), t('qa_doctor.url_missing_hint')));
118
+ } else {
119
+ const { reachable, error } = await checkTargetUrl(configUrl);
120
+ checks.push(makeCheck(
121
+ 'url.reachable',
122
+ reachable,
123
+ 'warn',
124
+ reachable
125
+ ? t('qa_doctor.url_ok', { url: configUrl })
126
+ : t('qa_doctor.url_unreachable', { url: configUrl, error }),
127
+ reachable ? '' : t('qa_doctor.url_unreachable_hint')
128
+ ));
129
+ }
130
+
131
+ // Check 5 — project.context.md
132
+ const contextResult = await validateProjectContextFile(targetDir);
133
+ checks.push(makeCheck(
134
+ 'context.exists',
135
+ contextResult.exists,
136
+ 'warn',
137
+ contextResult.exists ? t('qa_doctor.context_ok') : t('qa_doctor.context_missing')
138
+ ));
139
+
140
+ // Check 6 — prd.md (optional enrichment)
141
+ const prdContent = await readTextIfExists(prdPath);
142
+ const acCount = countAcItems(prdContent || '');
143
+ checks.push(makeCheck(
144
+ 'prd.exists',
145
+ Boolean(prdContent),
146
+ 'warn',
147
+ prdContent
148
+ ? t('qa_doctor.prd_ok', { count: acCount })
149
+ : t('qa_doctor.prd_missing')
150
+ ));
151
+
152
+ const summary = summarizeChecks(checks);
153
+ const output = {
154
+ ok: summary.failed === 0,
155
+ targetDir,
156
+ configPath,
157
+ configExists,
158
+ configParsed,
159
+ url: configUrl,
160
+ checks,
161
+ summary
162
+ };
163
+
164
+ if (options.json) return output;
165
+
166
+ logger.log(t('qa_doctor.report_title', { path: targetDir }));
167
+ for (const check of checks) {
168
+ logger.log(t('qa_doctor.check_line', {
169
+ prefix: formatPrefix(check, t),
170
+ id: check.id,
171
+ message: check.message
172
+ }));
173
+ if (check.hint) logger.log(t('qa_doctor.hint_line', { hint: check.hint }));
174
+ }
175
+ logger.log(t('qa_doctor.summary', {
176
+ passed: summary.passed,
177
+ failed: summary.failed,
178
+ warnings: summary.warnings
179
+ }));
180
+
181
+ if (!output.ok) process.exitCode = 1;
182
+ return output;
183
+ }
184
+
185
+ module.exports = { runQaDoctor };
@@ -0,0 +1,161 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const fs = require('node:fs/promises');
5
+ const { readTextIfExists, ensureDir, exists } = require('../utils');
6
+ const { validateProjectContextFile } = require('../context');
7
+
8
+ const DEFAULT_PERSONAS = ['naive', 'hacker', 'power', 'mobile'];
9
+
10
+ const DEFAULT_SECURITY_PROBES = [
11
+ 'exposed_env_vars',
12
+ 'xss_inputs',
13
+ 'open_redirect',
14
+ 'sensitive_files',
15
+ 'idor_probe',
16
+ 'console_leaks',
17
+ 'debug_routes',
18
+ 'mixed_content',
19
+ 'sensitive_get_params'
20
+ ];
21
+
22
+ const DEFAULT_PERFORMANCE_THRESHOLDS = {
23
+ page_load_ms: 3000,
24
+ ttfb_ms: 800,
25
+ requests_max: 80,
26
+ transfer_max_kb: 2048
27
+ };
28
+
29
+ function extractFrontmatterValue(markdown, key) {
30
+ if (!markdown) return '';
31
+ const regex = new RegExp(`^-\\s*${key}:\\s*(.*)$`, 'im');
32
+ const match = String(markdown).match(regex);
33
+ return match ? String(match[1] || '').trim() : '';
34
+ }
35
+
36
+ function extractYamlValue(markdown, key) {
37
+ if (!markdown) return '';
38
+ const regex = new RegExp(`^${key}:\\s*(.*)$`, 'im');
39
+ const match = String(markdown).match(regex);
40
+ return match ? String(match[1] || '').trim().replace(/^['"]|['"]$/g, '') : '';
41
+ }
42
+
43
+ function parseAcItems(prdContent) {
44
+ if (!prdContent) return [];
45
+ const items = [];
46
+ // Match table rows: | AC-01 | description |
47
+ const tableRows = [...String(prdContent).matchAll(/\|\s*(AC-\d+)\s*\|\s*([^|]+)\|/g)];
48
+ for (const match of tableRows) {
49
+ items.push({ id: match[1].trim(), description: match[2].trim() });
50
+ }
51
+ // Match must-have items in MVP section
52
+ const mvpMatches = [...String(prdContent).matchAll(/🔴\s*([^\n]+)/g)];
53
+ for (const match of mvpMatches) {
54
+ if (items.length >= 20) break;
55
+ items.push({ id: `AC-${String(items.length + 1).padStart(2, '0')}`, description: match[1].trim() });
56
+ }
57
+ return items.slice(0, 20);
58
+ }
59
+
60
+ function parseBusinessRules(discoveryContent) {
61
+ if (!discoveryContent) return [];
62
+ const rules = [];
63
+ const matches = [...String(discoveryContent).matchAll(/[-*]\s*([A-Z][^\n]{10,80})/g)];
64
+ for (const match of matches.slice(0, 15)) {
65
+ rules.push(match[1].trim());
66
+ }
67
+ return rules;
68
+ }
69
+
70
+ async function runQaInit({ args, options = {}, logger, t }) {
71
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
72
+ const dryRun = Boolean(options['dry-run']);
73
+ const configPath = path.join(targetDir, 'aios-qa.config.json');
74
+
75
+ const contextResult = await validateProjectContextFile(targetDir);
76
+ const contextMarkdown = await readTextIfExists(path.join(targetDir, '.aioson/context/project.context.md'));
77
+ const prdContent = await readTextIfExists(path.join(targetDir, '.aioson/context/prd.md'));
78
+ const discoveryContent = await readTextIfExists(path.join(targetDir, '.aioson/context/discovery.md'));
79
+
80
+ const contextData = contextResult.parsed && contextResult.data ? contextResult.data : {};
81
+
82
+ // Resolve URL: CLI flag > context app_url > context framework hint > ask
83
+ let url = String(options.url || '').trim();
84
+ if (!url && contextMarkdown) {
85
+ url = extractFrontmatterValue(contextMarkdown, 'app_url') ||
86
+ extractFrontmatterValue(contextMarkdown, 'dev_url') || '';
87
+ }
88
+ if (!url) {
89
+ url = 'http://localhost:3000';
90
+ }
91
+
92
+ const projectName = String(contextData.project_name || path.basename(targetDir) || 'Project');
93
+ const language = String(contextData.conversation_language || 'en');
94
+
95
+ // Parse prd.md for AC items
96
+ const acItems = parseAcItems(prdContent);
97
+ const businessRules = parseBusinessRules(discoveryContent);
98
+
99
+ if (contextResult.exists) {
100
+ logger.log(t('qa_init.context_found', { name: projectName, url }));
101
+ }
102
+
103
+ if (prdContent) {
104
+ logger.log(t('qa_init.prd_found', { count: acItems.length }));
105
+ } else {
106
+ logger.log(t('qa_init.prd_missing'));
107
+ }
108
+
109
+ const config = {
110
+ project_name: projectName,
111
+ url,
112
+ language,
113
+ personas: DEFAULT_PERSONAS,
114
+ security_probes: DEFAULT_SECURITY_PROBES,
115
+ performance_thresholds: DEFAULT_PERFORMANCE_THRESHOLDS,
116
+ accessibility: true,
117
+ network_capture: true,
118
+ screenshot_on_finding: true,
119
+ scenarios: acItems,
120
+ business_rules: businessRules,
121
+ generated_at: new Date().toISOString(),
122
+ aioson_version: require('../../package.json').version
123
+ };
124
+
125
+ if (!dryRun) {
126
+ await ensureDir(path.dirname(configPath));
127
+ await fs.writeFile(configPath, `${JSON.stringify(config, null, 2)}\n`, 'utf8');
128
+ }
129
+
130
+ const output = {
131
+ ok: true,
132
+ targetDir,
133
+ configPath,
134
+ dryRun,
135
+ written: !dryRun,
136
+ url,
137
+ projectName,
138
+ scenariosCount: acItems.length,
139
+ personasCount: DEFAULT_PERSONAS.length,
140
+ probesCount: DEFAULT_SECURITY_PROBES.length,
141
+ config
142
+ };
143
+
144
+ if (options.json) return output;
145
+
146
+ logger.log(
147
+ dryRun
148
+ ? t('qa_init.dry_run_generated', { path: configPath })
149
+ : t('qa_init.generated', { path: configPath })
150
+ );
151
+ logger.log(t('qa_init.scenarios_count', { count: acItems.length }));
152
+ logger.log(t('qa_init.personas_count', { count: DEFAULT_PERSONAS.length }));
153
+ logger.log(t('qa_init.probes_count', { count: DEFAULT_SECURITY_PROBES.length }));
154
+ logger.log(t('qa_init.next_steps'));
155
+ logger.log(t('qa_init.step_doctor'));
156
+ logger.log(t('qa_init.step_run'));
157
+
158
+ return output;
159
+ }
160
+
161
+ module.exports = { runQaInit };
@@ -0,0 +1,58 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const fs = require('node:fs/promises');
5
+ const { readTextIfExists, exists } = require('../utils');
6
+
7
+ async function runQaReport({ args, options = {}, logger, t }) {
8
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
9
+ const mdPath = path.join(targetDir, 'aios-qa-report.md');
10
+ const jsonPath = path.join(targetDir, 'aios-qa-report.json');
11
+
12
+ if (options.json) {
13
+ if (!(await exists(jsonPath))) {
14
+ return { ok: false, error: 'report_not_found', path: jsonPath };
15
+ }
16
+ try {
17
+ const raw = await fs.readFile(jsonPath, 'utf8');
18
+ return { ok: true, ...JSON.parse(raw) };
19
+ } catch (err) {
20
+ return { ok: false, error: err.message };
21
+ }
22
+ }
23
+
24
+ if (options.html) {
25
+ if (!(await exists(jsonPath))) {
26
+ logger.error(t('qa_report.not_found'));
27
+ process.exitCode = 1;
28
+ return { ok: false, error: 'report_not_found' };
29
+ }
30
+ try {
31
+ const raw = await fs.readFile(jsonPath, 'utf8');
32
+ const data = JSON.parse(raw);
33
+ const { writeHtmlReport } = require('../qa-html-report');
34
+ const screenshotsDir = path.join(targetDir, 'aios-qa-screenshots');
35
+ const result = await writeHtmlReport(
36
+ targetDir, data.project || 'Project', data.url || '',
37
+ data.findings || [], data.ac_coverage || [], data.performance || null,
38
+ data.mode || 'run', screenshotsDir, { routes: data.routes_scanned }
39
+ );
40
+ logger.log(t('qa_report.html_report_written', { path: result.htmlPath }));
41
+ return { ok: true, htmlPath: result.htmlPath };
42
+ } catch (err) {
43
+ return { ok: false, error: err.message };
44
+ }
45
+ }
46
+
47
+ const content = await readTextIfExists(mdPath);
48
+ if (!content) {
49
+ logger.error(t('qa_report.not_found'));
50
+ process.exitCode = 1;
51
+ return { ok: false, error: 'report_not_found' };
52
+ }
53
+
54
+ logger.log(content);
55
+ return { ok: true, path: mdPath };
56
+ }
57
+
58
+ module.exports = { runQaReport };