@jaimevalasek/aioson 1.6.0 → 1.7.2

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 (275) hide show
  1. package/CHANGELOG.md +74 -0
  2. package/README.md +729 -232
  3. package/docs/design-previews/pt.squarespace.com-homepage.html +889 -0
  4. package/docs/integrations/sdlc-genius-boundary.md +76 -0
  5. package/docs/integrations/sdlc-genius-eval-matrix.md +75 -0
  6. package/docs/integrations/sdlc-genius-install-checklist.md +93 -0
  7. package/docs/integrations/sdlc-genius-review-samples.md +86 -0
  8. package/docs/pt/README.md +3 -0
  9. package/docs/pt/agentes.md +1 -0
  10. package/docs/pt/comandos-cli.md +888 -2
  11. package/docs/pt/design-hybrid-forge.md +255 -6
  12. package/docs/pt/devlog-pipeline.md +270 -0
  13. package/docs/pt/fluxo-artefatos.md +178 -0
  14. package/docs/pt/hooks-session-guard.md +454 -0
  15. package/docs/pt/monitor-de-contexto.md +59 -5
  16. package/docs/pt/sdd-automation-scripts.md +557 -0
  17. package/docs/pt/site-forge.md +309 -0
  18. package/docs/pt/spec-learnings-pipeline.md +265 -0
  19. package/package.json +1 -1
  20. package/src/a2a/client.js +165 -0
  21. package/src/a2a/server.js +223 -0
  22. package/src/cli.js +235 -1
  23. package/src/commands/agent-audit.js +397 -0
  24. package/src/commands/agent-export-skill.js +229 -0
  25. package/src/commands/artifact-validate.js +189 -0
  26. package/src/commands/brief-gen.js +405 -0
  27. package/src/commands/brief-validate.js +65 -0
  28. package/src/commands/classify.js +256 -0
  29. package/src/commands/context-compact.js +49 -0
  30. package/src/commands/context-health.js +175 -0
  31. package/src/commands/context-monitor.js +71 -0
  32. package/src/commands/context-trim.js +177 -0
  33. package/src/commands/detect-test-runner.js +55 -0
  34. package/src/commands/devlog-export-brains.js +27 -0
  35. package/src/commands/devlog-process.js +292 -0
  36. package/src/commands/devlog-watch.js +131 -0
  37. package/src/commands/feature-close.js +165 -0
  38. package/src/commands/gate-check.js +228 -0
  39. package/src/commands/hooks-emit.js +253 -0
  40. package/src/commands/hooks-install.js +347 -0
  41. package/src/commands/learning-auto-promote.js +195 -0
  42. package/src/commands/learning-evolve.js +18 -9
  43. package/src/commands/learning-export.js +103 -0
  44. package/src/commands/learning-rollback.js +164 -0
  45. package/src/commands/live.js +25 -1
  46. package/src/commands/pattern-detect.js +33 -0
  47. package/src/commands/preflight-context.js +30 -0
  48. package/src/commands/preflight.js +208 -0
  49. package/src/commands/pulse-update.js +130 -0
  50. package/src/commands/runner-daemon.js +274 -0
  51. package/src/commands/runner-plan.js +70 -0
  52. package/src/commands/runner-queue-from-plan.js +166 -0
  53. package/src/commands/runner-queue.js +189 -0
  54. package/src/commands/runner-run.js +129 -0
  55. package/src/commands/runtime.js +47 -1
  56. package/src/commands/self-implement-loop.js +256 -0
  57. package/src/commands/session-guard.js +218 -0
  58. package/src/commands/sizing.js +165 -0
  59. package/src/commands/skill.js +65 -0
  60. package/src/commands/spec-checkpoint.js +177 -0
  61. package/src/commands/spec-status.js +79 -0
  62. package/src/commands/spec-sync.js +190 -0
  63. package/src/commands/spec-tasks.js +288 -0
  64. package/src/commands/squad-autorun.js +1220 -0
  65. package/src/commands/squad-bus.js +217 -0
  66. package/src/commands/squad-card.js +149 -0
  67. package/src/commands/squad-daemon.js +134 -0
  68. package/src/commands/squad-dependency-graph.js +164 -0
  69. package/src/commands/squad-review.js +106 -0
  70. package/src/commands/squad-scaffold.js +55 -0
  71. package/src/commands/squad-tool-register.js +157 -0
  72. package/src/commands/state-save.js +122 -0
  73. package/src/commands/update.js +2 -0
  74. package/src/commands/verify-gate.js +572 -0
  75. package/src/commands/workflow-execute.js +241 -0
  76. package/src/constants.js +22 -0
  77. package/src/install-profile.js +2 -2
  78. package/src/install-wizard.js +3 -2
  79. package/src/installer.js +6 -0
  80. package/src/lib/health-check.js +158 -0
  81. package/src/lib/hook-protocol.js +76 -0
  82. package/src/mcp/apps/squad-dashboard/app.js +163 -0
  83. package/src/mcp/apps/squad-dashboard/index.html +261 -0
  84. package/src/mcp/apps/squad-dashboard/mcp-manifest.json +23 -0
  85. package/src/mcp/resources/squad-state.js +130 -0
  86. package/src/preflight-engine.js +443 -0
  87. package/src/runner/cascade.js +97 -0
  88. package/src/runner/cli-launcher.js +109 -0
  89. package/src/runner/plan-importer.js +63 -0
  90. package/src/runner/queue-store.js +159 -0
  91. package/src/runtime-store.js +61 -3
  92. package/src/squad/agent-teams-adapter.js +264 -0
  93. package/src/squad/brief-validator.js +350 -0
  94. package/src/squad/bus-bridge.js +140 -0
  95. package/src/squad/context-compactor.js +265 -0
  96. package/src/squad/cross-ai-synthesizer.js +250 -0
  97. package/src/squad/hooks-generator.js +196 -0
  98. package/src/squad/inter-squad-events.js +175 -0
  99. package/src/squad/intra-bus.js +345 -0
  100. package/src/squad/learning-extractor.js +213 -0
  101. package/src/squad/pattern-detector.js +365 -0
  102. package/src/squad/preflight-context.js +296 -0
  103. package/src/squad/recovery-context.js +242 -71
  104. package/src/squad/reflection.js +365 -0
  105. package/src/squad/squad-scaffold.js +177 -0
  106. package/src/squad/state-manager.js +310 -0
  107. package/src/squad/task-decomposer.js +652 -0
  108. package/src/squad/verify-gate.js +303 -0
  109. package/src/updater.js +4 -5
  110. package/src/worker-runner.js +186 -1
  111. package/template/.aioson/agents/analyst.md +62 -1
  112. package/template/.aioson/agents/architect.md +61 -1
  113. package/template/.aioson/agents/copywriter.md +463 -0
  114. package/template/.aioson/agents/design-hybrid-forge.md +14 -0
  115. package/template/.aioson/agents/dev.md +271 -25
  116. package/template/.aioson/agents/deyvin.md +67 -8
  117. package/template/.aioson/agents/discovery-design-doc.md +44 -0
  118. package/template/.aioson/agents/genome.md +14 -0
  119. package/template/.aioson/agents/neo.md +83 -2
  120. package/template/.aioson/agents/orache.md +50 -4
  121. package/template/.aioson/agents/orchestrator.md +197 -1
  122. package/template/.aioson/agents/pm.md +35 -0
  123. package/template/.aioson/agents/product.md +50 -5
  124. package/template/.aioson/agents/profiler-enricher.md +14 -0
  125. package/template/.aioson/agents/profiler-forge.md +14 -0
  126. package/template/.aioson/agents/profiler-researcher.md +14 -0
  127. package/template/.aioson/agents/qa.md +273 -21
  128. package/template/.aioson/agents/setup.md +96 -10
  129. package/template/.aioson/agents/sheldon.md +131 -6
  130. package/template/.aioson/agents/site-forge.md +1753 -0
  131. package/template/.aioson/agents/squad.md +352 -0
  132. package/template/.aioson/agents/tester.md +53 -0
  133. package/template/.aioson/agents/ux-ui.md +203 -4
  134. package/template/.aioson/brains/README.md +128 -0
  135. package/template/.aioson/brains/_index.json +16 -0
  136. package/template/.aioson/brains/scripts/query.js +103 -0
  137. package/template/.aioson/brains/site-forge/visual-patterns.brain.json +205 -0
  138. package/template/.aioson/config.md +143 -13
  139. package/template/.aioson/constitution.md +33 -0
  140. package/template/.aioson/context/project-pulse.md +34 -0
  141. package/template/.aioson/docs/LAYERS.md +79 -0
  142. package/template/.aioson/docs/README.md +76 -0
  143. package/template/.aioson/docs/example-external-api-context.md +72 -0
  144. package/template/.aioson/genomes/copywriting.md +204 -0
  145. package/template/.aioson/locales/en/agents/architect.md +17 -0
  146. package/template/.aioson/locales/en/agents/dev.md +79 -13
  147. package/template/.aioson/locales/en/agents/orache.md +6 -0
  148. package/template/.aioson/locales/en/agents/orchestrator.md +24 -0
  149. package/template/.aioson/locales/en/agents/product.md +50 -0
  150. package/template/.aioson/locales/en/agents/sheldon.md +115 -0
  151. package/template/.aioson/locales/en/agents/squad.md +14 -0
  152. package/template/.aioson/locales/en/agents/tester.md +6 -0
  153. package/template/.aioson/locales/es/agents/analyst.md +2 -0
  154. package/template/.aioson/locales/es/agents/architect.md +19 -0
  155. package/template/.aioson/locales/es/agents/dev.md +64 -4
  156. package/template/.aioson/locales/es/agents/deyvin.md +2 -0
  157. package/template/.aioson/locales/es/agents/discovery-design-doc.md +2 -0
  158. package/template/.aioson/locales/es/agents/genome.md +2 -0
  159. package/template/.aioson/locales/es/agents/neo.md +2 -0
  160. package/template/.aioson/locales/es/agents/orache.md +2 -0
  161. package/template/.aioson/locales/es/agents/orchestrator.md +26 -0
  162. package/template/.aioson/locales/es/agents/pair.md +2 -0
  163. package/template/.aioson/locales/es/agents/pm.md +2 -0
  164. package/template/.aioson/locales/es/agents/product.md +52 -0
  165. package/template/.aioson/locales/es/agents/profiler-enricher.md +2 -0
  166. package/template/.aioson/locales/es/agents/profiler-forge.md +2 -0
  167. package/template/.aioson/locales/es/agents/profiler-researcher.md +2 -0
  168. package/template/.aioson/locales/es/agents/qa.md +2 -0
  169. package/template/.aioson/locales/es/agents/setup.md +2 -0
  170. package/template/.aioson/locales/es/agents/sheldon.md +117 -0
  171. package/template/.aioson/locales/es/agents/squad.md +16 -0
  172. package/template/.aioson/locales/es/agents/tester.md +9 -0
  173. package/template/.aioson/locales/es/agents/ux-ui.md +2 -0
  174. package/template/.aioson/locales/fr/agents/analyst.md +2 -0
  175. package/template/.aioson/locales/fr/agents/architect.md +19 -0
  176. package/template/.aioson/locales/fr/agents/dev.md +64 -4
  177. package/template/.aioson/locales/fr/agents/deyvin.md +2 -0
  178. package/template/.aioson/locales/fr/agents/discovery-design-doc.md +2 -0
  179. package/template/.aioson/locales/fr/agents/genome.md +2 -0
  180. package/template/.aioson/locales/fr/agents/neo.md +2 -0
  181. package/template/.aioson/locales/fr/agents/orache.md +2 -0
  182. package/template/.aioson/locales/fr/agents/orchestrator.md +26 -0
  183. package/template/.aioson/locales/fr/agents/pair.md +2 -0
  184. package/template/.aioson/locales/fr/agents/pm.md +2 -0
  185. package/template/.aioson/locales/fr/agents/product.md +52 -0
  186. package/template/.aioson/locales/fr/agents/profiler-enricher.md +2 -0
  187. package/template/.aioson/locales/fr/agents/profiler-forge.md +2 -0
  188. package/template/.aioson/locales/fr/agents/profiler-researcher.md +2 -0
  189. package/template/.aioson/locales/fr/agents/qa.md +2 -0
  190. package/template/.aioson/locales/fr/agents/setup.md +2 -0
  191. package/template/.aioson/locales/fr/agents/sheldon.md +117 -0
  192. package/template/.aioson/locales/fr/agents/squad.md +16 -0
  193. package/template/.aioson/locales/fr/agents/tester.md +9 -0
  194. package/template/.aioson/locales/fr/agents/ux-ui.md +2 -0
  195. package/template/.aioson/locales/pt-BR/agents/analyst.md +64 -3
  196. package/template/.aioson/locales/pt-BR/agents/architect.md +42 -0
  197. package/template/.aioson/locales/pt-BR/agents/dev.md +147 -14
  198. package/template/.aioson/locales/pt-BR/agents/deyvin.md +47 -0
  199. package/template/.aioson/locales/pt-BR/agents/neo.md +62 -1
  200. package/template/.aioson/locales/pt-BR/agents/orchestrator.md +158 -2
  201. package/template/.aioson/locales/pt-BR/agents/pm.md +95 -1
  202. package/template/.aioson/locales/pt-BR/agents/product.md +145 -18
  203. package/template/.aioson/locales/pt-BR/agents/qa.md +16 -0
  204. package/template/.aioson/locales/pt-BR/agents/setup.md +101 -18
  205. package/template/.aioson/locales/pt-BR/agents/sheldon.md +132 -1
  206. package/template/.aioson/locales/pt-BR/agents/squad.md +14 -0
  207. package/template/.aioson/locales/pt-BR/agents/tester.md +449 -0
  208. package/template/.aioson/rules/README.md +69 -0
  209. package/template/.aioson/rules/data-format-convention.md +136 -0
  210. package/template/.aioson/rules/example-monetary-values.md +30 -0
  211. package/template/.aioson/schemas/squad-manifest.schema.json +124 -3
  212. package/template/.aioson/skills/design/cognitive-core-ui/references/motion.md +2 -0
  213. package/template/.aioson/skills/design/pt.squarespace.com/.skill-meta.json +31 -0
  214. package/template/.aioson/skills/design/pt.squarespace.com/SKILL.md +66 -0
  215. package/template/.aioson/skills/design/pt.squarespace.com/references/components.md +368 -0
  216. package/template/.aioson/skills/design/pt.squarespace.com/references/design-tokens.md +150 -0
  217. package/template/.aioson/skills/design/pt.squarespace.com/references/motion.md +270 -0
  218. package/template/.aioson/skills/design/pt.squarespace.com/references/patterns.md +189 -0
  219. package/template/.aioson/skills/design/pt.squarespace.com/references/websites.md +165 -0
  220. package/template/.aioson/skills/marketing/references/anti-patterns.md +254 -0
  221. package/template/.aioson/skills/marketing/references/fascinations.md +192 -0
  222. package/template/.aioson/skills/marketing/references/five-acts.md +248 -0
  223. package/template/.aioson/skills/marketing/references/market-intelligence.md +198 -0
  224. package/template/.aioson/skills/marketing/references/offer-structure.md +203 -0
  225. package/template/.aioson/skills/marketing/references/one-belief.md +149 -0
  226. package/template/.aioson/skills/marketing/references/patterns.md +218 -0
  227. package/template/.aioson/skills/marketing/references/pms-research.md +193 -0
  228. package/template/.aioson/skills/marketing/vsl-craft.md +385 -0
  229. package/template/.aioson/skills/process/aioson-spec-driven/SKILL.md +1 -0
  230. package/template/.aioson/skills/process/aioson-spec-driven/references/analyst.md +30 -0
  231. package/template/.aioson/skills/process/aioson-spec-driven/references/architect.md +23 -0
  232. package/template/.aioson/skills/process/aioson-spec-driven/references/dev.md +47 -0
  233. package/template/.aioson/skills/process/aioson-spec-driven/references/deyvin.md +27 -0
  234. package/template/.aioson/skills/process/aioson-spec-driven/references/maintenance-and-state.md +35 -0
  235. package/template/.aioson/skills/process/aioson-spec-driven/references/product.md +25 -0
  236. package/template/.aioson/skills/process/aioson-spec-driven/references/qa.md +30 -0
  237. package/template/.aioson/skills/process/aioson-spec-driven/references/sheldon.md +25 -0
  238. package/template/.aioson/skills/process/design-hybrid-forge/SKILL.md +4 -1
  239. package/template/.aioson/skills/process/design-hybrid-forge/references/output-contract.md +15 -0
  240. package/template/.aioson/skills/process/design-hybrid-forge/references/pair-compatibility.md +32 -0
  241. package/template/.aioson/skills/process/design-hybrid-forge/references/quality-gates.md +20 -0
  242. package/template/.aioson/skills/process/simplify/SKILL.md +173 -0
  243. package/template/.aioson/skills/static/context-budget-guide.md +46 -0
  244. package/template/.aioson/skills/static/harness-sensors.md +74 -0
  245. package/template/.aioson/skills/static/landing-page-deploy.md +192 -0
  246. package/template/.aioson/skills/static/landing-page-forge.md +730 -0
  247. package/template/.aioson/skills/static/multi-agent-patterns.md +43 -0
  248. package/template/.aioson/skills/static/react-motion-patterns.md +22 -0
  249. package/template/.aioson/skills/static/static-html-patterns/checklists.md +43 -0
  250. package/template/.aioson/skills/static/static-html-patterns/css-tokens.md +609 -0
  251. package/template/.aioson/skills/static/static-html-patterns/motion.md +193 -0
  252. package/template/.aioson/skills/static/static-html-patterns/premium.md +711 -0
  253. package/template/.aioson/skills/static/static-html-patterns/structure.md +209 -0
  254. package/template/.aioson/skills/static/static-html-patterns/utilities.md +190 -0
  255. package/template/.aioson/skills/static/static-html-patterns.md +58 -1913
  256. package/template/.aioson/skills/static/threejs-patterns.md +929 -0
  257. package/template/.aioson/skills/static/ui-ux-modern.md +1 -0
  258. package/template/.aioson/skills/static/web-research-cache.md +112 -0
  259. package/template/.aioson/tasks/implementation-plan.md +21 -1
  260. package/template/.aioson/tasks/squad-create.md +22 -0
  261. package/template/.aioson/tasks/squad-design.md +30 -0
  262. package/template/.aioson/templates/squads/digital-marketing-agency/template.json +96 -0
  263. package/template/.claude/commands/aioson/agent/design-hybrid-forge.md +5 -0
  264. package/template/.claude/commands/aioson/agent/orache.md +5 -0
  265. package/template/.claude/commands/aioson/agent/sheldon.md +5 -0
  266. package/template/.claude/commands/aioson/agent/site-forge.md +5 -0
  267. package/template/AGENTS.md +55 -3
  268. package/template/CLAUDE.md +31 -0
  269. package/template/OPENCODE.md +4 -0
  270. package/template/researchs/.gitkeep +0 -0
  271. package/template/.aioson/skills/design-system/components/SKILL.md:Zone.Identifier +0 -0
  272. package/template/.aioson/skills/design-system/dashboards/SKILL.md:Zone.Identifier +0 -0
  273. package/template/.aioson/skills/design-system/foundations/SKILL.md:Zone.Identifier +0 -0
  274. package/template/.aioson/skills/design-system/motion/SKILL.md:Zone.Identifier +0 -0
  275. package/template/.aioson/skills/design-system/patterns/SKILL.md:Zone.Identifier +0 -0
@@ -0,0 +1,190 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+ const { openRuntimeDb } = require('../runtime-store');
6
+
7
+ function nowIso() {
8
+ return new Date().toISOString();
9
+ }
10
+
11
+ function createLearningId() {
12
+ return `learning-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
13
+ }
14
+
15
+ function parseFrontmatter(content) {
16
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
17
+ if (!match) return {};
18
+ const result = {};
19
+ for (const line of match[1].split(/\r?\n/)) {
20
+ const colonIdx = line.indexOf(':');
21
+ if (colonIdx === -1) continue;
22
+ const key = line.slice(0, colonIdx).trim();
23
+ const value = line.slice(colonIdx + 1).trim().replace(/^["']|["']$/g, '');
24
+ if (key) result[key] = value;
25
+ }
26
+ return result;
27
+ }
28
+
29
+ function extractSection(content, sectionName) {
30
+ const re = new RegExp(`^#{1,4}\\s+${sectionName}[\\s\\S]*?(?=^#{1,4}\\s|$)`, 'im');
31
+ const match = content.match(re);
32
+ if (!match) return '';
33
+ return match[0].replace(/^#{1,4}\s+\S.*\n/, '').trim();
34
+ }
35
+
36
+ function extractLearnings(content) {
37
+ const section = extractSection(content, 'Session Learnings');
38
+ const learnings = [];
39
+ for (const line of section.split(/\r?\n/)) {
40
+ const trimmed = line.replace(/^[-*]\s*/, '').trim();
41
+ if (!trimmed) continue;
42
+ const typeMatch = trimmed.match(/^\[(process|domain|quality|preference)\]\s+(.+)/i);
43
+ if (typeMatch) {
44
+ learnings.push({ type: typeMatch[1].toLowerCase(), title: typeMatch[2].trim() });
45
+ } else if (trimmed.length > 5) {
46
+ learnings.push({ type: 'process', title: trimmed });
47
+ }
48
+ }
49
+ return learnings;
50
+ }
51
+
52
+ function extractLastCheckpoint(content) {
53
+ const section = extractSection(content, 'last_checkpoint');
54
+ if (section) return section.replace(/^.*:\s*/, '').trim();
55
+ const fmMatch = content.match(/^---[\s\S]*?last_checkpoint:\s*(.+)/m);
56
+ return fmMatch ? fmMatch[1].trim().replace(/^["']|["']$/g, '') : null;
57
+ }
58
+
59
+ function upsertProjectLearning(db, { title, type, featureSlug, evidence, sourceSession }) {
60
+ const existing = db.prepare(
61
+ 'SELECT learning_id, frequency FROM project_learnings WHERE title = ? AND feature_slug = ?'
62
+ ).get(title, featureSlug || null);
63
+
64
+ if (existing) {
65
+ db.prepare(
66
+ 'UPDATE project_learnings SET frequency = ?, last_reinforced = ?, updated_at = ? WHERE learning_id = ?'
67
+ ).run(existing.frequency + 1, nowIso(), nowIso(), existing.learning_id);
68
+ return { action: 'updated', learningId: existing.learning_id };
69
+ }
70
+
71
+ const learningId = createLearningId();
72
+ db.prepare(`
73
+ INSERT INTO project_learnings
74
+ (learning_id, feature_slug, type, title, confidence, frequency, last_reinforced,
75
+ applies_to, source_session, evidence, status, created_at, updated_at)
76
+ VALUES (?, ?, ?, ?, 'medium', 1, ?, 'project', ?, ?, 'active', ?, ?)
77
+ `).run(learningId, featureSlug || null, type, title, nowIso(), sourceSession || null, evidence || null, nowIso(), nowIso());
78
+ return { action: 'inserted', learningId };
79
+ }
80
+
81
+ function syncPlanPhases(db, featureSlug, phaseGates) {
82
+ if (!phaseGates || typeof phaseGates !== 'object') return 0;
83
+ const plan = db.prepare(
84
+ "SELECT plan_id FROM implementation_plans WHERE feature_slug = ? AND status != 'archived' ORDER BY created_at DESC LIMIT 1"
85
+ ).get(featureSlug);
86
+ if (!plan) return 0;
87
+
88
+ let updated = 0;
89
+ const gateMap = { plan: 1, requirements: 2, design: 3 };
90
+ for (const [gate, status] of Object.entries(phaseGates)) {
91
+ const phaseNum = gateMap[gate];
92
+ if (!phaseNum) continue;
93
+ const phase = db.prepare(
94
+ 'SELECT phase_number, status FROM plan_phases WHERE plan_id = ? AND phase_number = ?'
95
+ ).get(plan.plan_id, phaseNum);
96
+ if (!phase) continue;
97
+
98
+ const newStatus = status === 'approved' ? 'completed' : (status === 'pending' ? 'pending' : phase.status);
99
+ if (newStatus !== phase.status) {
100
+ db.prepare(
101
+ 'UPDATE plan_phases SET status = ?, completed_at = ? WHERE plan_id = ? AND phase_number = ?'
102
+ ).run(newStatus, newStatus === 'completed' ? nowIso() : null, plan.plan_id, phaseNum);
103
+ updated++;
104
+ }
105
+ }
106
+ return updated;
107
+ }
108
+
109
+ async function syncSpecFile(db, specPath, { verbose = false } = {}) {
110
+ let content;
111
+ try {
112
+ content = await fs.readFile(specPath, 'utf8');
113
+ } catch {
114
+ return { skipped: true, reason: 'not_found' };
115
+ }
116
+
117
+ const filename = path.basename(specPath, '.md');
118
+ const featureSlug = filename.startsWith('spec-') ? filename.slice(5) : null;
119
+ const fm = parseFrontmatter(content);
120
+ const phaseGates = fm.phase_gates ? JSON.parse(fm.phase_gates.replace(/'/g, '"')).catch?.() || null : null;
121
+
122
+ const learnings = extractLearnings(content);
123
+ const lastCheckpoint = extractLastCheckpoint(content);
124
+
125
+ let learningsSynced = 0;
126
+ for (const { type, title } of learnings) {
127
+ upsertProjectLearning(db, { title, type, featureSlug, sourceSession: filename });
128
+ learningsSynced++;
129
+ }
130
+
131
+ let phasesSynced = 0;
132
+ if (featureSlug && fm.phase_gates) {
133
+ try {
134
+ const gates = JSON.parse(fm.phase_gates.replace(/'/g, '"'));
135
+ phasesSynced = syncPlanPhases(db, featureSlug, gates);
136
+ } catch { /* malformed phase_gates — skip */ }
137
+ }
138
+
139
+ return { featureSlug, learningsSynced, phasesSynced, lastCheckpoint };
140
+ }
141
+
142
+ async function runSpecSync({ args, options = {}, logger }) {
143
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
144
+ const contextDir = path.join(targetDir, '.aioson', 'context');
145
+
146
+ let files;
147
+ try {
148
+ const entries = await fs.readdir(contextDir);
149
+ files = entries.filter((f) => f.startsWith('spec') && f.endsWith('.md'));
150
+ } catch {
151
+ if (!options.json) logger.log('No .aioson/context/ directory found.');
152
+ return { ok: false, reason: 'no_context_dir' };
153
+ }
154
+
155
+ const { db, dbPath } = await openRuntimeDb(targetDir);
156
+ const results = [];
157
+
158
+ try {
159
+ for (const file of files) {
160
+ const result = await syncSpecFile(db, path.join(contextDir, file), { verbose: options.verbose });
161
+ if (!result.skipped) {
162
+ results.push({ file, ...result });
163
+ }
164
+ }
165
+ } finally {
166
+ db.close();
167
+ }
168
+
169
+ const totalLearnings = results.reduce((s, r) => s + (r.learningsSynced || 0), 0);
170
+ const totalPhases = results.reduce((s, r) => s + (r.phasesSynced || 0), 0);
171
+
172
+ if (options.json) {
173
+ return { ok: true, files: results, totalLearnings, totalPhases, dbPath };
174
+ }
175
+
176
+ logger.log(`Spec Sync — ${targetDir}`);
177
+ logger.log('─'.repeat(50));
178
+ for (const r of results) {
179
+ logger.log(`${r.file}`);
180
+ if (r.learningsSynced > 0) logger.log(` Learnings synced: ${r.learningsSynced}`);
181
+ if (r.phasesSynced > 0) logger.log(` Plan phases updated: ${r.phasesSynced}`);
182
+ if (r.lastCheckpoint) logger.log(` last_checkpoint: "${r.lastCheckpoint}"`);
183
+ }
184
+ logger.log('─'.repeat(50));
185
+ logger.log(`Summary: ${totalLearnings} learnings synced, ${totalPhases} plan phases updated`);
186
+
187
+ return { ok: true, files: results, totalLearnings, totalPhases, dbPath };
188
+ }
189
+
190
+ module.exports = { runSpecSync };
@@ -0,0 +1,288 @@
1
+ 'use strict';
2
+
3
+ const fs = require('node:fs/promises');
4
+ const path = require('node:path');
5
+
6
+ function nowIso() {
7
+ return new Date().toISOString();
8
+ }
9
+
10
+ function parseFrontmatter(content) {
11
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
12
+ if (!match) return {};
13
+ const result = {};
14
+ for (const line of match[1].split(/\r?\n/)) {
15
+ const colonIdx = line.indexOf(':');
16
+ if (colonIdx === -1) continue;
17
+ const key = line.slice(0, colonIdx).trim();
18
+ const value = line.slice(colonIdx + 1).trim().replace(/^["']|["']$/g, '');
19
+ if (key) result[key] = value;
20
+ }
21
+ return result;
22
+ }
23
+
24
+ function extractBulletValue(text, label) {
25
+ // Matches "**Label:**" or "- **Label:**" patterns (case-insensitive)
26
+ const re = new RegExp(`\\*\\*${label}[:\\s][*]*\\*\\*:?\\s*(.+)`, 'i');
27
+ const match = text.match(re);
28
+ if (match) return match[1].trim();
29
+ // Fallback: plain "Label:" without bold
30
+ const re2 = new RegExp(`^[-*]?\\s*${label}[:\\s]+(.+)`, 'im');
31
+ const m2 = text.match(re2);
32
+ return m2 ? m2[1].trim() : null;
33
+ }
34
+
35
+ function parsePhases(content) {
36
+ // Match phase headers: ### Fase N — title or ## Phase N — title
37
+ const phasePattern = /^#{2,4}\s+(Fase|Phase)\s+(\d+)\s*[—\-–]\s*(.+)$/im;
38
+ const phases = [];
39
+
40
+ // Split by phase headers
41
+ const lines = content.split(/\r?\n/);
42
+ let currentPhase = null;
43
+ let currentLines = [];
44
+
45
+ for (const line of lines) {
46
+ const m = line.match(/^(#{2,4})\s+(Fase|Phase)\s+(\d+)\s*[—\-–]\s*(.+)$/i);
47
+ if (m) {
48
+ if (currentPhase) {
49
+ currentPhase.body = currentLines.join('\n');
50
+ phases.push(currentPhase);
51
+ }
52
+ currentPhase = {
53
+ number: parseInt(m[3], 10),
54
+ title: m[4].trim(),
55
+ body: ''
56
+ };
57
+ currentLines = [];
58
+ } else if (currentPhase) {
59
+ currentLines.push(line);
60
+ }
61
+ }
62
+ if (currentPhase) {
63
+ currentPhase.body = currentLines.join('\n');
64
+ phases.push(currentPhase);
65
+ }
66
+
67
+ return phases.map((phase) => {
68
+ const what = extractBulletValue(phase.body, 'O que') ||
69
+ extractBulletValue(phase.body, 'What');
70
+ const dependsOn = extractBulletValue(phase.body, 'Depende de') ||
71
+ extractBulletValue(phase.body, 'Depends on');
72
+ const inputs = extractBulletValue(phase.body, 'Artefatos de entrada') ||
73
+ extractBulletValue(phase.body, 'Input artifacts') ||
74
+ extractBulletValue(phase.body, 'Inputs');
75
+ const done = extractBulletValue(phase.body, 'Critério de done') ||
76
+ extractBulletValue(phase.body, 'Done criterion') ||
77
+ extractBulletValue(phase.body, 'Done criteria') ||
78
+ extractBulletValue(phase.body, 'Criterio de done');
79
+ const checkpoint = extractBulletValue(phase.body, 'Checkpoint');
80
+ const parallel = /parallel:\s*true/i.test(phase.body);
81
+
82
+ return {
83
+ number: phase.number,
84
+ title: phase.title,
85
+ what,
86
+ dependsOn,
87
+ inputs,
88
+ done,
89
+ checkpoint,
90
+ parallel
91
+ };
92
+ });
93
+ }
94
+
95
+ function extractParallelNotes(content) {
96
+ // Find "Fases paralelas" or "Parallel" section
97
+ const re = /^#{1,4}\s+(Fases paralelas|Parallel phases?)[^\n]*\n([\s\S]*?)(?=^#{1,4}|\s*$)/im;
98
+ const m = content.match(re);
99
+ if (!m) return [];
100
+ return m[2]
101
+ .split(/\r?\n/)
102
+ .map((l) => l.replace(/^[-*]\s*/, '').trim())
103
+ .filter(Boolean);
104
+ }
105
+
106
+ function extractOpenAssumptions(content) {
107
+ const re = /^#{1,4}\s+(Decisões adiadas|Deferred decisions|Open assumptions)[^\n]*\n([\s\S]*?)(?=^#{1,4}|\s*$)/im;
108
+ const m = content.match(re);
109
+ if (!m) return [];
110
+ return m[2]
111
+ .split(/\r?\n/)
112
+ .map((l) => l.replace(/^[-*]\s*/, '').trim())
113
+ .filter(Boolean)
114
+ .slice(0, 5); // cap to avoid bloat
115
+ }
116
+
117
+ function buildTasksDoc({ title, slug, planPath, phases, parallelNotes, openAssumptions, generatedAt }) {
118
+ const lines = [];
119
+ const header = title || (slug ? `tasks-${slug}` : 'tasks');
120
+
121
+ lines.push(`# Tasks — ${header}`);
122
+ lines.push('');
123
+ lines.push(`> Generated from: \`${path.basename(planPath)}\``);
124
+ lines.push(`> Generated at: ${generatedAt}`);
125
+ lines.push(`> Do not edit manually — regenerate from the plan when phases change.`);
126
+ lines.push('');
127
+
128
+ for (const phase of phases) {
129
+ lines.push(`## Phase ${phase.number} — ${phase.title}`);
130
+ lines.push('');
131
+
132
+ if (phase.inputs) {
133
+ lines.push(`- [ ] Read input artifacts: ${phase.inputs}`);
134
+ } else {
135
+ lines.push(`- [ ] Read required input artifacts`);
136
+ }
137
+
138
+ if (phase.what) {
139
+ lines.push(`- [ ] Execute: ${phase.what}`);
140
+ } else {
141
+ lines.push(`- [ ] Execute planned work`);
142
+ }
143
+
144
+ if (phase.done) {
145
+ lines.push(`- [ ] Verify done criterion: ${phase.done}`);
146
+ } else {
147
+ lines.push(`- [ ] Verify done criterion`);
148
+ }
149
+
150
+ if (phase.checkpoint) {
151
+ lines.push(`- [ ] Pass checkpoint gate: ${phase.checkpoint}`);
152
+ } else {
153
+ lines.push(`- [ ] Pass checkpoint gate`);
154
+ }
155
+
156
+ if (phase.dependsOn && phase.dependsOn !== 'nada' && phase.dependsOn !== 'nothing' && phase.dependsOn !== 'none') {
157
+ lines.push('');
158
+ lines.push(`> Depends on: ${phase.dependsOn}`);
159
+ }
160
+
161
+ if (phase.parallel) {
162
+ lines.push(`> Can run in parallel (no shared entities with adjacent phases)`);
163
+ }
164
+
165
+ lines.push('');
166
+ }
167
+
168
+ if (parallelNotes.length > 0) {
169
+ lines.push('## Parallel notes');
170
+ lines.push('');
171
+ for (const note of parallelNotes) {
172
+ lines.push(`- ${note}`);
173
+ }
174
+ lines.push('');
175
+ }
176
+
177
+ if (openAssumptions.length > 0) {
178
+ lines.push('## Deferred decisions');
179
+ lines.push('');
180
+ for (const item of openAssumptions) {
181
+ lines.push(`- ${item}`);
182
+ }
183
+ lines.push('');
184
+ }
185
+
186
+ return lines.join('\n');
187
+ }
188
+
189
+ async function runSpecTasks({ args, options = {}, logger }) {
190
+ const targetDir = path.resolve(process.cwd(), args[0] || '.');
191
+ const contextDir = path.join(targetDir, '.aioson', 'context');
192
+
193
+ // Resolve plan path
194
+ let planPath;
195
+ if (options.plan) {
196
+ // If absolute path or starts with ./ — use as-is relative to cwd
197
+ planPath = path.resolve(process.cwd(), options.plan);
198
+ } else {
199
+ // Default: look for implementation-plan.md in context dir
200
+ planPath = path.join(contextDir, 'implementation-plan.md');
201
+ }
202
+
203
+ let planContent;
204
+ try {
205
+ planContent = await fs.readFile(planPath, 'utf8');
206
+ } catch {
207
+ if (!options.json) {
208
+ logger.log(`Error: plan file not found: ${planPath}`);
209
+ logger.log('Usage: aioson spec:tasks . --plan=.aioson/context/implementation-plan-{slug}.md');
210
+ }
211
+ return { ok: false, reason: 'plan_not_found', planPath };
212
+ }
213
+
214
+ const fm = parseFrontmatter(planContent);
215
+ const featureSlug = fm.feature_slug && fm.feature_slug !== 'null' ? fm.feature_slug : null;
216
+ const planTitle = fm.project || null;
217
+
218
+ const phases = parsePhases(planContent);
219
+
220
+ if (phases.length === 0) {
221
+ if (!options.json) {
222
+ logger.log('Warning: no phases found in plan. Check that phases follow the format:');
223
+ logger.log(' ### Fase N — title');
224
+ }
225
+ return { ok: false, reason: 'no_phases', planPath };
226
+ }
227
+
228
+ const parallelNotes = extractParallelNotes(planContent);
229
+ const openAssumptions = extractOpenAssumptions(planContent);
230
+ const generatedAt = nowIso();
231
+
232
+ const tasksContent = buildTasksDoc({
233
+ title: planTitle,
234
+ slug: featureSlug,
235
+ planPath,
236
+ phases,
237
+ parallelNotes,
238
+ openAssumptions,
239
+ generatedAt
240
+ });
241
+
242
+ // Determine output path
243
+ const outputFilename = featureSlug ? `tasks-${featureSlug}.md` : 'tasks.md';
244
+ const outputPath = path.join(contextDir, outputFilename);
245
+
246
+ // Ensure context dir exists
247
+ try {
248
+ await fs.mkdir(contextDir, { recursive: true });
249
+ } catch { /* already exists */ }
250
+
251
+ await fs.writeFile(outputPath, tasksContent, 'utf8');
252
+
253
+ if (options.json) {
254
+ return {
255
+ ok: true,
256
+ planPath,
257
+ outputPath,
258
+ featureSlug,
259
+ phasesCount: phases.length,
260
+ generatedAt
261
+ };
262
+ }
263
+
264
+ logger.log(`spec:tasks — ${path.basename(planPath)}`);
265
+ logger.log('─'.repeat(50));
266
+ logger.log(`Phases parsed: ${phases.length}`);
267
+ for (const p of phases) {
268
+ const parallelLabel = p.parallel ? ' [parallel]' : '';
269
+ logger.log(` Phase ${p.number}: ${p.title}${parallelLabel}`);
270
+ }
271
+ if (parallelNotes.length > 0) logger.log(`Parallel notes: ${parallelNotes.length}`);
272
+ if (openAssumptions.length > 0) logger.log(`Deferred decisions: ${openAssumptions.length}`);
273
+ logger.log('─'.repeat(50));
274
+ logger.log(`Output: ${outputPath}`);
275
+ logger.log('');
276
+ logger.log(`Next: open ${outputFilename} and start executing phase by phase with @dev or @deyvin`);
277
+
278
+ return {
279
+ ok: true,
280
+ planPath,
281
+ outputPath,
282
+ featureSlug,
283
+ phasesCount: phases.length,
284
+ generatedAt
285
+ };
286
+ }
287
+
288
+ module.exports = { runSpecTasks };