@rubytech/create-maxy-code 0.1.60 → 0.1.62

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 (163) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/admin/PLUGIN.md +1 -1
  3. package/payload/platform/plugins/brochures/skills/property-brochure/SKILL.md +26 -3
  4. package/payload/platform/plugins/brochures/skills/property-brochure/references/registers.md +1 -1
  5. package/payload/platform/plugins/docs/references/admin-session.md +2 -2
  6. package/payload/platform/plugins/docs/references/internals.md +1 -1
  7. package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
  8. package/payload/platform/plugins/epc/.claude-plugin/plugin.json +17 -0
  9. package/payload/platform/plugins/epc/PLUGIN.md +85 -0
  10. package/payload/platform/plugins/epc/mcp/dist/index.d.ts +2 -0
  11. package/payload/platform/plugins/epc/mcp/dist/index.d.ts.map +1 -0
  12. package/payload/platform/plugins/epc/mcp/dist/index.js +117 -0
  13. package/payload/platform/plugins/epc/mcp/dist/index.js.map +1 -0
  14. package/payload/platform/plugins/epc/mcp/dist/lib/crypto.d.ts +3 -0
  15. package/payload/platform/plugins/epc/mcp/dist/lib/crypto.d.ts.map +1 -0
  16. package/payload/platform/plugins/epc/mcp/dist/lib/crypto.js +72 -0
  17. package/payload/platform/plugins/epc/mcp/dist/lib/crypto.js.map +1 -0
  18. package/payload/platform/plugins/epc/mcp/dist/lib/epc-api.d.ts +60 -0
  19. package/payload/platform/plugins/epc/mcp/dist/lib/epc-api.d.ts.map +1 -0
  20. package/payload/platform/plugins/epc/mcp/dist/lib/epc-api.js +181 -0
  21. package/payload/platform/plugins/epc/mcp/dist/lib/epc-api.js.map +1 -0
  22. package/payload/platform/plugins/epc/mcp/dist/lib/file-crypto.d.ts +3 -0
  23. package/payload/platform/plugins/epc/mcp/dist/lib/file-crypto.d.ts.map +1 -0
  24. package/payload/platform/plugins/epc/mcp/dist/lib/file-crypto.js +49 -0
  25. package/payload/platform/plugins/epc/mcp/dist/lib/file-crypto.js.map +1 -0
  26. package/payload/platform/plugins/epc/mcp/dist/lib/key-store.d.ts +15 -0
  27. package/payload/platform/plugins/epc/mcp/dist/lib/key-store.d.ts.map +1 -0
  28. package/payload/platform/plugins/epc/mcp/dist/lib/key-store.js +130 -0
  29. package/payload/platform/plugins/epc/mcp/dist/lib/key-store.js.map +1 -0
  30. package/payload/platform/plugins/epc/mcp/dist/lib/neo4j.d.ts +5 -0
  31. package/payload/platform/plugins/epc/mcp/dist/lib/neo4j.d.ts.map +1 -0
  32. package/payload/platform/plugins/epc/mcp/dist/lib/neo4j.js +38 -0
  33. package/payload/platform/plugins/epc/mcp/dist/lib/neo4j.js.map +1 -0
  34. package/payload/platform/plugins/epc/mcp/dist/tools/key-deregister.d.ts +4 -0
  35. package/payload/platform/plugins/epc/mcp/dist/tools/key-deregister.d.ts.map +1 -0
  36. package/payload/platform/plugins/epc/mcp/dist/tools/key-deregister.js +9 -0
  37. package/payload/platform/plugins/epc/mcp/dist/tools/key-deregister.js.map +1 -0
  38. package/payload/platform/plugins/epc/mcp/dist/tools/key-list.d.ts +4 -0
  39. package/payload/platform/plugins/epc/mcp/dist/tools/key-list.d.ts.map +1 -0
  40. package/payload/platform/plugins/epc/mcp/dist/tools/key-list.js +10 -0
  41. package/payload/platform/plugins/epc/mcp/dist/tools/key-list.js.map +1 -0
  42. package/payload/platform/plugins/epc/mcp/dist/tools/key-register.d.ts +5 -0
  43. package/payload/platform/plugins/epc/mcp/dist/tools/key-register.d.ts.map +1 -0
  44. package/payload/platform/plugins/epc/mcp/dist/tools/key-register.js +36 -0
  45. package/payload/platform/plugins/epc/mcp/dist/tools/key-register.js.map +1 -0
  46. package/payload/platform/plugins/epc/mcp/dist/tools/lookup.d.ts +10 -0
  47. package/payload/platform/plugins/epc/mcp/dist/tools/lookup.d.ts.map +1 -0
  48. package/payload/platform/plugins/epc/mcp/dist/tools/lookup.js +30 -0
  49. package/payload/platform/plugins/epc/mcp/dist/tools/lookup.js.map +1 -0
  50. package/payload/platform/plugins/epc/mcp/package-lock.json +2566 -0
  51. package/payload/platform/plugins/epc/mcp/package.json +21 -0
  52. package/payload/platform/plugins/epc/mcp/src/__tests__/epc-api.test.ts +251 -0
  53. package/payload/platform/plugins/epc/mcp/src/__tests__/key-roundtrip.test.ts +113 -0
  54. package/payload/platform/plugins/epc/mcp/src/__tests__/lookup.test.ts +181 -0
  55. package/payload/platform/plugins/epc/mcp/src/__tests__/schema-parity.test.ts +54 -0
  56. package/payload/platform/plugins/epc/mcp/src/index.ts +156 -0
  57. package/payload/platform/plugins/epc/mcp/src/lib/crypto.ts +79 -0
  58. package/payload/platform/plugins/epc/mcp/src/lib/epc-api.ts +241 -0
  59. package/payload/platform/plugins/epc/mcp/src/lib/file-crypto.ts +55 -0
  60. package/payload/platform/plugins/epc/mcp/src/lib/key-store.ts +172 -0
  61. package/payload/platform/plugins/epc/mcp/src/lib/neo4j.ts +47 -0
  62. package/payload/platform/plugins/epc/mcp/src/tools/key-deregister.ts +9 -0
  63. package/payload/platform/plugins/epc/mcp/src/tools/key-list.ts +10 -0
  64. package/payload/platform/plugins/epc/mcp/src/tools/key-register.ts +44 -0
  65. package/payload/platform/plugins/epc/mcp/src/tools/lookup.ts +43 -0
  66. package/payload/platform/plugins/epc/mcp/tsconfig.json +20 -0
  67. package/payload/platform/plugins/epc/mcp/vitest.config.ts +8 -0
  68. package/payload/platform/plugins/preval/.claude-plugin/plugin.json +1 -1
  69. package/payload/platform/plugins/preval/PLUGIN.md +25 -17
  70. package/payload/platform/plugins/preval/skills/property-preval/SKILL.md +107 -39
  71. package/payload/platform/plugins/preval/skills/property-preval/references/render.py +449 -84
  72. package/payload/platform/plugins/preval/skills/property-preval/references/template-inputs.schema.json +126 -80
  73. package/payload/platform/plugins/preval/skills/property-preval/references/template.html +183 -124
  74. package/payload/platform/services/claude-session-manager/dist/http-server.d.ts.map +1 -1
  75. package/payload/platform/services/claude-session-manager/dist/http-server.js +6 -1
  76. package/payload/platform/services/claude-session-manager/dist/http-server.js.map +1 -1
  77. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts +7 -0
  78. package/payload/platform/services/claude-session-manager/dist/pty-spawner.d.ts.map +1 -1
  79. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js +8 -0
  80. package/payload/platform/services/claude-session-manager/dist/pty-spawner.js.map +1 -1
  81. package/payload/platform/templates/agents/admin/IDENTITY.md +1 -1
  82. package/payload/platform/templates/agents/admin/SOUL.md +1 -1
  83. package/payload/premium-plugins/real-agent/BUNDLE.md +4 -2
  84. package/payload/premium-plugins/real-agent/agents/valuer.md +1 -1
  85. package/payload/premium-plugins/real-agent/plugins/.claude-plugin/marketplace.json +5 -0
  86. package/payload/premium-plugins/real-agent/plugins/brochures/skills/property-brochure/SKILL.md +26 -3
  87. package/payload/premium-plugins/real-agent/plugins/brochures/skills/property-brochure/references/registers.md +1 -1
  88. package/payload/premium-plugins/real-agent/plugins/epc/.claude-plugin/plugin.json +17 -0
  89. package/payload/premium-plugins/real-agent/plugins/epc/PLUGIN.md +85 -0
  90. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/index.d.ts +2 -0
  91. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/index.d.ts.map +1 -0
  92. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/index.js +117 -0
  93. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/index.js.map +1 -0
  94. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/crypto.d.ts +3 -0
  95. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/crypto.d.ts.map +1 -0
  96. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/crypto.js +72 -0
  97. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/crypto.js.map +1 -0
  98. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/epc-api.d.ts +60 -0
  99. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/epc-api.d.ts.map +1 -0
  100. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/epc-api.js +181 -0
  101. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/epc-api.js.map +1 -0
  102. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/file-crypto.d.ts +3 -0
  103. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/file-crypto.d.ts.map +1 -0
  104. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/file-crypto.js +49 -0
  105. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/file-crypto.js.map +1 -0
  106. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/key-store.d.ts +15 -0
  107. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/key-store.d.ts.map +1 -0
  108. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/key-store.js +130 -0
  109. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/key-store.js.map +1 -0
  110. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/neo4j.d.ts +5 -0
  111. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/neo4j.d.ts.map +1 -0
  112. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/neo4j.js +38 -0
  113. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/lib/neo4j.js.map +1 -0
  114. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-deregister.d.ts +4 -0
  115. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-deregister.d.ts.map +1 -0
  116. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-deregister.js +9 -0
  117. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-deregister.js.map +1 -0
  118. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-list.d.ts +4 -0
  119. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-list.d.ts.map +1 -0
  120. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-list.js +10 -0
  121. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-list.js.map +1 -0
  122. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-register.d.ts +5 -0
  123. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-register.d.ts.map +1 -0
  124. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-register.js +36 -0
  125. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/key-register.js.map +1 -0
  126. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/lookup.d.ts +10 -0
  127. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/lookup.d.ts.map +1 -0
  128. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/lookup.js +30 -0
  129. package/payload/premium-plugins/real-agent/plugins/epc/mcp/dist/tools/lookup.js.map +1 -0
  130. package/payload/premium-plugins/real-agent/plugins/epc/mcp/package-lock.json +2566 -0
  131. package/payload/premium-plugins/real-agent/plugins/epc/mcp/package.json +21 -0
  132. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/__tests__/epc-api.test.ts +251 -0
  133. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/__tests__/key-roundtrip.test.ts +113 -0
  134. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/__tests__/lookup.test.ts +181 -0
  135. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/__tests__/schema-parity.test.ts +54 -0
  136. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/index.ts +156 -0
  137. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/lib/crypto.ts +79 -0
  138. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/lib/epc-api.ts +241 -0
  139. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/lib/file-crypto.ts +55 -0
  140. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/lib/key-store.ts +172 -0
  141. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/lib/neo4j.ts +47 -0
  142. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/tools/key-deregister.ts +9 -0
  143. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/tools/key-list.ts +10 -0
  144. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/tools/key-register.ts +44 -0
  145. package/payload/premium-plugins/real-agent/plugins/epc/mcp/src/tools/lookup.ts +43 -0
  146. package/payload/premium-plugins/real-agent/plugins/epc/mcp/tsconfig.json +20 -0
  147. package/payload/premium-plugins/real-agent/plugins/epc/mcp/vitest.config.ts +8 -0
  148. package/payload/premium-plugins/real-agent/plugins/preval/.claude-plugin/plugin.json +1 -1
  149. package/payload/premium-plugins/real-agent/plugins/preval/PLUGIN.md +25 -17
  150. package/payload/premium-plugins/real-agent/plugins/preval/skills/property-preval/SKILL.md +107 -39
  151. package/payload/premium-plugins/real-agent/plugins/preval/skills/property-preval/references/render.py +449 -84
  152. package/payload/premium-plugins/real-agent/plugins/preval/skills/property-preval/references/template-inputs.schema.json +126 -80
  153. package/payload/premium-plugins/real-agent/plugins/preval/skills/property-preval/references/template.html +183 -124
  154. package/payload/server/public/assets/{admin-Bp-BjBCX.js → admin-DhN3G8W7.js} +1 -1
  155. package/payload/server/public/assets/{data-BGUAGVkV.js → data-B2ZVXOcE.js} +1 -1
  156. package/payload/server/public/assets/{graph-MvYxZOBF.js → graph-BAMGPHrK.js} +1 -1
  157. package/payload/server/public/assets/{graph-labels-D865qb3K.js → graph-labels-D9eBbvxo.js} +1 -1
  158. package/payload/server/public/assets/{page-C2b1nlOc.js → page-CV27Al6Z.js} +1 -1
  159. package/payload/server/public/assets/{page--hOVRrgN.js → page-DjdVMWCz.js} +1 -1
  160. package/payload/server/public/data.html +3 -3
  161. package/payload/server/public/graph.html +3 -3
  162. package/payload/server/public/index.html +4 -4
  163. package/payload/server/server.js +3 -16
@@ -1,24 +1,78 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
3
  "title": "property-preval inputs",
4
- "description": "Single-source contract for render.py. Mirrors the 12 property-data MCP responses for one full UK address.",
4
+ "description": "Single-source contract for render.py. Mirrors the 12 property-data MCP responses + 1 EPC MCP response for one full UK address, plus operator-supplied brand, subject, market_summary, and agent_listings.",
5
5
  "type": "object",
6
- "required": ["out_dir", "filename_stem", "address", "postcode", "generated", "valuation", "area", "demand_trend"],
6
+ "required": [
7
+ "out_dir", "filename_stem", "address", "postcode", "generated",
8
+ "brand", "subject", "valuation", "area", "demand_trend",
9
+ "market_summary", "agent_listings"
10
+ ],
7
11
  "properties": {
8
- "out_dir": { "type": "string", "description": "Absolute path to the output directory (created if missing)." },
9
- "filename_stem": { "type": "string", "description": "PDF filename stem; .pdf appended by the renderer." },
10
- "address": { "type": "string", "description": "Full UK address as the operator gave it." },
11
- "postcode": { "type": "string", "description": "Postcode derived from the address, e.g. 'CM23 2UJ'." },
12
- "generated": { "type": "string", "description": "Human date, e.g. '19 May 2026'." },
12
+ "out_dir": { "type": "string", "description": "Absolute path to the output directory (created if missing)." },
13
+ "filename_stem": { "type": "string", "description": "PDF filename stem; .pdf appended by the renderer." },
14
+ "address": { "type": "string", "description": "Full UK address as the operator gave it." },
15
+ "postcode": { "type": "string", "description": "Postcode derived from the address, e.g. 'CM23 2UJ'." },
16
+ "generated": { "type": "string", "description": "Human date, e.g. '19 May 2026'." },
17
+
18
+ "brand": {
19
+ "type": "object",
20
+ "required": ["name", "tagline", "primary", "primary_dark", "accent", "paper", "paper_banded", "rule"],
21
+ "properties": {
22
+ "name": { "type": "string" },
23
+ "tagline": { "type": "string" },
24
+ "primary": { "type": "string", "description": "Hex, e.g. '#064444'." },
25
+ "primary_dark": { "type": "string" },
26
+ "accent": { "type": "string" },
27
+ "paper": { "type": "string" },
28
+ "paper_banded": { "type": "string" },
29
+ "rule": { "type": "string" },
30
+ "logo_light": { "type": "string", "description": "Optional filesystem path to white-on-transparent logo for the cover." },
31
+ "logo_dark": { "type": "string", "description": "Optional filesystem path to full-colour logo for body-page headers." }
32
+ }
33
+ },
34
+
35
+ "subject": {
36
+ "type": "object",
37
+ "required": ["cover_hero", "sqft", "sqft_m2"],
38
+ "properties": {
39
+ "cover_hero": { "type": "string", "description": "Filesystem path or URL to a photo of the subject property — required for the cover. Renderer asserts the file exists, is ≥ 1200×800, and is not symlinked to another property's folder." },
40
+ "beds": { "type": ["integer", "null"] },
41
+ "type": { "type": ["string", "null"], "description": "e.g. 'Semi-detached', 'Terraced', 'Flat'." },
42
+ "tenure": { "type": ["string", "null"], "description": "e.g. 'Freehold', 'Leasehold'." },
43
+ "condition": { "type": ["string", "null"], "description": "'well' | 'average' | 'needs work'." },
44
+ "sqft": { "type": "integer", "description": "Total internal area in square feet. Required. Pulled from EPC (m² × 10.7639, rounded) when available; operator paste-in is the only override. Null aborts the run." },
45
+ "sqft_m2": { "type": "number", "description": "Total internal area in square metres from the EPC register. Required alongside sqft." },
46
+ "epc_current": { "type": ["string", "null"], "description": "Current energy rating, e.g. 'C'." },
47
+ "epc_potential": { "type": ["string", "null"], "description": "Potential energy rating, e.g. 'B'." },
48
+ "epc_lodgement": { "type": ["string", "null"], "description": "ISO date of the EPC lodgement." },
49
+ "narrative": { "type": ["string", "null"], "description": "One short paragraph the operator writes about the subject — shown under the valuation panel." },
50
+ "adjustments": {
51
+ "type": "array",
52
+ "description": "Operator-curated esoteric factors that shift the headline range. Deltas sum linearly into the final factor (1 + Σ delta_pct/100).",
53
+ "items": {
54
+ "type": "object",
55
+ "required": ["label", "delta_pct"],
56
+ "properties": {
57
+ "label": { "type": "string" },
58
+ "delta_pct": { "type": "number", "description": "Percent change, e.g. 3 for +3%, -8 for -8%." }
59
+ }
60
+ }
61
+ }
62
+ }
63
+ },
64
+
13
65
  "valuation": {
14
66
  "type": "object",
15
- "required": ["avg_asking", "asking_psf", "avg_sold", "sold_psf", "sold_comps", "asking_comps"],
67
+ "required": ["avg_asking", "asking_psf", "avg_sold", "sold_psf", "sold_psf_low", "sold_psf_high", "sold_comps", "asking_comps"],
16
68
  "properties": {
17
- "avg_asking": { "type": "integer", "description": "Mean asking £ (from property-data-prices)." },
18
- "asking_psf": { "type": "integer", "description": "Asking £/sqft (from property-data-prices-per-sqf)." },
19
- "avg_sold": { "type": "integer", "description": "Mean sold £ (from property-data-sold-prices)." },
20
- "sold_psf": { "type": "integer", "description": "Sold £/sqft (from property-data-sold-prices-per-sqf)." },
21
- "sold_range": { "type": "array", "items": { "type": "integer" }, "minItems": 2, "maxItems": 2, "description": "Optional 70pc range [low, high]." },
69
+ "avg_asking": { "type": "integer", "description": "Mean asking £ from property-data-prices." },
70
+ "asking_psf": { "type": "integer", "description": "Mean asking £/sqft." },
71
+ "avg_sold": { "type": "integer", "description": "Mean sold £ from property-data-sold-prices." },
72
+ "sold_psf": { "type": "integer", "description": "Mean sold £/sqft (midpoint of valuation)." },
73
+ "sold_psf_low": { "type": "integer", "description": "Lower bound of the sold £/sqft 70pc range." },
74
+ "sold_psf_high":{ "type": "integer", "description": "Upper bound of the sold £/sqft 70pc range." },
75
+ "sold_range": { "type": "array", "items": { "type": "integer" }, "minItems": 2, "maxItems": 2, "description": "Optional 70pc sold-£ range [low, high]." },
22
76
  "sold_comps": {
23
77
  "type": "array",
24
78
  "description": "Nearest sold transactions (≤ 6 rows in the table).",
@@ -26,10 +80,10 @@
26
80
  "type": "object",
27
81
  "required": ["date", "address", "type", "price"],
28
82
  "properties": {
29
- "date": { "type": "string" },
30
- "address": { "type": "string" },
31
- "type": { "type": "string" },
32
- "price": { "type": "integer" }
83
+ "date": { "type": "string" },
84
+ "address": { "type": "string" },
85
+ "type": { "type": "string" },
86
+ "price": { "type": "integer" }
33
87
  }
34
88
  }
35
89
  },
@@ -40,80 +94,47 @@
40
94
  "type": "object",
41
95
  "required": ["address", "price"],
42
96
  "properties": {
43
- "address": { "type": "string" },
44
- "price": { "type": "integer" },
45
- "beds": { "type": ["integer", "null"] },
46
- "type": { "type": ["string", "null"] }
97
+ "address": { "type": "string" },
98
+ "price": { "type": "integer" },
99
+ "beds": { "type": ["integer", "null"] },
100
+ "type": { "type": ["string", "null"] }
47
101
  }
48
102
  }
49
103
  }
50
104
  }
51
105
  },
106
+
52
107
  "area": {
53
108
  "type": "object",
54
109
  "required": ["crime_rating", "flood_risk", "council_tax_band_d", "population", "crime_breakdown", "council_tax_bands", "headline"],
55
110
  "properties": {
56
- "crime_rating": { "type": "string", "description": "e.g. 'Below average'." },
57
- "flood_risk": { "type": "string", "description": "e.g. 'Very low'." },
58
- "council_tax_band_d": { "type": "integer", "description": "Annual £ at band D." },
59
- "population": { "type": "integer", "description": "Local area population." },
60
- "crime_breakdown": {
61
- "type": "array",
62
- "items": {
63
- "type": "object",
64
- "required": ["category", "count"],
65
- "properties": {
66
- "category": { "type": "string" },
67
- "count": { "type": "integer" }
68
- }
69
- }
70
- },
71
- "council_tax_bands": {
72
- "type": "array",
73
- "description": "Band → annual £ rows.",
74
- "items": {
75
- "type": "object",
76
- "required": ["band", "amount"],
77
- "properties": {
78
- "band": { "type": "string" },
79
- "amount": { "type": "integer" }
80
- }
81
- }
82
- },
83
- "headline": { "type": "string", "description": "One-sentence area read." }
111
+ "crime_rating": { "type": "string" },
112
+ "flood_risk": { "type": "string" },
113
+ "council_tax_band_d": { "type": "integer" },
114
+ "population": { "type": "integer" },
115
+ "crime_breakdown": { "type": "array", "items": { "type": "object", "required": ["category", "count"], "properties": { "category": {"type":"string"}, "count": {"type":"integer"} } } },
116
+ "council_tax_bands": { "type": "array", "items": { "type": "object", "required": ["band", "amount"], "properties": { "band": {"type":"string"}, "amount": {"type":"integer"} } } },
117
+ "headline": { "type": "string" }
84
118
  }
85
119
  },
120
+
86
121
  "demand_trend": {
87
122
  "type": "object",
88
123
  "required": ["demand_rating", "dom", "turnover_pct", "for_sale", "growth_series", "growth_psf_series", "rental", "type_dist"],
89
124
  "properties": {
90
- "demand_rating": { "type": "string", "description": "e.g. 'Balanced market'." },
91
- "dom": { "type": "integer", "description": "Days on market." },
92
- "turnover_pct": { "type": "integer", "description": "Turnover percent per month." },
93
- "for_sale": { "type": "integer", "description": "Current for-sale count." },
125
+ "demand_rating": { "type": "string" },
126
+ "dom": { "type": "integer" },
127
+ "turnover_pct": { "type": "integer" },
128
+ "for_sale": { "type": "integer" },
129
+ "sales_per_month":{ "type": "integer" },
130
+ "yoy_growth_pct": { "type": "number" },
94
131
  "growth_series": {
95
132
  "type": "array",
96
- "description": "7-year £ series, oldest first.",
97
- "items": {
98
- "type": "object",
99
- "required": ["label", "price"],
100
- "properties": {
101
- "label": { "type": "string" },
102
- "price": { "type": "integer" }
103
- }
104
- }
133
+ "items": { "type": "object", "required": ["label", "price"], "properties": { "label": {"type":"string"}, "price": {"type":"integer"} } }
105
134
  },
106
135
  "growth_psf_series": {
107
136
  "type": "array",
108
- "description": "7-year £/sqft series, oldest first.",
109
- "items": {
110
- "type": "object",
111
- "required": ["label", "psf"],
112
- "properties": {
113
- "label": { "type": "string" },
114
- "psf": { "type": "integer" }
115
- }
116
- }
137
+ "items": { "type": "object", "required": ["label", "psf"], "properties": { "label": {"type":"string"}, "psf": {"type":"integer"} } }
117
138
  },
118
139
  "rental": {
119
140
  "type": "object",
@@ -127,15 +148,40 @@
127
148
  },
128
149
  "type_dist": {
129
150
  "type": "array",
130
- "description": "Property-type mix doughnut rows.",
131
- "items": {
132
- "type": "object",
133
- "required": ["name", "pct"],
134
- "properties": {
135
- "name": { "type": "string" },
136
- "pct": { "type": "number" }
137
- }
138
- }
151
+ "items": { "type": "object", "required": ["name", "pct"], "properties": { "name": {"type":"string"}, "pct": {"type":"number"} } }
152
+ }
153
+ }
154
+ },
155
+
156
+ "market_summary": {
157
+ "type": "object",
158
+ "required": ["headline", "paragraphs", "demand_gauge_position", "price_growth_note", "demand_note"],
159
+ "properties": {
160
+ "headline": { "type": "string", "description": "One-line display-italic h2 for the market context page." },
161
+ "paragraphs": { "type": "array", "minItems": 2, "items": { "type": "string" }, "description": "2–3 short paragraphs. First gets a drop cap. Fewer than 2 aborts the run." },
162
+ "demand_gauge_position": { "type": "string", "description": "Percentage string for the gauge marker position, e.g. '62%'." },
163
+ "price_growth_note": { "type": "string" },
164
+ "demand_note": { "type": "string" }
165
+ }
166
+ },
167
+
168
+ "agent_listings": {
169
+ "type": "array",
170
+ "minItems": 1,
171
+ "maxItems": 6,
172
+ "description": "Agent's own recent listings to feature on the closing page. Operator paste-in (not the PropertyData sample). Empty array aborts the run.",
173
+ "items": {
174
+ "type": "object",
175
+ "required": ["address", "price", "status", "slug"],
176
+ "properties": {
177
+ "url": { "type": ["string", "null"], "description": "Source URL — listing card becomes an <a> when present." },
178
+ "address": { "type": "string" },
179
+ "price": { "type": "integer" },
180
+ "beds": { "type": ["integer", "null"] },
181
+ "type": { "type": ["string", "null"] },
182
+ "image": { "type": ["string", "null"], "description": "Path or URL to a tile photo for the card." },
183
+ "status": { "type": "string", "description": "'Sold' | 'Under offer' | 'For sale'." },
184
+ "slug": { "type": "string", "description": "Used to name the copied image file under img/." }
139
185
  }
140
186
  }
141
187
  }