@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,437 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const { validateProjectContextFile } = require('../context');
6
+ const { exists, ensureDir } = require('../utils');
7
+ const {
8
+ parseWorkers,
9
+ renderSharedDecisions,
10
+ renderAgentStatus,
11
+ PREREQUISITE_FILES
12
+ } = require('./parallel-init');
13
+
14
+ const DEFAULT_FIX_WORKERS = 3;
15
+
16
+ function makeCheck(id, ok, severity, message, hint = '') {
17
+ return {
18
+ id,
19
+ ok: Boolean(ok),
20
+ severity,
21
+ message: String(message || ''),
22
+ hint: String(hint || '')
23
+ };
24
+ }
25
+
26
+ function buildLaneFilename(index) {
27
+ return `agent-${index}.status.md`;
28
+ }
29
+
30
+ function parseLaneIndex(fileName) {
31
+ const match = String(fileName || '').match(/^agent-(\d+)\.status\.md$/);
32
+ if (!match) return null;
33
+ const value = Number(match[1]);
34
+ if (!Number.isFinite(value) || value <= 0) return null;
35
+ return Math.floor(value);
36
+ }
37
+
38
+ function laneRange(count) {
39
+ const output = [];
40
+ for (let i = 1; i <= count; i += 1) {
41
+ output.push(i);
42
+ }
43
+ return output;
44
+ }
45
+
46
+ function summarizeChecks(checks) {
47
+ const passed = checks.filter((item) => item.ok).length;
48
+ const failed = checks.filter((item) => !item.ok && item.severity === 'error').length;
49
+ const warnings = checks.filter((item) => !item.ok && item.severity === 'warn').length;
50
+ return {
51
+ total: checks.length,
52
+ passed,
53
+ failed,
54
+ warnings
55
+ };
56
+ }
57
+
58
+ function formatCheckPrefix(check, t) {
59
+ if (check.ok) return t('parallel_doctor.prefix_ok');
60
+ if (check.severity === 'warn') return t('parallel_doctor.prefix_warn');
61
+ return t('parallel_doctor.prefix_fail');
62
+ }
63
+
64
+ async function collectPrerequisites(targetDir) {
65
+ const items = [];
66
+ for (const rel of PREREQUISITE_FILES) {
67
+ items.push({
68
+ path: rel,
69
+ exists: await exists(path.join(targetDir, rel))
70
+ });
71
+ }
72
+ return items;
73
+ }
74
+
75
+ function resolveExpectedWorkers(state, workersOption) {
76
+ if (workersOption !== undefined && workersOption !== null) return workersOption;
77
+ if (state.laneIndices.length > 0) return Math.max(...state.laneIndices);
78
+ return DEFAULT_FIX_WORKERS;
79
+ }
80
+
81
+ async function inspectParallelState(targetDir, workersOption) {
82
+ const parallelDir = path.join(targetDir, '.aioson/context/parallel');
83
+ const dirExists = await exists(parallelDir);
84
+ const entries = dirExists ? await fs.readdir(parallelDir) : [];
85
+ const sharedExists = entries.includes('shared-decisions.md');
86
+ const laneIndices = entries
87
+ .map(parseLaneIndex)
88
+ .filter((value) => value !== null)
89
+ .sort((a, b) => a - b);
90
+ const laneFiles = laneIndices.map((index) => buildLaneFilename(index));
91
+
92
+ const expectedWorkers = resolveExpectedWorkers(
93
+ {
94
+ laneIndices
95
+ },
96
+ workersOption
97
+ );
98
+ const expectedLaneIndices = laneRange(expectedWorkers);
99
+ const missingLaneIndices = expectedLaneIndices.filter((index) => !laneIndices.includes(index));
100
+
101
+ return {
102
+ parallelDir,
103
+ dirExists,
104
+ entries,
105
+ sharedExists,
106
+ laneIndices,
107
+ laneFiles,
108
+ expectedWorkers,
109
+ expectedLaneIndices,
110
+ missingLaneIndices
111
+ };
112
+ }
113
+
114
+ function buildChecks(context, state, prerequisites, workersOption, force, t) {
115
+ const checks = [];
116
+
117
+ checks.push(
118
+ makeCheck(
119
+ 'context.exists',
120
+ context.exists,
121
+ 'error',
122
+ context.exists
123
+ ? t('parallel_doctor.check_context_exists_ok')
124
+ : t('parallel_doctor.check_context_exists_missing'),
125
+ context.exists ? '' : t('parallel_doctor.check_context_exists_hint')
126
+ )
127
+ );
128
+
129
+ checks.push(
130
+ makeCheck(
131
+ 'context.parsed',
132
+ context.parsed,
133
+ 'error',
134
+ context.parsed
135
+ ? t('parallel_doctor.check_context_parsed_ok')
136
+ : t('parallel_doctor.check_context_parsed_invalid'),
137
+ context.parsed ? '' : t('parallel_doctor.check_context_parsed_hint')
138
+ )
139
+ );
140
+
141
+ const classification = String(context.data && context.data.classification ? context.data.classification : '');
142
+ const isMedium = classification === 'MEDIUM';
143
+ const classificationOk = isMedium || force;
144
+ checks.push(
145
+ makeCheck(
146
+ 'context.classification',
147
+ classificationOk,
148
+ 'error',
149
+ classificationOk
150
+ ? t('parallel_doctor.check_context_classification_ok', {
151
+ classification: classification || t('parallel_doctor.classification_unknown')
152
+ })
153
+ : t('parallel_doctor.check_context_classification_invalid', {
154
+ classification: classification || t('parallel_doctor.classification_unknown')
155
+ }),
156
+ classificationOk ? '' : t('parallel_doctor.check_context_classification_hint')
157
+ )
158
+ );
159
+
160
+ checks.push(
161
+ makeCheck(
162
+ 'parallel.dir',
163
+ state.dirExists,
164
+ 'error',
165
+ state.dirExists
166
+ ? t('parallel_doctor.check_parallel_dir_ok')
167
+ : t('parallel_doctor.check_parallel_dir_missing'),
168
+ state.dirExists ? '' : t('parallel_doctor.check_parallel_dir_hint')
169
+ )
170
+ );
171
+
172
+ checks.push(
173
+ makeCheck(
174
+ 'parallel.shared',
175
+ state.sharedExists,
176
+ 'error',
177
+ state.sharedExists
178
+ ? t('parallel_doctor.check_parallel_shared_ok')
179
+ : t('parallel_doctor.check_parallel_shared_missing'),
180
+ state.sharedExists ? '' : t('parallel_doctor.check_parallel_shared_hint')
181
+ )
182
+ );
183
+
184
+ checks.push(
185
+ makeCheck(
186
+ 'parallel.lanes.present',
187
+ state.laneIndices.length > 0,
188
+ 'error',
189
+ state.laneIndices.length > 0
190
+ ? t('parallel_doctor.check_lanes_present_ok', {
191
+ count: state.laneIndices.length
192
+ })
193
+ : t('parallel_doctor.check_lanes_present_missing'),
194
+ state.laneIndices.length > 0 ? '' : t('parallel_doctor.check_lanes_present_hint')
195
+ )
196
+ );
197
+
198
+ checks.push(
199
+ makeCheck(
200
+ 'parallel.lanes.sequence',
201
+ state.missingLaneIndices.length === 0,
202
+ 'error',
203
+ state.missingLaneIndices.length === 0
204
+ ? t('parallel_doctor.check_lanes_sequence_ok', {
205
+ workers: state.expectedWorkers
206
+ })
207
+ : t('parallel_doctor.check_lanes_sequence_missing', {
208
+ lanes: state.missingLaneIndices.join(', ')
209
+ }),
210
+ state.missingLaneIndices.length === 0 ? '' : t('parallel_doctor.check_lanes_sequence_hint')
211
+ )
212
+ );
213
+
214
+ if (workersOption !== undefined && workersOption !== null) {
215
+ checks.push(
216
+ makeCheck(
217
+ 'parallel.workers.option',
218
+ state.expectedWorkers === workersOption,
219
+ 'info',
220
+ t('parallel_doctor.check_workers_option', { workers: workersOption })
221
+ )
222
+ );
223
+ }
224
+
225
+ const missingPrereq = prerequisites.filter((item) => !item.exists).length;
226
+ checks.push(
227
+ makeCheck(
228
+ 'parallel.prerequisites',
229
+ missingPrereq === 0,
230
+ missingPrereq === 0 ? 'info' : 'warn',
231
+ missingPrereq === 0
232
+ ? t('parallel_doctor.check_prereq_ok')
233
+ : t('parallel_doctor.check_prereq_missing', { count: missingPrereq }),
234
+ missingPrereq === 0 ? '' : t('parallel_doctor.check_prereq_hint')
235
+ )
236
+ );
237
+
238
+ return checks;
239
+ }
240
+
241
+ async function applyParallelFixes(targetDir, context, state, options) {
242
+ const dryRun = Boolean(options.dryRun);
243
+ const generatedAt = new Date().toISOString();
244
+ const projectName =
245
+ String((context.data && context.data.project_name) || '').trim() || path.basename(targetDir) || 'project';
246
+ const classification = String((context.data && context.data.classification) || 'MEDIUM');
247
+ const actions = [];
248
+ let changedCount = 0;
249
+
250
+ if (!state.dirExists) {
251
+ if (!dryRun) {
252
+ await ensureDir(state.parallelDir);
253
+ }
254
+ actions.push({
255
+ id: 'parallel_dir',
256
+ applied: true,
257
+ count: 1
258
+ });
259
+ changedCount += 1;
260
+ } else {
261
+ actions.push({
262
+ id: 'parallel_dir',
263
+ applied: false,
264
+ skipped: true,
265
+ count: 0
266
+ });
267
+ }
268
+
269
+ if (!state.sharedExists) {
270
+ const sharedPath = path.join(state.parallelDir, 'shared-decisions.md');
271
+ const content = renderSharedDecisions({
272
+ projectName,
273
+ classification,
274
+ workers: state.expectedWorkers,
275
+ generatedAt
276
+ });
277
+ if (!dryRun) {
278
+ await ensureDir(path.dirname(sharedPath));
279
+ await fs.writeFile(sharedPath, content, 'utf8');
280
+ }
281
+ actions.push({
282
+ id: 'shared_decisions',
283
+ applied: true,
284
+ count: 1
285
+ });
286
+ changedCount += 1;
287
+ } else {
288
+ actions.push({
289
+ id: 'shared_decisions',
290
+ applied: false,
291
+ skipped: true,
292
+ count: 0
293
+ });
294
+ }
295
+
296
+ if (state.missingLaneIndices.length > 0) {
297
+ for (const index of state.missingLaneIndices) {
298
+ const lanePath = path.join(state.parallelDir, buildLaneFilename(index));
299
+ const content = renderAgentStatus({
300
+ index,
301
+ generatedAt
302
+ });
303
+ if (!dryRun) {
304
+ await ensureDir(path.dirname(lanePath));
305
+ await fs.writeFile(lanePath, content, 'utf8');
306
+ }
307
+ }
308
+ actions.push({
309
+ id: 'lane_files',
310
+ applied: true,
311
+ count: state.missingLaneIndices.length
312
+ });
313
+ changedCount += state.missingLaneIndices.length;
314
+ } else {
315
+ actions.push({
316
+ id: 'lane_files',
317
+ applied: false,
318
+ skipped: true,
319
+ count: 0
320
+ });
321
+ }
322
+
323
+ return {
324
+ dryRun,
325
+ actions,
326
+ changedCount
327
+ };
328
+ }
329
+
330
+ async function runParallelDoctor({ args, options = {}, logger, t }) {
331
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
332
+ const dryRun = Boolean(options['dry-run']);
333
+ const fix = Boolean(options.fix);
334
+ const force = Boolean(options.force);
335
+ const workersOptionRaw = options.workers;
336
+ const workersOption = workersOptionRaw !== undefined ? parseWorkers(workersOptionRaw) : undefined;
337
+ if (workersOptionRaw !== undefined && workersOption === null) {
338
+ throw new Error(
339
+ t('parallel_doctor.invalid_workers', {
340
+ min: 2,
341
+ max: 6
342
+ })
343
+ );
344
+ }
345
+
346
+ const context = await validateProjectContextFile(targetDir);
347
+ const prerequisites = await collectPrerequisites(targetDir);
348
+ let state = await inspectParallelState(targetDir, workersOption);
349
+ let checks = buildChecks(context, state, prerequisites, workersOption, force, t);
350
+ let fixResult = null;
351
+
352
+ if (fix) {
353
+ const classification = String((context.data && context.data.classification) || '');
354
+ if (classification !== 'MEDIUM' && !force) {
355
+ throw new Error(
356
+ t('parallel_doctor.requires_medium', {
357
+ classification: classification || t('parallel_doctor.classification_unknown')
358
+ })
359
+ );
360
+ }
361
+ fixResult = await applyParallelFixes(targetDir, context, state, {
362
+ dryRun
363
+ });
364
+
365
+ state = await inspectParallelState(targetDir, workersOption);
366
+ checks = buildChecks(context, state, prerequisites, workersOption, force, t);
367
+ }
368
+
369
+ const summary = summarizeChecks(checks);
370
+ const output = {
371
+ ok: summary.failed === 0,
372
+ targetDir,
373
+ workers: state.expectedWorkers,
374
+ fix: {
375
+ enabled: fix,
376
+ dryRun,
377
+ force,
378
+ ...(fixResult
379
+ ? {
380
+ changedCount: fixResult.changedCount,
381
+ actions: fixResult.actions
382
+ }
383
+ : {})
384
+ },
385
+ state: {
386
+ parallelDir: state.parallelDir,
387
+ dirExists: state.dirExists,
388
+ sharedExists: state.sharedExists,
389
+ laneFiles: state.laneFiles,
390
+ laneIndices: state.laneIndices,
391
+ missingLaneIndices: state.missingLaneIndices
392
+ },
393
+ checks,
394
+ summary
395
+ };
396
+
397
+ if (options.json) {
398
+ return output;
399
+ }
400
+
401
+ logger.log(t('parallel_doctor.report_title', { path: targetDir }));
402
+ for (const check of checks) {
403
+ logger.log(
404
+ t('parallel_doctor.check_line', {
405
+ prefix: formatCheckPrefix(check, t),
406
+ id: check.id,
407
+ message: check.message
408
+ })
409
+ );
410
+ if (check.hint) {
411
+ logger.log(t('parallel_doctor.hint_line', { hint: check.hint }));
412
+ }
413
+ }
414
+ logger.log(
415
+ t('parallel_doctor.summary', {
416
+ passed: summary.passed,
417
+ failed: summary.failed,
418
+ warnings: summary.warnings
419
+ })
420
+ );
421
+
422
+ if (fixResult) {
423
+ logger.log(
424
+ dryRun
425
+ ? t('parallel_doctor.fix_summary_dry_run', { count: fixResult.changedCount })
426
+ : t('parallel_doctor.fix_summary', { count: fixResult.changedCount })
427
+ );
428
+ }
429
+
430
+ return output;
431
+ }
432
+
433
+ module.exports = {
434
+ runParallelDoctor,
435
+ parseLaneIndex,
436
+ summarizeChecks
437
+ };
@@ -0,0 +1,249 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const { validateProjectContextFile } = require('../context');
6
+ const { ensureDir, exists, toRelativeSafe } = require('../utils');
7
+ const { recordRuntimeOperation } = require('../execution-gateway');
8
+
9
+ const MIN_WORKERS = 2;
10
+ const MAX_WORKERS = 6;
11
+ const DEFAULT_WORKERS = 3;
12
+ const PREREQUISITE_FILES = [
13
+ '.aioson/context/discovery.md',
14
+ '.aioson/context/architecture.md',
15
+ '.aioson/context/prd.md'
16
+ ];
17
+
18
+ function normalizeClassification(value) {
19
+ return String(value || '').trim().toUpperCase();
20
+ }
21
+
22
+ function parseWorkers(value) {
23
+ if (value === undefined || value === null || value === '') return DEFAULT_WORKERS;
24
+ const num = Number(value);
25
+ if (!Number.isFinite(num)) return null;
26
+ const normalized = Math.floor(num);
27
+ if (normalized < MIN_WORKERS || normalized > MAX_WORKERS) return null;
28
+ return normalized;
29
+ }
30
+
31
+ function renderSharedDecisions(input) {
32
+ return `# Shared Decisions
33
+
34
+ ## Session
35
+ - Project: ${input.projectName}
36
+ - Classification: ${input.classification}
37
+ - Workers: ${input.workers}
38
+ - Generated at: ${input.generatedAt}
39
+
40
+ ## Protocol
41
+ - Record only decisions that affect more than one parallel lane.
42
+ - When a decision changes a contract, update all impacted lane files.
43
+ - Keep entries concise and include rationale plus impact.
44
+
45
+ ## Decision Log
46
+ | time | decision | rationale | impact |
47
+ |------|----------|-----------|--------|
48
+ | ${input.generatedAt} | Parallel workspace initialized | Baseline orchestration context created | Ready for @orchestrator assignment |
49
+ `;
50
+ }
51
+
52
+ function renderAgentStatus(input) {
53
+ const lane = `agent-${input.index}`;
54
+ return `# Parallel Lane Status - ${lane}
55
+
56
+ ## Metadata
57
+ - lane: ${lane}
58
+ - role: @dev
59
+ - owner: [unassigned]
60
+ - status: pending
61
+ - priority: medium
62
+ - updated_at: ${input.generatedAt}
63
+
64
+ ## Scope
65
+ - [define module or feature boundary]
66
+
67
+ ## Dependencies
68
+ - [list dependencies on other lanes or shared decisions]
69
+
70
+ ## Deliverables
71
+ - [ ] Code changes completed
72
+ - [ ] Self-check completed
73
+ - [ ] Handoff note written
74
+
75
+ ## Blockers
76
+ - [none]
77
+
78
+ ## Notes
79
+ - Created by \`aioson parallel:init\`.
80
+ `;
81
+ }
82
+
83
+ async function collectPrerequisites(targetDir) {
84
+ const items = [];
85
+ for (const rel of PREREQUISITE_FILES) {
86
+ const abs = path.join(targetDir, rel);
87
+ items.push({
88
+ path: rel,
89
+ exists: await exists(abs)
90
+ });
91
+ }
92
+ return items;
93
+ }
94
+
95
+ function buildTargetFiles(targetDir, workers) {
96
+ const parallelDir = path.join(targetDir, '.aioson/context/parallel');
97
+ const files = [path.join(parallelDir, 'shared-decisions.md')];
98
+ for (let i = 1; i <= workers; i += 1) {
99
+ files.push(path.join(parallelDir, `agent-${i}.status.md`));
100
+ }
101
+ return files;
102
+ }
103
+
104
+ async function runParallelInit({ args, options = {}, logger, t }) {
105
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
106
+ const dryRun = Boolean(options['dry-run']);
107
+ const force = Boolean(options.force);
108
+ const workers = parseWorkers(options.workers);
109
+
110
+ if (workers === null) {
111
+ throw new Error(
112
+ t('parallel_init.invalid_workers', {
113
+ min: MIN_WORKERS,
114
+ max: MAX_WORKERS
115
+ })
116
+ );
117
+ }
118
+
119
+ const context = await validateProjectContextFile(targetDir);
120
+ const contextPath = path.join(targetDir, '.aioson/context/project.context.md');
121
+ if (!context.exists) {
122
+ throw new Error(t('parallel_init.context_missing', { path: contextPath }));
123
+ }
124
+ if (!context.parsed) {
125
+ throw new Error(t('parallel_init.context_invalid', { path: contextPath }));
126
+ }
127
+
128
+ const classification = normalizeClassification(
129
+ options.classification || (context.data && context.data.classification) || ''
130
+ );
131
+ if (classification !== 'MEDIUM' && !force) {
132
+ throw new Error(
133
+ t('parallel_init.requires_medium', {
134
+ classification: classification || t('parallel_init.classification_unknown')
135
+ })
136
+ );
137
+ }
138
+
139
+ const generatedAt = new Date().toISOString();
140
+ const projectName =
141
+ String((context.data && context.data.project_name) || '').trim() || path.basename(targetDir) || 'project';
142
+ const prerequisiteChecks = await collectPrerequisites(targetDir);
143
+ const missingPrerequisites = prerequisiteChecks
144
+ .filter((item) => !item.exists)
145
+ .map((item) => item.path);
146
+
147
+ const targetFiles = buildTargetFiles(targetDir, workers);
148
+ const existingFiles = [];
149
+ for (const absPath of targetFiles) {
150
+ if (await exists(absPath)) {
151
+ existingFiles.push(toRelativeSafe(targetDir, absPath));
152
+ }
153
+ }
154
+
155
+ if (existingFiles.length > 0 && !force) {
156
+ throw new Error(
157
+ t('parallel_init.already_exists', {
158
+ count: existingFiles.length
159
+ })
160
+ );
161
+ }
162
+
163
+ if (!dryRun) {
164
+ const parallelDir = path.join(targetDir, '.aioson/context/parallel');
165
+ await ensureDir(parallelDir);
166
+
167
+ const sharedContent = renderSharedDecisions({
168
+ projectName,
169
+ classification: classification || 'MEDIUM',
170
+ workers,
171
+ generatedAt
172
+ });
173
+ await fs.writeFile(path.join(parallelDir, 'shared-decisions.md'), sharedContent, 'utf8');
174
+
175
+ for (let i = 1; i <= workers; i += 1) {
176
+ const statusContent = renderAgentStatus({ index: i, generatedAt });
177
+ await fs.writeFile(path.join(parallelDir, `agent-${i}.status.md`), statusContent, 'utf8');
178
+ }
179
+ }
180
+
181
+ const output = {
182
+ ok: true,
183
+ targetDir,
184
+ classification: classification || 'MEDIUM',
185
+ workers,
186
+ dryRun,
187
+ force,
188
+ generatedAt,
189
+ files: targetFiles.map((absPath) => toRelativeSafe(targetDir, absPath)),
190
+ existingFiles,
191
+ prerequisites: prerequisiteChecks,
192
+ missingPrerequisites
193
+ };
194
+
195
+ if (!dryRun) {
196
+ output.runtime = await recordRuntimeOperation(targetDir, {
197
+ agentName: 'orchestrator',
198
+ source: 'orchestration',
199
+ sessionKey: 'parallel:workspace',
200
+ title: 'Parallel orchestration workspace',
201
+ goal: 'Prepare and manage parallel development lanes',
202
+ runTitle: 'parallel:init',
203
+ message: 'Parallel workspace initialization started',
204
+ summary: `Parallel workspace initialized with ${workers} lanes`,
205
+ eventType: 'parallel.initialized',
206
+ phase: 'parallel',
207
+ payload: {
208
+ command: 'parallel:init',
209
+ classification: output.classification,
210
+ workers,
211
+ files: output.files,
212
+ missingPrerequisites
213
+ }
214
+ });
215
+ }
216
+
217
+ if (options.json) {
218
+ return output;
219
+ }
220
+
221
+ logger.log(
222
+ dryRun
223
+ ? t('parallel_init.dry_run_prepared', { path: path.join(targetDir, '.aioson/context/parallel') })
224
+ : t('parallel_init.prepared', { path: path.join(targetDir, '.aioson/context/parallel') })
225
+ );
226
+ logger.log(t('parallel_init.workers_count', { count: workers }));
227
+ logger.log(t('parallel_init.files_count', { count: output.files.length }));
228
+ if (missingPrerequisites.length > 0) {
229
+ logger.log(
230
+ t('parallel_init.missing_prereq_count', {
231
+ count: missingPrerequisites.length
232
+ })
233
+ );
234
+ }
235
+ for (const file of output.files) {
236
+ logger.log(t('parallel_init.file_line', { file }));
237
+ }
238
+
239
+ return output;
240
+ }
241
+
242
+ module.exports = {
243
+ runParallelInit,
244
+ parseWorkers,
245
+ normalizeClassification,
246
+ renderSharedDecisions,
247
+ renderAgentStatus,
248
+ PREREQUISITE_FILES
249
+ };