@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,594 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const { getAgentDefinition, resolveInstructionPath, buildAgentPrompt } = require('../agents');
6
+ const { resolveAgentLocale } = require('../locales');
7
+ const { validateProjectContextFile } = require('../context');
8
+ const { exists, ensureDir } = require('../utils');
9
+ const { syncWorkflowRuntime } = require('../execution-gateway');
10
+
11
+ const STATE_RELATIVE_PATH = '.aioson/context/workflow.state.json';
12
+ const CONFIG_RELATIVE_PATH = '.aioson/context/workflow.config.json';
13
+ const EVENTS_RELATIVE_PATH = '.aioson/context/workflow.events.jsonl';
14
+
15
+ const DEFAULT_FEATURE_WORKFLOW_BY_CLASSIFICATION = {
16
+ MICRO: ['product', 'dev', 'qa'],
17
+ SMALL: ['product', 'analyst', 'dev', 'qa'],
18
+ MEDIUM: ['product', 'analyst', 'dev', 'qa']
19
+ };
20
+
21
+ function normalizeAgentName(input) {
22
+ return String(input || '')
23
+ .trim()
24
+ .toLowerCase()
25
+ .replace(/^@/, '');
26
+ }
27
+
28
+ function normalizeClassification(value, fallback = 'MICRO') {
29
+ const text = String(value || '').trim().toUpperCase();
30
+ if (text === 'MICRO' || text === 'SMALL' || text === 'MEDIUM') return text;
31
+ return fallback;
32
+ }
33
+
34
+ function buildDefaultWorkflowConfig() {
35
+ return {
36
+ version: 1,
37
+ project: {
38
+ MICRO: ['setup', 'dev'],
39
+ SMALL: ['setup', 'product', 'analyst', 'architect', 'dev', 'qa'],
40
+ MEDIUM: ['setup', 'product', 'analyst', 'architect', 'ux-ui', 'pm', 'orchestrator', 'dev', 'qa']
41
+ },
42
+ feature: DEFAULT_FEATURE_WORKFLOW_BY_CLASSIFICATION,
43
+ rules: {
44
+ required: ['dev'],
45
+ allowDetours: true
46
+ }
47
+ };
48
+ }
49
+
50
+ function parseFeaturesMarkdown(markdown) {
51
+ return String(markdown || '')
52
+ .split(/\r?\n/)
53
+ .slice(3)
54
+ .map((line) => line.trim())
55
+ .filter(Boolean)
56
+ .filter((line) => line.startsWith('|'))
57
+ .map((line) => line.split('|').map((part) => part.trim()))
58
+ .filter((parts) => parts.length >= 5)
59
+ .map((parts) => ({
60
+ slug: parts[1],
61
+ status: parts[2],
62
+ started: parts[3],
63
+ completed: parts[4]
64
+ }))
65
+ .filter((row) => row.slug && row.slug !== 'slug');
66
+ }
67
+
68
+ async function readJsonIfExists(filePath) {
69
+ if (!(await exists(filePath))) return null;
70
+ const content = await fs.readFile(filePath, 'utf8');
71
+ return JSON.parse(content);
72
+ }
73
+
74
+ async function writeJson(filePath, payload) {
75
+ await ensureDir(path.dirname(filePath));
76
+ await fs.writeFile(filePath, `${JSON.stringify(payload, null, 2)}\n`, 'utf8');
77
+ }
78
+
79
+ async function appendJsonLine(filePath, payload) {
80
+ await ensureDir(path.dirname(filePath));
81
+ await fs.appendFile(filePath, `${JSON.stringify(payload)}\n`, 'utf8');
82
+ }
83
+
84
+ function buildWorkflowEventMessage({ created, state, activation, completedStage, options }) {
85
+ const requestedAgent = options.requestedAgent ? normalizeAgentName(options.requestedAgent) : null;
86
+ if (requestedAgent && activation.agent && requestedAgent !== activation.agent) {
87
+ return `Workflow enforced @${activation.agent} after direct request for @${requestedAgent}.`;
88
+ }
89
+ if (completedStage && activation.agent) {
90
+ return `Completed @${completedStage}. Next stage ready: @${activation.agent}.`;
91
+ }
92
+ if (completedStage && !activation.agent) {
93
+ return `Completed @${completedStage}. Workflow has no pending stage.`;
94
+ }
95
+ if (state.detour && state.detour.active) {
96
+ return `Detour started with @${state.detour.agent}. Return to ${
97
+ state.detour.returnTo ? `@${state.detour.returnTo}` : 'the main flow'
98
+ }.`;
99
+ }
100
+ if (options.skip && activation.agent) {
101
+ return `Workflow advanced to @${activation.agent} after skip.`;
102
+ }
103
+ if (activation.agent) {
104
+ return created
105
+ ? `Workflow initialized at @${activation.agent}.`
106
+ : `Stage @${activation.agent} is active.`;
107
+ }
108
+ return 'Workflow has no pending stage.';
109
+ }
110
+
111
+ function buildWorkflowEventType({ completedStage, state, activation, options }) {
112
+ const requestedAgent = options.requestedAgent ? normalizeAgentName(options.requestedAgent) : null;
113
+ if (requestedAgent && activation.agent && requestedAgent !== activation.agent) return 'routed';
114
+ if (completedStage) return 'completed';
115
+ if (state.detour && state.detour.active) return 'workflow';
116
+ if (options.skip) return 'workflow';
117
+ if (activation.agent) return 'start';
118
+ return 'workflow';
119
+ }
120
+
121
+ async function appendWorkflowEvent(targetDir, payload) {
122
+ const eventsPath = path.join(targetDir, EVENTS_RELATIVE_PATH);
123
+ await appendJsonLine(eventsPath, payload);
124
+ return eventsPath;
125
+ }
126
+
127
+ async function readWorkflowConfig(targetDir) {
128
+ const configPath = path.join(targetDir, CONFIG_RELATIVE_PATH);
129
+ const userConfig = await readJsonIfExists(configPath);
130
+ const base = buildDefaultWorkflowConfig();
131
+ if (!userConfig || typeof userConfig !== 'object') {
132
+ return { configPath, config: base, exists: false };
133
+ }
134
+
135
+ const merged = {
136
+ ...base,
137
+ ...userConfig,
138
+ project: {
139
+ ...base.project,
140
+ ...(userConfig.project || {})
141
+ },
142
+ feature: {
143
+ ...base.feature,
144
+ ...(userConfig.feature || {})
145
+ },
146
+ rules: {
147
+ ...base.rules,
148
+ ...(userConfig.rules || {})
149
+ }
150
+ };
151
+
152
+ return { configPath, config: merged, exists: true };
153
+ }
154
+
155
+ async function resolveLocaleForTarget(targetDir, options) {
156
+ const fromOption = options.language || options.lang;
157
+ if (fromOption) return resolveAgentLocale(fromOption);
158
+
159
+ const context = await validateProjectContextFile(targetDir);
160
+ if (context.parsed && context.data && context.data.conversation_language) {
161
+ return resolveAgentLocale(context.data.conversation_language);
162
+ }
163
+
164
+ return 'en';
165
+ }
166
+
167
+ async function resolveExistingInstructionPath(targetDir, agent, locale) {
168
+ const candidate = resolveInstructionPath(agent, locale);
169
+ const candidateAbs = path.join(targetDir, candidate);
170
+ if (await exists(candidateAbs)) return candidate;
171
+ return agent.path;
172
+ }
173
+
174
+ async function detectWorkflowMode(targetDir) {
175
+ const prdPath = path.join(targetDir, '.aioson/context/prd.md');
176
+ const featuresPath = path.join(targetDir, '.aioson/context/features.md');
177
+ const hasProjectPrd = await exists(prdPath);
178
+ const featuresMarkdown = await fs.readFile(featuresPath, 'utf8').catch(() => '');
179
+ const features = parseFeaturesMarkdown(featuresMarkdown);
180
+ const activeFeature = features.find((feature) => feature.status === 'in_progress') || null;
181
+
182
+ if (activeFeature) {
183
+ return {
184
+ mode: 'feature',
185
+ featureSlug: activeFeature.slug,
186
+ features
187
+ };
188
+ }
189
+
190
+ return {
191
+ mode: hasProjectPrd ? 'project' : 'project',
192
+ featureSlug: null,
193
+ features
194
+ };
195
+ }
196
+
197
+ function getSequenceForMode(config, mode, classification) {
198
+ const group = mode === 'feature' ? config.feature : config.project;
199
+ const sequence = group[normalizeClassification(classification, 'MICRO')];
200
+ return Array.isArray(sequence) && sequence.length > 0 ? [...sequence] : [];
201
+ }
202
+
203
+ async function validateStageArtifacts(targetDir, state, stage) {
204
+ const base = path.join(targetDir, '.aioson/context');
205
+ const slug = state.featureSlug;
206
+
207
+ if (stage === 'setup') {
208
+ const context = await validateProjectContextFile(targetDir);
209
+ return context.valid;
210
+ }
211
+
212
+ if (stage === 'product') {
213
+ if (state.mode === 'feature' && slug) {
214
+ const prdFeature = path.join(base, `prd-${slug}.md`);
215
+ const prdFix = path.join(base, `prd-${slug}-fix.md`);
216
+ return (await exists(prdFeature)) || (await exists(prdFix));
217
+ }
218
+ return await exists(path.join(base, 'prd.md'));
219
+ }
220
+
221
+ if (stage === 'analyst') {
222
+ if (state.mode === 'feature' && slug) {
223
+ const requirements = path.join(base, `requirements-${slug}.md`);
224
+ const spec = path.join(base, `spec-${slug}.md`);
225
+ return (await exists(requirements)) && (await exists(spec));
226
+ }
227
+ return await exists(path.join(base, 'discovery.md'));
228
+ }
229
+
230
+ if (stage === 'architect') {
231
+ return await exists(path.join(base, 'architecture.md'));
232
+ }
233
+
234
+ if (stage === 'ux-ui') {
235
+ return await exists(path.join(base, 'ui-spec.md'));
236
+ }
237
+
238
+ if (stage === 'orchestrator') {
239
+ return await exists(path.join(base, 'parallel'));
240
+ }
241
+
242
+ return true;
243
+ }
244
+
245
+ function isRequiredAgent(config, agentName) {
246
+ return Array.isArray(config.rules?.required)
247
+ ? config.rules.required.map(normalizeAgentName).includes(agentName)
248
+ : false;
249
+ }
250
+
251
+ function buildStatePayload(input) {
252
+ return {
253
+ version: 1,
254
+ mode: input.mode,
255
+ classification: input.classification,
256
+ sequence: input.sequence,
257
+ current: input.current || null,
258
+ next: input.next || null,
259
+ completed: Array.isArray(input.completed) ? input.completed : [],
260
+ skipped: Array.isArray(input.skipped) ? input.skipped : [],
261
+ featureSlug: input.featureSlug || null,
262
+ detour: input.detour || null,
263
+ updatedAt: new Date().toISOString()
264
+ };
265
+ }
266
+
267
+ function findNextFromSequence(sequence, completed, skipped) {
268
+ const done = new Set([...(completed || []), ...(skipped || [])].map(normalizeAgentName));
269
+ return sequence.find((stage) => !done.has(normalizeAgentName(stage))) || null;
270
+ }
271
+
272
+ function isInferableStage(stage) {
273
+ return ['setup', 'product', 'analyst', 'architect', 'ux-ui', 'orchestrator'].includes(
274
+ normalizeAgentName(stage)
275
+ );
276
+ }
277
+
278
+ async function inferCompletedStages(targetDir, draftState) {
279
+ const completed = [];
280
+ for (const stage of draftState.sequence) {
281
+ if (!isInferableStage(stage)) break;
282
+ const valid = await validateStageArtifacts(targetDir, draftState, stage);
283
+ if (!valid) break;
284
+ completed.push(normalizeAgentName(stage));
285
+ }
286
+ return completed;
287
+ }
288
+
289
+ async function loadOrCreateState(targetDir, options = {}) {
290
+ const statePath = path.join(targetDir, STATE_RELATIVE_PATH);
291
+ const existing = await readJsonIfExists(statePath);
292
+ if (existing && typeof existing === 'object' && Array.isArray(existing.sequence)) {
293
+ return { statePath, state: existing, created: false };
294
+ }
295
+
296
+ const context = await validateProjectContextFile(targetDir);
297
+ const classification = normalizeClassification(
298
+ options.classification || (context.data && context.data.classification) || 'MICRO',
299
+ 'MICRO'
300
+ );
301
+ const modeInfo = await detectWorkflowMode(targetDir);
302
+ const { config } = await readWorkflowConfig(targetDir);
303
+ const sequence = getSequenceForMode(config, modeInfo.mode, classification);
304
+ const draftState = buildStatePayload({
305
+ mode: modeInfo.mode,
306
+ classification,
307
+ sequence,
308
+ current: null,
309
+ next: null,
310
+ completed: [],
311
+ skipped: [],
312
+ featureSlug: modeInfo.featureSlug,
313
+ detour: null
314
+ });
315
+ const completed = await inferCompletedStages(targetDir, draftState);
316
+ const next = findNextFromSequence(sequence, completed, []);
317
+ const state = buildStatePayload({
318
+ mode: modeInfo.mode,
319
+ classification,
320
+ sequence,
321
+ current: null,
322
+ next,
323
+ completed,
324
+ skipped: [],
325
+ featureSlug: modeInfo.featureSlug,
326
+ detour: null
327
+ });
328
+
329
+ await writeJson(statePath, state);
330
+ return { statePath, state, created: true };
331
+ }
332
+
333
+ async function persistState(targetDir, nextState) {
334
+ const statePath = path.join(targetDir, STATE_RELATIVE_PATH);
335
+ await writeJson(statePath, nextState);
336
+ return statePath;
337
+ }
338
+
339
+ function ensureAgentInSequence(state, agentName) {
340
+ if (state.sequence.includes(agentName)) return;
341
+ throw new Error(`Agent ${agentName} is not part of the active workflow sequence.`);
342
+ }
343
+
344
+ function ensureSkippableTarget(config, state, targetAgent) {
345
+ const normalizedTarget = normalizeAgentName(targetAgent);
346
+ ensureAgentInSequence(state, normalizedTarget);
347
+
348
+ const currentIndex = state.next ? state.sequence.indexOf(state.next) : -1;
349
+ const targetIndex = state.sequence.indexOf(normalizedTarget);
350
+ const devIndex = state.sequence.indexOf('dev');
351
+
352
+ if (currentIndex === -1) {
353
+ throw new Error('No next stage is available to skip from.');
354
+ }
355
+ if (targetIndex === -1 || targetIndex < currentIndex) {
356
+ throw new Error(`Cannot skip backwards to ${targetAgent}.`);
357
+ }
358
+ if (normalizedTarget === 'dev') return;
359
+ if (devIndex !== -1 && targetIndex > devIndex) {
360
+ throw new Error('Cannot skip past @dev because @dev is mandatory.');
361
+ }
362
+ if (isRequiredAgent(config, normalizedTarget) && normalizedTarget !== 'dev') {
363
+ return;
364
+ }
365
+ }
366
+
367
+ async function finalizeCurrentStage(targetDir, config, state, stageName) {
368
+ const normalizedStage = normalizeAgentName(stageName || state.current || state.next);
369
+ if (!normalizedStage) {
370
+ throw new Error('No stage is active to complete.');
371
+ }
372
+
373
+ if (state.detour && state.detour.active && normalizeAgentName(state.detour.agent) === normalizedStage) {
374
+ const validDetour = await validateStageArtifacts(targetDir, state, normalizedStage);
375
+ if (!validDetour) {
376
+ throw new Error(`Cannot complete detour ${normalizedStage}; expected artifacts are missing.`);
377
+ }
378
+ const nextState = buildStatePayload({
379
+ ...state,
380
+ current: null,
381
+ next: state.detour.returnTo,
382
+ detour: null
383
+ });
384
+ return { state: nextState, completedStage: normalizedStage };
385
+ }
386
+
387
+ ensureAgentInSequence(state, normalizedStage);
388
+ const valid = await validateStageArtifacts(targetDir, state, normalizedStage);
389
+ if (!valid) {
390
+ throw new Error(`Cannot complete ${normalizedStage}; expected artifacts are missing.`);
391
+ }
392
+
393
+ const completed = Array.from(new Set([...(state.completed || []), normalizedStage]));
394
+ const next = findNextFromSequence(state.sequence, completed, state.skipped || []);
395
+ const nextState = buildStatePayload({
396
+ ...state,
397
+ completed,
398
+ current: null,
399
+ next,
400
+ detour: null
401
+ });
402
+
403
+ return { state: nextState, completedStage: normalizedStage };
404
+ }
405
+
406
+ function applySkip(config, state, target) {
407
+ const normalizedTarget = normalizeAgentName(target);
408
+ ensureSkippableTarget(config, state, normalizedTarget);
409
+ const currentIndex = state.sequence.indexOf(state.next);
410
+ const targetIndex = state.sequence.indexOf(normalizedTarget);
411
+ const toSkip = state.sequence.slice(currentIndex, targetIndex);
412
+ if (toSkip.some((agent) => normalizeAgentName(agent) === 'dev')) {
413
+ throw new Error('Cannot skip @dev because it is mandatory.');
414
+ }
415
+
416
+ const skipped = Array.from(new Set([...(state.skipped || []), ...toSkip]));
417
+ return buildStatePayload({
418
+ ...state,
419
+ skipped,
420
+ current: null,
421
+ next: normalizedTarget
422
+ });
423
+ }
424
+
425
+ async function activateStage(targetDir, state, locale, tool, explicitAgent = null) {
426
+ const stageName = normalizeAgentName(explicitAgent || state.current || state.next);
427
+ if (!stageName) {
428
+ return {
429
+ state,
430
+ agent: null,
431
+ instructionPath: null,
432
+ prompt: null
433
+ };
434
+ }
435
+
436
+ const agent = getAgentDefinition(stageName);
437
+ if (!agent) {
438
+ throw new Error(`Unknown agent: ${stageName}`);
439
+ }
440
+
441
+ const instructionPath = await resolveExistingInstructionPath(targetDir, agent, locale);
442
+ const prompt = buildAgentPrompt(agent, tool, { instructionPath });
443
+
444
+ let nextState = state;
445
+ if (explicitAgent && stageName !== normalizeAgentName(state.next)) {
446
+ nextState = buildStatePayload({
447
+ ...state,
448
+ current: stageName,
449
+ detour: {
450
+ active: true,
451
+ agent: stageName,
452
+ returnTo: state.next
453
+ }
454
+ });
455
+ } else {
456
+ nextState = buildStatePayload({
457
+ ...state,
458
+ current: stageName
459
+ });
460
+ }
461
+
462
+ return {
463
+ state: nextState,
464
+ agent: stageName,
465
+ instructionPath,
466
+ prompt
467
+ };
468
+ }
469
+
470
+ async function runWorkflowNext({ args, options, logger, t }) {
471
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
472
+ const tool = options.tool || 'codex';
473
+ const locale = await resolveLocaleForTarget(targetDir, options);
474
+ const { config } = await readWorkflowConfig(targetDir);
475
+ const loaded = await loadOrCreateState(targetDir, options);
476
+ let state = loaded.state;
477
+ let completedStage = null;
478
+
479
+ if (options.complete || options['complete-current']) {
480
+ const result = await finalizeCurrentStage(
481
+ targetDir,
482
+ config,
483
+ state,
484
+ options.complete === true ? state.current || state.next : options.complete
485
+ );
486
+ state = result.state;
487
+ completedStage = result.completedStage;
488
+ }
489
+
490
+ if (options.skip) {
491
+ state = applySkip(config, state, options.skip);
492
+ }
493
+
494
+ const requestedAgent = options.agent ? normalizeAgentName(options.agent) : null;
495
+ const activation = await activateStage(targetDir, state, locale, tool, requestedAgent);
496
+ state = activation.state;
497
+ const statePath = await persistState(targetDir, state);
498
+ const eventPayload = {
499
+ id: Date.now(),
500
+ kind: 'workflow',
501
+ createdAt: new Date().toISOString(),
502
+ eventType: buildWorkflowEventType({ completedStage, state, activation, options }),
503
+ message: buildWorkflowEventMessage({
504
+ created: loaded.created,
505
+ state,
506
+ activation,
507
+ completedStage,
508
+ options
509
+ }),
510
+ mode: state.mode,
511
+ classification: state.classification,
512
+ featureSlug: state.featureSlug,
513
+ current: state.current,
514
+ next: state.detour && state.detour.active ? state.detour.returnTo : state.next,
515
+ completedStage,
516
+ detour: state.detour,
517
+ requestedAgent: options.requestedAgent ? normalizeAgentName(options.requestedAgent) : null,
518
+ completed: state.completed,
519
+ skipped: state.skipped,
520
+ sequence: state.sequence
521
+ };
522
+ await appendWorkflowEvent(targetDir, eventPayload);
523
+ const runtime = await syncWorkflowRuntime(targetDir, {
524
+ state,
525
+ eventPayload,
526
+ activationAgent: activation.agent,
527
+ completedStage
528
+ });
529
+
530
+ const payload = {
531
+ ok: true,
532
+ targetDir,
533
+ locale,
534
+ tool,
535
+ statePath: STATE_RELATIVE_PATH,
536
+ configPath: CONFIG_RELATIVE_PATH,
537
+ created: loaded.created,
538
+ mode: state.mode,
539
+ classification: state.classification,
540
+ current: state.current,
541
+ next: state.detour && state.detour.active ? state.detour.returnTo : state.next,
542
+ detour: state.detour,
543
+ completed: state.completed,
544
+ skipped: state.skipped,
545
+ completedStage,
546
+ featureSlug: state.featureSlug,
547
+ runtime,
548
+ agent: activation.agent,
549
+ instructionPath: activation.instructionPath,
550
+ prompt: activation.prompt
551
+ };
552
+
553
+ logger.log(t('workflow_next.title', {
554
+ mode: state.mode,
555
+ classification: state.classification
556
+ }));
557
+ if (completedStage) {
558
+ logger.log(t('workflow_next.completed', { agent: `@${completedStage}` }));
559
+ }
560
+ if (state.detour && state.detour.active) {
561
+ logger.log(
562
+ t('workflow_next.detour', {
563
+ agent: `@${state.detour.agent}`,
564
+ returnTo: `@${state.detour.returnTo}`
565
+ })
566
+ );
567
+ }
568
+ if (activation.agent) {
569
+ logger.log(t('workflow_next.current_agent', { agent: `@${activation.agent}` }));
570
+ if (payload.next) {
571
+ logger.log(t('workflow_next.next_agent', { agent: `@${payload.next}` }));
572
+ }
573
+ logger.log(activation.prompt);
574
+ } else {
575
+ logger.log(t('workflow_next.done'));
576
+ }
577
+ logger.log(t('workflow_next.state_file', { path: STATE_RELATIVE_PATH }));
578
+
579
+ return payload;
580
+ }
581
+
582
+ module.exports = {
583
+ STATE_RELATIVE_PATH,
584
+ CONFIG_RELATIVE_PATH,
585
+ EVENTS_RELATIVE_PATH,
586
+ buildDefaultWorkflowConfig,
587
+ parseFeaturesMarkdown,
588
+ readWorkflowConfig,
589
+ detectWorkflowMode,
590
+ loadOrCreateState,
591
+ finalizeCurrentStage,
592
+ applySkip,
593
+ runWorkflowNext
594
+ };
@@ -0,0 +1,108 @@
1
+ 'use strict';
2
+
3
+ const path = require('node:path');
4
+ const { validateProjectContextFile } = require('../context');
5
+
6
+ const WORKFLOW_BY_CLASSIFICATION = {
7
+ MICRO: ['setup', 'dev'],
8
+ SMALL: ['setup', 'product', 'analyst', 'architect', 'dev', 'qa'],
9
+ MEDIUM: ['setup', 'product', 'analyst', 'architect', 'ux-ui', 'pm', 'orchestrator', 'dev', 'qa']
10
+ };
11
+
12
+ function normalizeClassification(value, fallback = 'MICRO') {
13
+ const text = String(value || '').trim().toUpperCase();
14
+ if (Object.prototype.hasOwnProperty.call(WORKFLOW_BY_CLASSIFICATION, text)) return text;
15
+ return fallback;
16
+ }
17
+
18
+ function withAgentPrefix(sequence) {
19
+ return sequence.map((id) => `@${id}`);
20
+ }
21
+
22
+ function buildWorkflowPlan(input = {}) {
23
+ const classification = normalizeClassification(input.classification, 'MICRO');
24
+ const projectType = String(input.projectType || 'web_app');
25
+ const frameworkInstalled = Boolean(input.frameworkInstalled);
26
+ const sequence = WORKFLOW_BY_CLASSIFICATION[classification] || WORKFLOW_BY_CLASSIFICATION.MICRO;
27
+ const noteKeys = [];
28
+
29
+ if (!frameworkInstalled) {
30
+ noteKeys.push('framework_not_installed');
31
+ }
32
+ if (projectType === 'dapp' || Boolean(input.web3Enabled)) {
33
+ noteKeys.push('dapp_context');
34
+ }
35
+ if (classification === 'MICRO') {
36
+ noteKeys.push('micro_scope');
37
+ noteKeys.push('product_optional');
38
+ }
39
+ if (classification === 'SMALL' || classification === 'MEDIUM') {
40
+ noteKeys.push('feature_flow');
41
+ }
42
+
43
+ return {
44
+ classification,
45
+ sequence,
46
+ commands: withAgentPrefix(sequence),
47
+ noteKeys
48
+ };
49
+ }
50
+
51
+ async function runWorkflowPlan({ args, options = {}, logger, t }) {
52
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
53
+ const jsonMode = Boolean(options.json);
54
+ const context = await validateProjectContextFile(targetDir);
55
+ const contextData = context.parsed && context.data ? context.data : {};
56
+
57
+ const plan = buildWorkflowPlan({
58
+ classification: options.classification || contextData.classification || 'MICRO',
59
+ projectType: contextData.project_type || options['project-type'] || 'web_app',
60
+ frameworkInstalled:
61
+ contextData.framework_installed !== undefined
62
+ ? contextData.framework_installed
63
+ : options['framework-installed'] === 'true',
64
+ web3Enabled:
65
+ contextData.web3_enabled !== undefined
66
+ ? contextData.web3_enabled
67
+ : options['web3-enabled'] === 'true'
68
+ });
69
+
70
+ const output = {
71
+ ok: true,
72
+ targetDir,
73
+ contextExists: context.exists,
74
+ contextParsed: context.parsed,
75
+ classification: plan.classification,
76
+ sequence: plan.sequence,
77
+ commands: plan.commands,
78
+ notes: plan.noteKeys.map((key) => t(`workflow_plan.note_${key}`)),
79
+ noteKeys: plan.noteKeys
80
+ };
81
+
82
+ if (jsonMode) {
83
+ return output;
84
+ }
85
+
86
+ if (!context.exists) {
87
+ logger.log(t('workflow_plan.context_missing'));
88
+ }
89
+ logger.log(t('workflow_plan.title', { classification: plan.classification }));
90
+ for (const command of plan.commands) {
91
+ logger.log(t('workflow_plan.command_line', { command }));
92
+ }
93
+ if (output.notes.length > 0) {
94
+ logger.log(t('workflow_plan.notes'));
95
+ for (const note of output.notes) {
96
+ logger.log(t('workflow_plan.note_line', { note }));
97
+ }
98
+ }
99
+
100
+ return output;
101
+ }
102
+
103
+ module.exports = {
104
+ runWorkflowPlan,
105
+ normalizeClassification,
106
+ buildWorkflowPlan,
107
+ WORKFLOW_BY_CLASSIFICATION
108
+ };