@jaimevalasek/aioson 1.6.0 → 1.7.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 (252) hide show
  1. package/CHANGELOG.md +49 -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 +9 -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/design-hybrid-forge.md +14 -0
  114. package/template/.aioson/agents/dev.md +242 -24
  115. package/template/.aioson/agents/deyvin.md +66 -8
  116. package/template/.aioson/agents/discovery-design-doc.md +44 -0
  117. package/template/.aioson/agents/genome.md +14 -0
  118. package/template/.aioson/agents/neo.md +78 -1
  119. package/template/.aioson/agents/orache.md +50 -4
  120. package/template/.aioson/agents/orchestrator.md +197 -1
  121. package/template/.aioson/agents/pm.md +35 -0
  122. package/template/.aioson/agents/product.md +50 -5
  123. package/template/.aioson/agents/profiler-enricher.md +14 -0
  124. package/template/.aioson/agents/profiler-forge.md +14 -0
  125. package/template/.aioson/agents/profiler-researcher.md +14 -0
  126. package/template/.aioson/agents/qa.md +172 -21
  127. package/template/.aioson/agents/setup.md +79 -9
  128. package/template/.aioson/agents/sheldon.md +131 -6
  129. package/template/.aioson/agents/site-forge.md +1753 -0
  130. package/template/.aioson/agents/squad.md +162 -0
  131. package/template/.aioson/agents/tester.md +53 -0
  132. package/template/.aioson/agents/ux-ui.md +34 -1
  133. package/template/.aioson/brains/README.md +128 -0
  134. package/template/.aioson/brains/_index.json +16 -0
  135. package/template/.aioson/brains/scripts/query.js +103 -0
  136. package/template/.aioson/brains/site-forge/visual-patterns.brain.json +205 -0
  137. package/template/.aioson/config.md +143 -13
  138. package/template/.aioson/constitution.md +33 -0
  139. package/template/.aioson/context/project-pulse.md +34 -0
  140. package/template/.aioson/docs/LAYERS.md +79 -0
  141. package/template/.aioson/docs/README.md +76 -0
  142. package/template/.aioson/docs/example-external-api-context.md +72 -0
  143. package/template/.aioson/locales/en/agents/architect.md +17 -0
  144. package/template/.aioson/locales/en/agents/dev.md +79 -13
  145. package/template/.aioson/locales/en/agents/orache.md +6 -0
  146. package/template/.aioson/locales/en/agents/orchestrator.md +24 -0
  147. package/template/.aioson/locales/en/agents/product.md +50 -0
  148. package/template/.aioson/locales/en/agents/sheldon.md +115 -0
  149. package/template/.aioson/locales/en/agents/squad.md +14 -0
  150. package/template/.aioson/locales/en/agents/tester.md +6 -0
  151. package/template/.aioson/locales/es/agents/analyst.md +2 -0
  152. package/template/.aioson/locales/es/agents/architect.md +19 -0
  153. package/template/.aioson/locales/es/agents/dev.md +64 -4
  154. package/template/.aioson/locales/es/agents/deyvin.md +2 -0
  155. package/template/.aioson/locales/es/agents/discovery-design-doc.md +2 -0
  156. package/template/.aioson/locales/es/agents/genome.md +2 -0
  157. package/template/.aioson/locales/es/agents/neo.md +2 -0
  158. package/template/.aioson/locales/es/agents/orache.md +2 -0
  159. package/template/.aioson/locales/es/agents/orchestrator.md +26 -0
  160. package/template/.aioson/locales/es/agents/pair.md +2 -0
  161. package/template/.aioson/locales/es/agents/pm.md +2 -0
  162. package/template/.aioson/locales/es/agents/product.md +52 -0
  163. package/template/.aioson/locales/es/agents/profiler-enricher.md +2 -0
  164. package/template/.aioson/locales/es/agents/profiler-forge.md +2 -0
  165. package/template/.aioson/locales/es/agents/profiler-researcher.md +2 -0
  166. package/template/.aioson/locales/es/agents/qa.md +2 -0
  167. package/template/.aioson/locales/es/agents/setup.md +2 -0
  168. package/template/.aioson/locales/es/agents/sheldon.md +117 -0
  169. package/template/.aioson/locales/es/agents/squad.md +16 -0
  170. package/template/.aioson/locales/es/agents/tester.md +9 -0
  171. package/template/.aioson/locales/es/agents/ux-ui.md +2 -0
  172. package/template/.aioson/locales/fr/agents/analyst.md +2 -0
  173. package/template/.aioson/locales/fr/agents/architect.md +19 -0
  174. package/template/.aioson/locales/fr/agents/dev.md +64 -4
  175. package/template/.aioson/locales/fr/agents/deyvin.md +2 -0
  176. package/template/.aioson/locales/fr/agents/discovery-design-doc.md +2 -0
  177. package/template/.aioson/locales/fr/agents/genome.md +2 -0
  178. package/template/.aioson/locales/fr/agents/neo.md +2 -0
  179. package/template/.aioson/locales/fr/agents/orache.md +2 -0
  180. package/template/.aioson/locales/fr/agents/orchestrator.md +26 -0
  181. package/template/.aioson/locales/fr/agents/pair.md +2 -0
  182. package/template/.aioson/locales/fr/agents/pm.md +2 -0
  183. package/template/.aioson/locales/fr/agents/product.md +52 -0
  184. package/template/.aioson/locales/fr/agents/profiler-enricher.md +2 -0
  185. package/template/.aioson/locales/fr/agents/profiler-forge.md +2 -0
  186. package/template/.aioson/locales/fr/agents/profiler-researcher.md +2 -0
  187. package/template/.aioson/locales/fr/agents/qa.md +2 -0
  188. package/template/.aioson/locales/fr/agents/setup.md +2 -0
  189. package/template/.aioson/locales/fr/agents/sheldon.md +117 -0
  190. package/template/.aioson/locales/fr/agents/squad.md +16 -0
  191. package/template/.aioson/locales/fr/agents/tester.md +9 -0
  192. package/template/.aioson/locales/fr/agents/ux-ui.md +2 -0
  193. package/template/.aioson/locales/pt-BR/agents/analyst.md +64 -3
  194. package/template/.aioson/locales/pt-BR/agents/architect.md +42 -0
  195. package/template/.aioson/locales/pt-BR/agents/dev.md +147 -14
  196. package/template/.aioson/locales/pt-BR/agents/deyvin.md +47 -0
  197. package/template/.aioson/locales/pt-BR/agents/neo.md +62 -1
  198. package/template/.aioson/locales/pt-BR/agents/orchestrator.md +158 -2
  199. package/template/.aioson/locales/pt-BR/agents/pm.md +95 -1
  200. package/template/.aioson/locales/pt-BR/agents/product.md +145 -18
  201. package/template/.aioson/locales/pt-BR/agents/qa.md +16 -0
  202. package/template/.aioson/locales/pt-BR/agents/setup.md +101 -18
  203. package/template/.aioson/locales/pt-BR/agents/sheldon.md +132 -1
  204. package/template/.aioson/locales/pt-BR/agents/squad.md +14 -0
  205. package/template/.aioson/locales/pt-BR/agents/tester.md +449 -0
  206. package/template/.aioson/rules/README.md +69 -0
  207. package/template/.aioson/rules/data-format-convention.md +136 -0
  208. package/template/.aioson/rules/example-monetary-values.md +30 -0
  209. package/template/.aioson/schemas/squad-manifest.schema.json +124 -3
  210. package/template/.aioson/skills/design/pt.squarespace.com/.skill-meta.json +31 -0
  211. package/template/.aioson/skills/design/pt.squarespace.com/SKILL.md +66 -0
  212. package/template/.aioson/skills/design/pt.squarespace.com/references/components.md +368 -0
  213. package/template/.aioson/skills/design/pt.squarespace.com/references/design-tokens.md +150 -0
  214. package/template/.aioson/skills/design/pt.squarespace.com/references/motion.md +270 -0
  215. package/template/.aioson/skills/design/pt.squarespace.com/references/patterns.md +189 -0
  216. package/template/.aioson/skills/design/pt.squarespace.com/references/websites.md +165 -0
  217. package/template/.aioson/skills/process/aioson-spec-driven/SKILL.md +1 -0
  218. package/template/.aioson/skills/process/aioson-spec-driven/references/analyst.md +30 -0
  219. package/template/.aioson/skills/process/aioson-spec-driven/references/architect.md +23 -0
  220. package/template/.aioson/skills/process/aioson-spec-driven/references/dev.md +47 -0
  221. package/template/.aioson/skills/process/aioson-spec-driven/references/deyvin.md +27 -0
  222. package/template/.aioson/skills/process/aioson-spec-driven/references/maintenance-and-state.md +35 -0
  223. package/template/.aioson/skills/process/aioson-spec-driven/references/product.md +25 -0
  224. package/template/.aioson/skills/process/aioson-spec-driven/references/qa.md +30 -0
  225. package/template/.aioson/skills/process/aioson-spec-driven/references/sheldon.md +25 -0
  226. package/template/.aioson/skills/process/design-hybrid-forge/SKILL.md +4 -1
  227. package/template/.aioson/skills/process/design-hybrid-forge/references/output-contract.md +15 -0
  228. package/template/.aioson/skills/process/design-hybrid-forge/references/pair-compatibility.md +32 -0
  229. package/template/.aioson/skills/process/design-hybrid-forge/references/quality-gates.md +20 -0
  230. package/template/.aioson/skills/process/simplify/SKILL.md +173 -0
  231. package/template/.aioson/skills/static/context-budget-guide.md +46 -0
  232. package/template/.aioson/skills/static/harness-sensors.md +74 -0
  233. package/template/.aioson/skills/static/multi-agent-patterns.md +43 -0
  234. package/template/.aioson/skills/static/react-motion-patterns.md +22 -0
  235. package/template/.aioson/skills/static/static-html-patterns/checklists.md +43 -0
  236. package/template/.aioson/skills/static/static-html-patterns/css-tokens.md +609 -0
  237. package/template/.aioson/skills/static/static-html-patterns/motion.md +193 -0
  238. package/template/.aioson/skills/static/static-html-patterns/premium.md +711 -0
  239. package/template/.aioson/skills/static/static-html-patterns/structure.md +209 -0
  240. package/template/.aioson/skills/static/static-html-patterns/utilities.md +190 -0
  241. package/template/.aioson/skills/static/static-html-patterns.md +58 -1913
  242. package/template/.aioson/skills/static/threejs-patterns.md +929 -0
  243. package/template/.aioson/skills/static/web-research-cache.md +112 -0
  244. package/template/.aioson/tasks/implementation-plan.md +21 -1
  245. package/template/.claude/commands/aioson/agent/design-hybrid-forge.md +5 -0
  246. package/template/.claude/commands/aioson/agent/orache.md +5 -0
  247. package/template/.claude/commands/aioson/agent/sheldon.md +5 -0
  248. package/template/.claude/commands/aioson/agent/site-forge.md +5 -0
  249. package/template/AGENTS.md +55 -3
  250. package/template/CLAUDE.md +30 -0
  251. package/template/OPENCODE.md +4 -0
  252. package/template/researchs/.gitkeep +0 -0
@@ -0,0 +1,303 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Squad 4-tier verification gate
5
+ *
6
+ * Verifies task deliverables beyond pass/fail:
7
+ *
8
+ * Tier 1 — Exists: File/path exists on disk
9
+ * Tier 2 — Substantive: Not a stub/placeholder (min lines + anti-pattern scan)
10
+ * Tier 3 — Wired: Referenced/imported in expected target location
11
+ * Tier 4 — Functional: Smoke command returns expected output (opt-in, expensive)
12
+ *
13
+ * Used by reflection.js when a task has a `must_haves` contract.
14
+ */
15
+
16
+ const fs = require('node:fs/promises');
17
+ const { execFile } = require('node:child_process');
18
+ const { promisify } = require('node:util');
19
+
20
+ const execFileAsync = promisify(execFile);
21
+
22
+ // Patterns that indicate a file is a stub, not real implementation
23
+ const ANTI_PATTERNS = [
24
+ { pattern: /\bTODO\b/, label: 'TODO marker' },
25
+ { pattern: /\bFIXME\b/, label: 'FIXME marker' },
26
+ { pattern: /\bplaceholder\b/i, label: 'placeholder text' },
27
+ { pattern: /not\s+implemented/i, label: '"not implemented"' },
28
+ { pattern: /return\s+null;\s*\n?\s*\}/m, label: 'empty return null' },
29
+ { pattern: /throw\s+new\s+Error\s*\(['"]not\s+impl/i, label: 'NotImplemented throw' },
30
+ { pattern: /^\s*pass\s*$/m, label: 'Python stub (pass)' },
31
+ { pattern: /^\s*\.\.\.\s*$/m, label: 'ellipsis stub' }
32
+ ];
33
+
34
+ // ─── Artifact string parser ───────────────────────────────────────────────────
35
+
36
+ /**
37
+ * Parse an artifact descriptor string into structured fields.
38
+ *
39
+ * Examples:
40
+ * "src/routes/auth.ts"
41
+ * "src/routes/auth.ts (>50 lines)"
42
+ * "src/routes/auth.ts (>50 lines, exports router)"
43
+ *
44
+ * Returns: { filePath, minLines, wiredPattern }
45
+ */
46
+ function parseArtifact(str, projectDir) {
47
+ const s = String(str).trim();
48
+
49
+ // Extract optional annotations in parentheses
50
+ const parenMatch = s.match(/^(.+?)\s*\(([^)]*)\)\s*$/);
51
+ const rawPath = parenMatch ? parenMatch[1].trim() : s;
52
+ const annotations = parenMatch ? parenMatch[2] : '';
53
+
54
+ // Build absolute path (relative to projectDir if given)
55
+ const filePath = projectDir
56
+ ? require('node:path').resolve(projectDir, rawPath)
57
+ : rawPath;
58
+
59
+ // Parse min lines: ">50 lines" or "50 lines"
60
+ const linesMatch = annotations.match(/>?\s*(\d+)\s+lines?/i);
61
+ const minLines = linesMatch ? parseInt(linesMatch[1], 10) : 5;
62
+
63
+ // Parse wired pattern: any text after "exports" or "imports" or "registers"
64
+ const wiredMatch = annotations.match(/(?:exports?|imports?|registers?)\s+(.+)/i);
65
+ const wiredPattern = wiredMatch ? wiredMatch[1].trim() : null;
66
+
67
+ return { filePath, rawPath, minLines, wiredPattern };
68
+ }
69
+
70
+ /**
71
+ * Parse a key_link descriptor string.
72
+ *
73
+ * Example: "auth router registered in src/app.ts"
74
+ * Returns: { pattern, inFile }
75
+ *
76
+ * Pattern is extracted heuristically from the string.
77
+ */
78
+ function parseKeyLink(str, projectDir) {
79
+ const path = require('node:path');
80
+ const s = String(str).trim();
81
+
82
+ // Pattern: "<thing> in <filepath>" or "<thing> registered/imported/used in <filepath>"
83
+ const inMatch = s.match(/^(.+?)\s+(?:in|from|inside)\s+(\S+\.(?:ts|js|tsx|jsx|py|rb|go|java|php|vue|svelte))\s*$/i);
84
+ if (inMatch) {
85
+ const rawFile = inMatch[2].trim();
86
+ const inFile = projectDir ? path.resolve(projectDir, rawFile) : rawFile;
87
+
88
+ // Extract meaningful keywords from the pattern part (first 3 significant words)
89
+ const patternWords = inMatch[1]
90
+ .replace(/\b(?:registered|imported|used|exported|referenced|wired|connected|added)\b/gi, '')
91
+ .trim()
92
+ .split(/\s+/)
93
+ .filter((w) => w.length > 2)
94
+ .slice(0, 3);
95
+
96
+ return { pattern: patternWords.join('|'), inFile, rawFile };
97
+ }
98
+
99
+ return null;
100
+ }
101
+
102
+ // ─── Tier implementations ─────────────────────────────────────────────────────
103
+
104
+ /** Tier 1: file exists on disk */
105
+ async function verifyExists(filePath) {
106
+ try {
107
+ await fs.access(filePath);
108
+ return { passed: true, tier: 1, file: filePath };
109
+ } catch {
110
+ return { passed: false, tier: 1, file: filePath, reason: 'file does not exist' };
111
+ }
112
+ }
113
+
114
+ /** Tier 2: file is substantive (not a stub) */
115
+ async function verifySubstantive(filePath, minLines = 5) {
116
+ let content;
117
+ try {
118
+ content = await fs.readFile(filePath, 'utf8');
119
+ } catch {
120
+ return { passed: false, tier: 2, file: filePath, reason: 'cannot read file' };
121
+ }
122
+
123
+ const nonEmptyLines = content.split('\n').filter((l) => l.trim().length > 0).length;
124
+ if (nonEmptyLines < minLines) {
125
+ return {
126
+ passed: false, tier: 2, file: filePath,
127
+ reason: `only ${nonEmptyLines} non-empty lines (min: ${minLines})`
128
+ };
129
+ }
130
+
131
+ for (const { pattern, label } of ANTI_PATTERNS) {
132
+ if (pattern.test(content)) {
133
+ return { passed: false, tier: 2, file: filePath, reason: `contains ${label}` };
134
+ }
135
+ }
136
+
137
+ return { passed: true, tier: 2, file: filePath, lines: nonEmptyLines };
138
+ }
139
+
140
+ /** Tier 3: pattern found in a target file (wired/imported/registered) */
141
+ async function verifyWired(pattern, inFile) {
142
+ if (!pattern || !inFile) {
143
+ return { passed: true, tier: 3, skipped: true, reason: 'no wired constraint to check' };
144
+ }
145
+
146
+ let content;
147
+ try {
148
+ content = await fs.readFile(inFile, 'utf8');
149
+ } catch {
150
+ return { passed: false, tier: 3, file: inFile, reason: `cannot read target file: ${inFile}` };
151
+ }
152
+
153
+ const regex = typeof pattern === 'string'
154
+ ? new RegExp(pattern.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'i')
155
+ : pattern;
156
+
157
+ if (regex.test(content)) {
158
+ return { passed: true, tier: 3, pattern, inFile };
159
+ }
160
+
161
+ return {
162
+ passed: false, tier: 3, pattern, inFile,
163
+ reason: `pattern "${pattern}" not found in ${inFile}`
164
+ };
165
+ }
166
+
167
+ /** Tier 4: smoke command returns expected output (optional, expensive) */
168
+ async function verifyFunctional(command, args = [], expectedPattern = null, timeoutMs = 10_000) {
169
+ try {
170
+ const { stdout, stderr } = await execFileAsync(command, args, {
171
+ timeout: timeoutMs,
172
+ shell: false
173
+ });
174
+ const output = stdout + stderr;
175
+
176
+ if (expectedPattern) {
177
+ const regex = typeof expectedPattern === 'string'
178
+ ? new RegExp(expectedPattern, 'i')
179
+ : expectedPattern;
180
+ if (!regex.test(output)) {
181
+ return {
182
+ passed: false, tier: 4, command,
183
+ reason: `expected pattern "${expectedPattern}" not found in command output`
184
+ };
185
+ }
186
+ }
187
+
188
+ return { passed: true, tier: 4, command, output: output.slice(0, 300) };
189
+ } catch (err) {
190
+ return { passed: false, tier: 4, command, reason: err.message.slice(0, 200) };
191
+ }
192
+ }
193
+
194
+ // ─── must_haves checker ───────────────────────────────────────────────────────
195
+
196
+ /**
197
+ * Run must_haves verification against a task's contract.
198
+ *
199
+ * @param {object} mustHaves — { truths?, artifacts?, key_links? }
200
+ * @param {string} output — Task output text (used for truths checks)
201
+ * @param {string} projectDir
202
+ * @returns {Promise<MustHavesResult>}
203
+ *
204
+ * MustHavesResult:
205
+ * {
206
+ * passed: boolean,
207
+ * failures: string[],
208
+ * warnings: string[],
209
+ * details: object[]
210
+ * }
211
+ */
212
+ async function checkMustHaves(mustHaves, output, projectDir) {
213
+ if (!mustHaves) return { passed: true, failures: [], warnings: [], details: [] };
214
+
215
+ const failures = [];
216
+ const warnings = [];
217
+ const details = [];
218
+ const outputText = String(output || '').toLowerCase();
219
+
220
+ // ── Truths: heuristic — check if output mentions the expected state ──────
221
+ for (const truth of (mustHaves.truths || [])) {
222
+ // Extract key nouns/verbs from truth statement and check they appear in output
223
+ const keywords = String(truth)
224
+ .replace(/\b(?:the|a|an|is|are|can|will|should|must|to|of|in|and|or|that)\b/gi, '')
225
+ .split(/\s+/)
226
+ .filter((w) => w.length > 3)
227
+ .slice(0, 4);
228
+
229
+ const found = keywords.some((kw) => outputText.includes(kw.toLowerCase()));
230
+ const result = { type: 'truth', statement: truth, passed: found };
231
+ details.push(result);
232
+
233
+ if (!found) {
234
+ warnings.push(`Truth not evident in output: "${truth.slice(0, 80)}"`);
235
+ }
236
+ }
237
+
238
+ // ── Artifacts: Tier 1 + Tier 2 checks ────────────────────────────────────
239
+ for (const artifactStr of (mustHaves.artifacts || [])) {
240
+ const { filePath, rawPath, minLines, wiredPattern } = parseArtifact(artifactStr, projectDir);
241
+
242
+ const t1 = await verifyExists(filePath);
243
+ details.push({ type: 'artifact', descriptor: artifactStr, ...t1 });
244
+
245
+ if (!t1.passed) {
246
+ failures.push(`Artifact missing: ${rawPath}`);
247
+ continue;
248
+ }
249
+
250
+ const t2 = await verifySubstantive(filePath, minLines);
251
+ details.push({ type: 'artifact_substantive', descriptor: artifactStr, ...t2 });
252
+
253
+ if (!t2.passed) {
254
+ failures.push(`Artifact is a stub: ${rawPath} — ${t2.reason}`);
255
+ continue;
256
+ }
257
+
258
+ // If wired pattern specified, verify it exists in the same file
259
+ if (wiredPattern) {
260
+ const t3 = await verifyWired(wiredPattern, filePath);
261
+ details.push({ type: 'artifact_wired', descriptor: artifactStr, ...t3 });
262
+ if (!t3.passed) {
263
+ warnings.push(`Artifact wiring issue: ${rawPath} — ${t3.reason}`);
264
+ }
265
+ }
266
+ }
267
+
268
+ // ── Key links: Tier 3 checks ──────────────────────────────────────────────
269
+ for (const keyLink of (mustHaves.key_links || [])) {
270
+ const parsed = parseKeyLink(keyLink, projectDir);
271
+
272
+ if (!parsed) {
273
+ // Cannot parse — skip silently (heuristic limitation)
274
+ details.push({ type: 'key_link', descriptor: keyLink, passed: true, skipped: true });
275
+ continue;
276
+ }
277
+
278
+ const t3 = await verifyWired(parsed.pattern, parsed.inFile);
279
+ details.push({ type: 'key_link', descriptor: keyLink, ...t3 });
280
+
281
+ if (!t3.passed) {
282
+ warnings.push(`Key link not wired: "${keyLink.slice(0, 80)}" — ${t3.reason}`);
283
+ }
284
+ }
285
+
286
+ return {
287
+ passed: failures.length === 0,
288
+ failures,
289
+ warnings,
290
+ details
291
+ };
292
+ }
293
+
294
+ module.exports = {
295
+ verifyExists,
296
+ verifySubstantive,
297
+ verifyWired,
298
+ verifyFunctional,
299
+ checkMustHaves,
300
+ parseArtifact,
301
+ parseKeyLink,
302
+ ANTI_PATTERNS
303
+ };
package/src/updater.js CHANGED
@@ -15,17 +15,16 @@ async function updateInstallation(targetDir, options = {}) {
15
15
 
16
16
  const savedProfile = await readInstallProfile(targetDir);
17
17
 
18
- // During update, pass null profile so ALL framework files are installed
19
- // (not just those matching the saved profile).
20
- // This ensures new framework files from the new version are always installed.
21
- // Profile-based filtering only applies to init/install.
18
+ // Default: only update files already present in the target (selective update).
19
+ // With --all: install every file from the template, including new ones not yet installed.
22
20
  const result = await installTemplate(targetDir, {
23
21
  overwrite: true,
24
22
  dryRun: Boolean(options.dryRun),
25
23
  mode: 'update',
26
24
  backupOnOverwrite: true,
27
25
  frameworkDetection: options.frameworkDetection || null,
28
- installProfile: null
26
+ installProfile: null,
27
+ selectiveUpdate: !options.all
29
28
  });
30
29
 
31
30
  return {
@@ -123,12 +123,188 @@ function sleep(ms) {
123
123
  return new Promise((resolve) => setTimeout(resolve, ms));
124
124
  }
125
125
 
126
+ // ─── Research Worker ──────────────────────────────────────────────────────────
127
+
128
+ /**
129
+ * Handle a `type: 'research'` worker.
130
+ *
131
+ * Checks the researchs/ cache first (7-day default TTL), falls back to
132
+ * scraping declared URLs or the topic keyword via web.js fetchPage.
133
+ *
134
+ * Cache location: researchs/{topic}/summary.md
135
+ */
136
+ async function runResearchWorker(projectDir, config, inputPayload) {
137
+ const { fetchPage } = require('./web');
138
+ const research = config.research || {};
139
+ const topic = String(research.topic || inputPayload?.topic || 'general').replace(/[^a-z0-9-]/gi, '-').toLowerCase();
140
+ const cacheHours = Number(research.cache_hours || 168); // 7 days default
141
+ const cacheDir = path.join(projectDir, research.cache_dir || 'researchs', topic);
142
+ const summaryPath = path.join(cacheDir, 'summary.md');
143
+
144
+ // ── Cache check ────────────────────────────────────────────────────────────
145
+ try {
146
+ const stat = await fs.stat(summaryPath);
147
+ const ageMs = Date.now() - stat.mtimeMs;
148
+ const ageHours = ageMs / (1000 * 60 * 60);
149
+ if (ageHours < cacheHours) {
150
+ const cached = await fs.readFile(summaryPath, 'utf8');
151
+ return {
152
+ ok: true,
153
+ output: { topic, summary: cached, cached: true, cache_age_hours: Math.round(ageHours) },
154
+ attempt: 1
155
+ };
156
+ }
157
+ } catch { /* no cache yet */ }
158
+
159
+ // ── Scrape sources ─────────────────────────────────────────────────────────
160
+ const urls = research.urls || inputPayload?.urls || [];
161
+ const maxSources = Number(research.max_sources || 5);
162
+ const pages = [];
163
+
164
+ for (const url of urls.slice(0, maxSources)) {
165
+ try {
166
+ const result = await fetchPage(url, { timeoutMs: 15000, extractLinks: false });
167
+ if (result.ok && result.text) {
168
+ pages.push({ url, content: result.text.slice(0, 3000) });
169
+ }
170
+ } catch { /* skip unreachable sources */ }
171
+ }
172
+
173
+ if (pages.length === 0) {
174
+ return {
175
+ ok: false,
176
+ error: `Research worker "${topic}": no URLs declared and no cached summary. Add "research.urls" to worker.json or provide ?topic= with cached data.`,
177
+ attempts: 1
178
+ };
179
+ }
180
+
181
+ // ── Build summary ──────────────────────────────────────────────────────────
182
+ const ts = new Date().toISOString();
183
+ const summary = [
184
+ `# Research: ${topic}`,
185
+ `_Generated: ${ts} · Sources: ${pages.length}_`,
186
+ '',
187
+ ...pages.map((p, i) => [
188
+ `## Source ${i + 1}: ${p.url}`,
189
+ '',
190
+ p.content.slice(0, 2000),
191
+ ''
192
+ ].join('\n'))
193
+ ].join('\n');
194
+
195
+ await fs.mkdir(cacheDir, { recursive: true });
196
+ await fs.writeFile(summaryPath, summary, 'utf8');
197
+
198
+ return {
199
+ ok: true,
200
+ output: { topic, summary, cached: false, sources: pages.length, generated_at: ts },
201
+ attempt: 1
202
+ };
203
+ }
204
+
205
+ // ─── Skill Worker (Plan 81 §2.2) ─────────────────────────────────────────────
206
+
207
+ /**
208
+ * Handle a `type: 'skill'` worker.
209
+ * Resolves an external Agent Skills Standard skill and executes it.
210
+ *
211
+ * Skill sources:
212
+ * - Local path: ./skills/my-skill/ or .claude/skills/my-skill/
213
+ * - NPM package: npm:@org/skill-name (resolved from node_modules)
214
+ */
215
+ async function runSkillWorker(projectDir, config, inputPayload) {
216
+ const skillRef = config.skill || config.source || '';
217
+ let skillDir;
218
+
219
+ if (skillRef.startsWith('npm:')) {
220
+ // Resolve from node_modules
221
+ const pkgName = skillRef.slice(4);
222
+ skillDir = path.join(projectDir, 'node_modules', pkgName);
223
+ } else {
224
+ // Local path
225
+ skillDir = path.resolve(projectDir, skillRef);
226
+ }
227
+
228
+ // Check SKILL.md exists
229
+ const skillMdPath = path.join(skillDir, 'SKILL.md');
230
+ if (!(await pathExists(skillMdPath))) {
231
+ return {
232
+ ok: false,
233
+ error: `Skill not found: ${skillRef} (expected SKILL.md at ${skillMdPath})`,
234
+ attempts: 0
235
+ };
236
+ }
237
+
238
+ // Check for executable scripts
239
+ const scriptsDir = path.join(skillDir, 'scripts');
240
+ const runScript = path.join(scriptsDir, 'run.js');
241
+ const runPyScript = path.join(scriptsDir, 'run.py');
242
+
243
+ if (await pathExists(runScript)) {
244
+ return spawnWorker(runScript, inputPayload || {}, {}, config.timeout_ms || DEFAULT_TIMEOUT);
245
+ }
246
+
247
+ if (await pathExists(runPyScript)) {
248
+ return spawnWorker(runPyScript, inputPayload || {}, {}, config.timeout_ms || DEFAULT_TIMEOUT);
249
+ }
250
+
251
+ // No executable script — return skill content for LLM-based execution
252
+ const skillContent = await fs.readFile(skillMdPath, 'utf8');
253
+ return {
254
+ ok: true,
255
+ output: {
256
+ type: 'skill-prompt',
257
+ skillPath: skillMdPath,
258
+ content: skillContent.slice(0, 4000),
259
+ message: `Skill "${skillRef}" loaded. No run script found — use skill content as agent instructions.`
260
+ },
261
+ attempt: 1
262
+ };
263
+ }
264
+
265
+ // ─── Agent Memory Loader (Plan 81 §Sprint 4) ────────────────────────────────
266
+
267
+ /**
268
+ * Load per-agent persistent memory if it exists.
269
+ * Returns memory content or null.
270
+ */
271
+ async function loadAgentMemory(projectDir, squadSlug, executorSlug) {
272
+ const memoryPath = path.join(
273
+ projectDir, SQUADS_DIR, squadSlug, 'agent-memory', `${executorSlug}.md`
274
+ );
275
+ try {
276
+ return await fs.readFile(memoryPath, 'utf8');
277
+ } catch {
278
+ return null;
279
+ }
280
+ }
281
+
126
282
  async function runWorker(projectDir, squadSlug, workerSlug, inputPayload, options = {}) {
127
283
  const config = await loadWorkerConfig(projectDir, squadSlug, workerSlug);
128
284
  if (!config) {
129
285
  return { ok: false, error: `Worker config not found: ${workerSlug}`, attempts: 0 };
130
286
  }
131
287
 
288
+ // Skill worker — external skill execution (Plan 81 §2.2)
289
+ if (config.type === 'skill') {
290
+ return runSkillWorker(projectDir, config, inputPayload || {});
291
+ }
292
+
293
+ // Research worker — special handler (4.1)
294
+ if (config.type === 'research') {
295
+ return runResearchWorker(projectDir, config, inputPayload || {});
296
+ }
297
+
298
+ // Load per-agent persistent memory (Plan 81 §Sprint 4)
299
+ const agentMemory = await loadAgentMemory(projectDir, squadSlug, workerSlug);
300
+ if (agentMemory && inputPayload) {
301
+ // Inject into _agent_memory field (readable by Node.js workers via process.argv[2])
302
+ inputPayload._agent_memory = agentMemory;
303
+ // Prefix into context so LLM-based workers receive it as part of their task context
304
+ const existingContext = inputPayload.context || '';
305
+ inputPayload.context = `## Your accumulated knowledge:\n${agentMemory}\n\n---\n\n${existingContext}`.trimEnd();
306
+ }
307
+
132
308
  // Validate inputs
133
309
  const validation = validateInputs(inputPayload || {}, config.inputs);
134
310
  if (!validation.valid) {
@@ -149,6 +325,13 @@ async function runWorker(projectDir, squadSlug, workerSlug, inputPayload, option
149
325
  // Resolve env vars
150
326
  const env = resolveEnvVars(config.env);
151
327
 
328
+ // Expose agent memory path as env var so workers can read it directly
329
+ const memoryFilePath = path.join(projectDir, SQUADS_DIR, squadSlug, 'agent-memory', `${workerSlug}.md`);
330
+ try {
331
+ await fs.access(memoryFilePath);
332
+ env.AIOSON_AGENT_MEMORY_PATH = memoryFilePath;
333
+ } catch { /* no memory file yet — env var omitted */ }
334
+
152
335
  // Resolve MCP env vars if worker declares uses_mcp
153
336
  if (config.uses_mcp && config.uses_mcp.length > 0) {
154
337
  try {
@@ -335,5 +518,7 @@ module.exports = {
335
518
  generateRunJs,
336
519
  generateWorkerReadme,
337
520
  validateInputs,
338
- resolveEnvVars
521
+ resolveEnvVars,
522
+ runSkillWorker,
523
+ loadAgentMemory
339
524
  };
@@ -39,6 +39,15 @@ Check the following before doing anything else:
39
39
  - `.aioson/context/prd-{slug}.md` (feature mode)
40
40
  - `.aioson/context/design-doc.md` + `readiness.md` (if present)
41
41
  - `.aioson/context/discovery.md` + `spec.md` (feature mode — project context, if present)
42
+ - `.aioson/plans/{slug}/manifest.md` (if present — Sheldon phased plans; check subdirectories of `.aioson/plans/`)
43
+
44
+ ## Sheldon enrichment context (RDA-01)
45
+
46
+ If `.aioson/context/sheldon-enrichment.md` (or `sheldon-enrichment-{slug}.md`) exists at session start:
47
+ - Read it silently — do not display its contents to the user
48
+ - Use the gaps identified and pre-made decisions as additional context for discovery
49
+ - Do not re-ask questions that are already documented in the enrichment log
50
+ - If `plan_path` is set in the frontmatter: read the manifest at that path and scope discovery to Phase 1 first
42
51
 
43
52
  ## Context loading policy
44
53
 
@@ -102,6 +111,10 @@ Stop here only when neither `discovery.md` nor local scan artifacts exist. Do no
102
111
 
103
112
  > **Rule:** whenever `discovery.md` is present, always read `spec.md` alongside it — never one without the other.
104
113
 
114
+ ## Web research cache
115
+
116
+ Before running any web search, load `.aioson/skills/static/web-research-cache.md` and follow the protocol: check `researchs/{slug}/summary.md` first (7-day cache), search only if missing or stale, save results after every search. Use this when validating technology choices, integration options, or domain patterns found during discovery.
117
+
105
118
  ## Skills and docs on demand
106
119
 
107
120
  Before deepening discovery:
@@ -111,7 +124,7 @@ Before deepening discovery:
111
124
  - load only the docs that actually matter for this batch
112
125
  - consult local skills only when they improve domain mapping or flow clarity
113
126
  - check `.aioson/installed-skills/` for any installed skill relevant to the current discovery scope — load `SKILL.md` of matching skills, then load per-agent references only if they reduce ambiguity for the current phase
114
- - if `aioson-spec-driven` is installed (`.aioson/installed-skills/aioson-spec-driven/SKILL.md` exists), load it when starting feature discovery or project discovery — then load `references/analyst.md` from that skill
127
+ - if `aioson-spec-driven` exists in `.aioson/installed-skills/aioson-spec-driven/SKILL.md` OR in `.aioson/skills/process/aioson-spec-driven/SKILL.md`, load it when starting feature discovery or project discovery — then load `references/analyst.md` from that skill
115
128
 
116
129
  Do not inflate context without need.
117
130
 
@@ -213,6 +226,7 @@ For each new or modified entity, produce field-level detail (same format as Phas
213
226
  feature: {slug}
214
227
  status: in_progress
215
228
  started: {ISO-date}
229
+ spec_version: 1
216
230
  phase_gates:
217
231
  requirements: approved # approved | pending | needs_work
218
232
  design: pending # approved | pending | skipped (MICRO/SMALL sem @architect)
@@ -255,6 +269,38 @@ AskUserQuestion:
255
269
  - label: "Todos aprovados"
256
270
  ```
257
271
 
272
+ ### Conformance contract (MEDIUM only)
273
+
274
+ If classification is MEDIUM, also generate `.aioson/context/conformance-{slug}.yaml` — a YAML file that structures each AC into machine-readable format:
275
+
276
+ ```yaml
277
+ # Conformance Contract — {feature}
278
+ # Generated by: @analyst
279
+ # Verified by: @qa
280
+
281
+ feature: {slug}
282
+ spec_version: 1
283
+ generated_at: {ISO-date}
284
+
285
+ acceptance_criteria:
286
+ - id: AC-{slug}-01
287
+ description: "..."
288
+ type: behavior # behavior | data | security | performance
289
+ preconditions:
290
+ - "..."
291
+ action: "..."
292
+ expected:
293
+ - "..."
294
+ negative_cases:
295
+ - input: "..."
296
+ expected: "..."
297
+ ```
298
+
299
+ Rules:
300
+ - Only for MEDIUM classification — do not generate for MICRO or SMALL
301
+ - @qa uses it as a structured verification checklist
302
+ - @dev uses it to understand exact expected behavior for test writing
303
+
258
304
  Then tell the user: "Feature spec ready. Activate **@dev** to implement — it will read `prd-{slug}.md`, `requirements-{slug}.md`, and `spec-{slug}.md`."
259
305
 
260
306
  ## MICRO shortcut
@@ -298,6 +344,7 @@ Generate `.aioson/context/discovery.md` with the following sections:
298
344
  - Do not finalize any output file with missing or assumed fields.
299
345
  - In feature mode: never duplicate content already in `discovery.md` — only document what is new or changed.
300
346
  - If `readiness.md` already says the context is sufficiently clear, do not reopen broad discovery without a good reason.
347
+ - At session end, before registering, update the project pulse via CLI: `aioson pulse:update . --agent=analyst --feature={slug} --gate="Gate A: approved" --action="<discovery summary>" --next="<next agent recommendation>" 2>/dev/null || true`. If `aioson` CLI is not available, update `.aioson/context/project-pulse.md` manually.
301
348
  - At session end, after writing the discovery file, register the session: `aioson agent:done . --agent=analyst --summary="<one-line summary of discovery produced>" 2>/dev/null || true`
302
349
  - If `aioson` CLI is not available, write a devlog at session end following the "Devlog" section in `.aioson/config.md`.
303
350
 
@@ -309,3 +356,17 @@ Ative: `/architect` ou `/dev`
309
356
 
310
357
  Também disponível: `/sheldon` (enriquecimento adicional), `/qa` (revisão dos requisitos)
311
358
  ---
359
+
360
+ ## Continuation Protocol
361
+
362
+ Before ending your response, always append:
363
+
364
+ ---
365
+ ## Next Up
366
+ - Domain modeled: [scope]
367
+ - Next step: `@architect` (technical decisions) or `@product` (PRD refinement) or `/sheldon` (enrichment)
368
+ - `/clear` → fresh context window before continuing
369
+
370
+ **Session artifacts written:**
371
+ - [ ] [list each file created or modified]
372
+ ---