@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,358 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const { openRuntimeDb } = require('../runtime-store');
6
+ const { exists } = require('../utils');
7
+ const { runSquadValidate } = require('./squad-validate');
8
+
9
+ function normalizeRel(value) {
10
+ return String(value || '')
11
+ .replace(/\\/g, '/')
12
+ .replace(/^\.\//, '')
13
+ .replace(/\/+$/, '');
14
+ }
15
+
16
+ function asObject(value) {
17
+ return value && typeof value === 'object' && !Array.isArray(value) ? value : null;
18
+ }
19
+
20
+ function asArray(value) {
21
+ return Array.isArray(value) ? value : [];
22
+ }
23
+
24
+ async function readJsonIfExists(filePath) {
25
+ try {
26
+ const raw = await fs.readFile(filePath, 'utf8');
27
+ return JSON.parse(raw);
28
+ } catch {
29
+ return null;
30
+ }
31
+ }
32
+
33
+ async function pathExists(targetPath) {
34
+ try {
35
+ await fs.access(targetPath);
36
+ return true;
37
+ } catch {
38
+ return false;
39
+ }
40
+ }
41
+
42
+ async function listFilesRecursive(rootDir) {
43
+ const result = [];
44
+ const queue = [rootDir];
45
+
46
+ while (queue.length > 0) {
47
+ const current = queue.shift();
48
+ const entries = await fs.readdir(current, { withFileTypes: true }).catch(() => []);
49
+ for (const entry of entries) {
50
+ const fullPath = path.join(current, entry.name);
51
+ if (entry.isDirectory()) {
52
+ queue.push(fullPath);
53
+ } else if (entry.isFile()) {
54
+ result.push(fullPath);
55
+ }
56
+ }
57
+ }
58
+
59
+ return result;
60
+ }
61
+
62
+ async function resolveSquadSlug(targetDir, requestedSlug) {
63
+ if (requestedSlug) return String(requestedSlug).trim();
64
+
65
+ const metadataDir = path.join(targetDir, '.aioson', 'squads');
66
+ const agentsDir = path.join(targetDir, 'agents');
67
+ const candidates = new Set();
68
+
69
+ const metadataEntries = await fs.readdir(metadataDir, { withFileTypes: true }).catch(() => []);
70
+ for (const entry of metadataEntries) {
71
+ if (entry.isDirectory()) candidates.add(entry.name);
72
+ if (entry.isFile() && entry.name.endsWith('.md')) candidates.add(entry.name.replace(/\.md$/i, ''));
73
+ }
74
+
75
+ const agentEntries = await fs.readdir(agentsDir, { withFileTypes: true }).catch(() => []);
76
+ for (const entry of agentEntries) {
77
+ if (entry.isDirectory()) candidates.add(entry.name);
78
+ }
79
+
80
+ const slugs = Array.from(candidates).filter(Boolean);
81
+ if (slugs.length === 1) return slugs[0];
82
+ if (slugs.length === 0) {
83
+ throw new Error('No squad found. Create a squad first or provide --squad=<slug>.');
84
+ }
85
+ throw new Error('Multiple squads found. Provide --squad=<slug>.');
86
+ }
87
+
88
+ async function parseSquadPaths(targetDir, slug) {
89
+ const packageDir = path.join(targetDir, '.aioson', 'squads', slug);
90
+ const hasPackageDir = await pathExists(packageDir);
91
+ const metadataPath = hasPackageDir
92
+ ? path.join(packageDir, 'squad.md')
93
+ : path.join(targetDir, '.aioson', 'squads', `${slug}.md`);
94
+ const manifestPath = hasPackageDir
95
+ ? path.join(packageDir, 'squad.manifest.json')
96
+ : path.join(targetDir, 'agents', slug, 'squad.manifest.json');
97
+ const rulesPath = hasPackageDir
98
+ ? path.join(packageDir, 'agents', 'agents.md')
99
+ : path.join(targetDir, 'agents', slug, 'agents.md');
100
+ const designDocPath = hasPackageDir
101
+ ? path.join(packageDir, 'docs', 'design-doc.md')
102
+ : path.join(targetDir, 'agents', slug, 'design-doc.md');
103
+ const readinessPath = hasPackageDir
104
+ ? path.join(packageDir, 'docs', 'readiness.md')
105
+ : path.join(targetDir, 'agents', slug, 'readiness.md');
106
+
107
+ const manifest = (await readJsonIfExists(manifestPath)) || {};
108
+ const rules = asObject(manifest.rules) || {};
109
+ const packageInfo = asObject(manifest.package) || {};
110
+
111
+ return {
112
+ packageDir,
113
+ metadataPath,
114
+ manifestPath,
115
+ rulesPath,
116
+ designDocPath,
117
+ readinessPath,
118
+ manifest,
119
+ agentsDir: hasPackageDir
120
+ ? path.join(targetDir, normalizeRel(packageInfo.agentsDir || `.aioson/squads/${slug}/agents`))
121
+ : path.join(targetDir, 'agents', slug),
122
+ outputDir: path.join(targetDir, normalizeRel(rules.outputsDir || `output/${slug}`)),
123
+ logsDir: path.join(targetDir, normalizeRel(rules.logsDir || `aios-logs/${slug}`)),
124
+ mediaDir: path.join(targetDir, normalizeRel(rules.mediaDir || `media/${slug}`))
125
+ };
126
+ }
127
+
128
+ function makeCheck(id, ok, severity, message, meta = {}) {
129
+ return { id, ok, severity, message, ...meta };
130
+ }
131
+
132
+ function inferStale(run, staleMinutes) {
133
+ const updatedAt = Date.parse(run.updated_at || run.updatedAt || '');
134
+ if (!updatedAt) return false;
135
+ return Date.now() - updatedAt > staleMinutes * 60 * 1000;
136
+ }
137
+
138
+ async function detectContentCandidates(outputDir, projectRoot) {
139
+ const existsOutput = await pathExists(outputDir);
140
+ if (!existsOutput) return [];
141
+
142
+ const allFiles = await listFilesRecursive(outputDir);
143
+ const contentJsonDirs = new Set(
144
+ allFiles
145
+ .filter((filePath) => path.basename(filePath).toLowerCase() === 'content.json')
146
+ .map((filePath) => path.dirname(filePath))
147
+ );
148
+
149
+ return allFiles
150
+ .filter((filePath) => {
151
+ const base = path.basename(filePath).toLowerCase();
152
+ const ext = path.extname(filePath).toLowerCase();
153
+ if (base === 'content.json') return true;
154
+ if (ext !== '.md' && ext !== '.html' && ext !== '.htm') return false;
155
+ if (contentJsonDirs.has(path.dirname(filePath))) return false;
156
+ return true;
157
+ })
158
+ .map((filePath) => path.relative(projectRoot, filePath).replace(/\\/g, '/'));
159
+ }
160
+
161
+ async function runSquadDoctor({ args, options = {}, logger, t }) {
162
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
163
+ const slug = await resolveSquadSlug(targetDir, options.squad);
164
+ const staleMinutes = Number(options['stale-minutes'] || 5);
165
+ const paths = await parseSquadPaths(targetDir, slug);
166
+ const manifest = asObject(paths.manifest) || {};
167
+ const executors = asArray(manifest.executors).filter((executor) => asObject(executor));
168
+
169
+ const checks = [];
170
+
171
+ const metadataExists = await exists(paths.metadataPath);
172
+ checks.push(makeCheck('metadata', metadataExists, metadataExists ? 'info' : 'error', t('squad_doctor.check_metadata', { path: paths.metadataPath })));
173
+
174
+ const manifestExists = await exists(paths.manifestPath);
175
+ checks.push(makeCheck('manifest', manifestExists, manifestExists ? 'info' : 'error', t('squad_doctor.check_manifest', { path: paths.manifestPath })));
176
+
177
+ const rulesExists = await exists(paths.rulesPath);
178
+ checks.push(makeCheck('rules', rulesExists, rulesExists ? 'info' : 'error', t('squad_doctor.check_rules', { path: paths.rulesPath })));
179
+
180
+ const designDocExists = await exists(paths.designDocPath);
181
+ checks.push(makeCheck('design_doc', designDocExists, designDocExists ? 'info' : 'warn', t('squad_doctor.check_design_doc', { path: paths.designDocPath })));
182
+
183
+ const readinessExists = await exists(paths.readinessPath);
184
+ checks.push(makeCheck('readiness', readinessExists, readinessExists ? 'info' : 'warn', t('squad_doctor.check_readiness', { path: paths.readinessPath })));
185
+
186
+ const executorFilesMissing = [];
187
+ for (const executor of executors) {
188
+ const relFile = normalizeRel(executor.file || '');
189
+ if (!relFile) continue;
190
+ const absFile = path.join(targetDir, relFile);
191
+ if (!(await exists(absFile))) executorFilesMissing.push(relFile);
192
+ }
193
+
194
+ checks.push(
195
+ makeCheck(
196
+ 'executors',
197
+ executors.length > 0 && executorFilesMissing.length === 0,
198
+ executors.length === 0 || executorFilesMissing.length > 0 ? 'error' : 'info',
199
+ t('squad_doctor.check_executors', {
200
+ count: executors.length,
201
+ missing: executorFilesMissing.length
202
+ }),
203
+ { missingExecutors: executorFilesMissing }
204
+ )
205
+ );
206
+
207
+ const outputExists = await exists(paths.outputDir);
208
+ const mediaExists = await exists(paths.mediaDir);
209
+ checks.push(makeCheck('output_dir', outputExists, outputExists ? 'info' : 'warn', t('squad_doctor.check_output_dir', { path: paths.outputDir })));
210
+ checks.push(makeCheck('media_dir', mediaExists, mediaExists ? 'info' : 'warn', t('squad_doctor.check_media_dir', { path: paths.mediaDir })));
211
+
212
+ const runtimeHandle = await openRuntimeDb(targetDir, { mustExist: true });
213
+ if (!runtimeHandle) {
214
+ checks.push(makeCheck('runtime_store', false, 'warn', t('squad_doctor.check_runtime_missing')));
215
+ } else {
216
+ const { db } = runtimeHandle;
217
+ try {
218
+ const activeRuns = db
219
+ .prepare(
220
+ `
221
+ SELECT run_key, agent_name, title, status, updated_at
222
+ FROM agent_runs
223
+ WHERE squad_slug = ? AND status IN ('queued', 'running')
224
+ ORDER BY updated_at DESC
225
+ `
226
+ )
227
+ .all(slug);
228
+ const staleRuns = activeRuns.filter((run) => inferStale(run, staleMinutes));
229
+ checks.push(
230
+ makeCheck(
231
+ 'runtime_active_runs',
232
+ staleRuns.length === 0,
233
+ staleRuns.length > 0 ? 'warn' : 'info',
234
+ t('squad_doctor.check_active_runs', {
235
+ count: activeRuns.length,
236
+ stale: staleRuns.length,
237
+ minutes: staleMinutes
238
+ }),
239
+ { activeRuns, staleRuns }
240
+ )
241
+ );
242
+
243
+ const indexedRows = db
244
+ .prepare(
245
+ `
246
+ SELECT content_key, json_path, html_path
247
+ FROM content_items
248
+ WHERE squad_slug = ?
249
+ `
250
+ )
251
+ .all(slug);
252
+ const indexedPaths = new Set();
253
+ for (const row of indexedRows) {
254
+ if (row.json_path) indexedPaths.add(normalizeRel(row.json_path));
255
+ if (row.html_path) indexedPaths.add(normalizeRel(row.html_path));
256
+ }
257
+
258
+ const candidatePaths = await detectContentCandidates(paths.outputDir, targetDir);
259
+ const unindexedPaths = candidatePaths.filter((candidate) => !indexedPaths.has(normalizeRel(candidate)));
260
+ checks.push(
261
+ makeCheck(
262
+ 'content_indexing',
263
+ unindexedPaths.length === 0,
264
+ unindexedPaths.length > 0 ? 'warn' : 'info',
265
+ t('squad_doctor.check_content_indexing', {
266
+ indexed: indexedRows.length,
267
+ pending: unindexedPaths.length
268
+ }),
269
+ { indexedRows, unindexedPaths }
270
+ )
271
+ );
272
+ } finally {
273
+ db.close();
274
+ }
275
+ }
276
+
277
+ // Formal validation via squad-validate
278
+ const silentLogger = { log() {}, error() {} };
279
+ const validateResult = await runSquadValidate({
280
+ args: [targetDir],
281
+ options: { squad: slug },
282
+ logger: silentLogger
283
+ });
284
+ if (!validateResult.valid || validateResult.warnings.length > 0) {
285
+ checks.push(
286
+ makeCheck(
287
+ 'formal_validation',
288
+ validateResult.valid,
289
+ validateResult.valid ? 'warn' : 'error',
290
+ validateResult.valid
291
+ ? `Manifest valid with ${validateResult.warnings.length} warning(s)`
292
+ : `Manifest invalid: ${validateResult.errors[0] || 'see details'}`,
293
+ { validateErrors: validateResult.errors, validateWarnings: validateResult.warnings }
294
+ )
295
+ );
296
+ } else {
297
+ checks.push(makeCheck('formal_validation', true, 'info', 'Manifest formally valid'));
298
+ }
299
+
300
+ const summary = {
301
+ failed: checks.filter((check) => check.severity === 'error' && !check.ok).length,
302
+ warned: checks.filter((check) => check.severity === 'warn' && !check.ok).length,
303
+ passed: checks.filter((check) => check.ok).length
304
+ };
305
+
306
+ const output = {
307
+ ok: summary.failed === 0,
308
+ targetDir,
309
+ squad: slug,
310
+ staleMinutes,
311
+ summary,
312
+ checks
313
+ };
314
+
315
+ if (options.json) {
316
+ return output;
317
+ }
318
+
319
+ logger.log(t('squad_doctor.report_title', { squad: slug, path: targetDir }));
320
+ for (const check of checks) {
321
+ const prefix = check.severity === 'warn'
322
+ ? t('squad_doctor.prefix_warn')
323
+ : check.ok
324
+ ? t('squad_doctor.prefix_ok')
325
+ : t('squad_doctor.prefix_fail');
326
+ logger.log(t('squad_doctor.check_line', { prefix, message: check.message }));
327
+ if (Array.isArray(check.missingExecutors) && check.missingExecutors.length > 0) {
328
+ for (const item of check.missingExecutors.slice(0, 10)) {
329
+ logger.log(` - ${item}`);
330
+ }
331
+ }
332
+ if (Array.isArray(check.unindexedPaths) && check.unindexedPaths.length > 0) {
333
+ for (const item of check.unindexedPaths.slice(0, 10)) {
334
+ logger.log(` - ${item}`);
335
+ }
336
+ }
337
+ if (Array.isArray(check.staleRuns) && check.staleRuns.length > 0) {
338
+ for (const item of check.staleRuns.slice(0, 10)) {
339
+ logger.log(` - ${item.agent_name} | ${item.title || '—'} | ${item.updated_at}`);
340
+ }
341
+ }
342
+ }
343
+
344
+ logger.log('');
345
+ logger.log(
346
+ t('squad_doctor.summary', {
347
+ passed: summary.passed,
348
+ warned: summary.warned,
349
+ failed: summary.failed
350
+ })
351
+ );
352
+
353
+ return output;
354
+ }
355
+
356
+ module.exports = {
357
+ runSquadDoctor
358
+ };
@@ -0,0 +1,46 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const { execSync } = require('node:child_process');
6
+
7
+ async function runSquadExport({ args = [], options = {}, logger = console } = {}) {
8
+ const projectDir = path.resolve(process.cwd(), args[0] || '.');
9
+ const slug = options.squad || args[1];
10
+
11
+ if (!slug) {
12
+ logger.error('Usage: aioson squad:export [path] --squad=<slug>');
13
+ return { ok: false, error: 'No slug provided' };
14
+ }
15
+
16
+ const squadDir = path.join(projectDir, '.aioson', 'squads', slug);
17
+ const exportsDir = path.join(projectDir, '.aioson', 'squads', 'exports');
18
+ const outputFile = path.join(exportsDir, `${slug}.aios-squad.tar.gz`);
19
+
20
+ try {
21
+ await fs.access(squadDir);
22
+ } catch {
23
+ logger.error(`Squad "${slug}" not found at ${squadDir}`);
24
+ return { ok: false, error: `Squad "${slug}" not found` };
25
+ }
26
+
27
+ await fs.mkdir(exportsDir, { recursive: true });
28
+
29
+ const relPath = path.relative(projectDir, squadDir).replace(/\\/g, '/');
30
+
31
+ try {
32
+ execSync(`tar -czf "${outputFile}" -C "${projectDir}" "${relPath}"`, { stdio: 'pipe' });
33
+ } catch (err) {
34
+ logger.error(`Export failed: ${err.message}`);
35
+ return { ok: false, error: err.message };
36
+ }
37
+
38
+ const relOutput = path.relative(projectDir, outputFile);
39
+ logger.log('');
40
+ logger.log(`\u2705 Squad "${slug}" exported to: ${relOutput}`);
41
+ logger.log('');
42
+
43
+ return { ok: true, slug, outputFile: relOutput };
44
+ }
45
+
46
+ module.exports = { runSquadExport };
@@ -0,0 +1,97 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const {
5
+ openRuntimeDb,
6
+ runtimeStoreExists,
7
+ listPipelines,
8
+ getPipelineDAG,
9
+ getTopologicalOrder
10
+ } = require('../runtime-store');
11
+
12
+ async function runSquadPipeline({ args = [], options = {}, logger = console } = {}) {
13
+ const projectDir = path.resolve(process.cwd(), args[0] || '.');
14
+ const subcommand = options.sub || args[1] || 'list';
15
+ const slugArg = options.pipeline || args[2];
16
+
17
+ const hasDb = await runtimeStoreExists(projectDir);
18
+ if (!hasDb) {
19
+ logger.error('Runtime store not initialized. Run: aioson runtime:init .');
20
+ return { ok: false, error: 'runtime_not_initialized' };
21
+ }
22
+
23
+ const result = await openRuntimeDb(projectDir, { mustExist: true });
24
+ if (!result) {
25
+ logger.error('Could not open runtime store.');
26
+ return { ok: false, error: 'db_open_failed' };
27
+ }
28
+ const { db } = result;
29
+
30
+ try {
31
+ if (subcommand === 'list') {
32
+ const pipelines = listPipelines(db);
33
+ if (pipelines.length === 0) {
34
+ logger.log('No pipelines found. Create one with: aioson squad:pipeline show --sub=create');
35
+ return { ok: true, pipelines: [] };
36
+ }
37
+ logger.log(`Pipelines (${pipelines.length}):`);
38
+ for (const p of pipelines) {
39
+ logger.log(` ${p.slug} [${p.status}] ${p.name || ''}`);
40
+ }
41
+ return { ok: true, pipelines };
42
+ }
43
+
44
+ if (subcommand === 'show') {
45
+ if (!slugArg) {
46
+ logger.error('Usage: aioson squad:pipeline [path] --sub=show --pipeline=<slug>');
47
+ return { ok: false, error: 'missing_slug' };
48
+ }
49
+ const dag = getPipelineDAG(db, slugArg);
50
+ if (!dag) {
51
+ logger.error(`Pipeline not found: ${slugArg}`);
52
+ return { ok: false, error: 'not_found' };
53
+ }
54
+ const order = getTopologicalOrder(db, slugArg);
55
+ logger.log(`Pipeline: ${dag.pipeline.name || dag.pipeline.slug}`);
56
+ logger.log(`Status: ${dag.pipeline.status} Trigger: ${dag.pipeline.trigger_mode}`);
57
+ logger.log(`Nodes: ${dag.nodes.length} Edges: ${dag.edges.length}`);
58
+ if (order) {
59
+ logger.log(`Topological order: ${order.join(' → ')}`);
60
+ } else {
61
+ logger.log('⚠️ Cycle detected — invalid pipeline.');
62
+ }
63
+ for (const edge of dag.edges) {
64
+ logger.log(` [${edge.source_squad}:${edge.source_port}] → [${edge.target_squad}:${edge.target_port}]`);
65
+ }
66
+ return { ok: true, dag, topologicalOrder: order };
67
+ }
68
+
69
+ if (subcommand === 'status') {
70
+ if (!slugArg) {
71
+ logger.error('Usage: aioson squad:pipeline [path] --sub=status --pipeline=<slug>');
72
+ return { ok: false, error: 'missing_slug' };
73
+ }
74
+ const dag = getPipelineDAG(db, slugArg);
75
+ if (!dag) {
76
+ logger.error(`Pipeline not found: ${slugArg}`);
77
+ return { ok: false, error: 'not_found' };
78
+ }
79
+ const handoffs = db
80
+ .prepare('SELECT * FROM squad_handoffs WHERE pipeline_slug = ? ORDER BY created_at DESC')
81
+ .all(slugArg);
82
+ const pending = handoffs.filter(h => h.status === 'pending').length;
83
+ const consumed = handoffs.filter(h => h.status === 'consumed').length;
84
+ const failed = handoffs.filter(h => h.status === 'failed').length;
85
+ logger.log(`Pipeline: ${dag.pipeline.slug} Status: ${dag.pipeline.status}`);
86
+ logger.log(`Handoffs — pending: ${pending} consumed: ${consumed} failed: ${failed}`);
87
+ return { ok: true, pipeline: dag.pipeline, handoffs: { pending, consumed, failed } };
88
+ }
89
+
90
+ logger.error(`Unknown subcommand: ${subcommand}. Available: list, show, status`);
91
+ return { ok: false, error: 'unknown_subcommand' };
92
+ } finally {
93
+ db.close();
94
+ }
95
+ }
96
+
97
+ module.exports = { runSquadPipeline };
@@ -0,0 +1,39 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const { repairSquadManifestGenomeBindings } = require('../lib/squads/genome-repair');
5
+
6
+ async function runSquadRepairGenomes({ args, options = {}, logger }) {
7
+ const target = args[0];
8
+ if (!target) {
9
+ throw new Error('Usage: aioson squad:repair-genomes <manifest.json> [--write] [--no-backup]');
10
+ }
11
+
12
+ const manifestPath = path.resolve(process.cwd(), target);
13
+ const write = Boolean(options.write);
14
+ const backup = !Boolean(options['no-backup']);
15
+ const payload = await repairSquadManifestGenomeBindings(manifestPath, {
16
+ dryRun: !write,
17
+ write,
18
+ backup
19
+ });
20
+ const result = {
21
+ ok: true,
22
+ write,
23
+ dryRun: !write,
24
+ backup,
25
+ ...payload
26
+ };
27
+
28
+ if (options.json) return result;
29
+
30
+ logger.log(`Squad genome repair target: ${manifestPath}`);
31
+ logger.log(`Mode: ${result.dryRun ? 'dry-run' : 'write'}`);
32
+ logger.log(`Changed: ${result.changed ? 'yes' : 'no'}`);
33
+ if (result.backupPath) logger.log(`Backup: ${result.backupPath}`);
34
+ return result;
35
+ }
36
+
37
+ module.exports = {
38
+ runSquadRepairGenomes
39
+ };