@theihtisham/agent-shadow-brain 5.2.0 → 6.0.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 (301) hide show
  1. package/README.md +310 -1014
  2. package/dist/brain/agent-firewall.d.ts +7 -0
  3. package/dist/brain/agent-firewall.d.ts.map +1 -0
  4. package/dist/brain/agent-firewall.js +152 -0
  5. package/dist/brain/agent-firewall.js.map +1 -0
  6. package/dist/brain/agent-handoff.d.ts +19 -0
  7. package/dist/brain/agent-handoff.d.ts.map +1 -0
  8. package/dist/brain/agent-handoff.js +127 -0
  9. package/dist/brain/agent-handoff.js.map +1 -0
  10. package/dist/brain/air-gap.d.ts +23 -0
  11. package/dist/brain/air-gap.d.ts.map +1 -0
  12. package/dist/brain/air-gap.js +137 -0
  13. package/dist/brain/air-gap.js.map +1 -0
  14. package/dist/brain/attention-heatmap.d.ts +24 -0
  15. package/dist/brain/attention-heatmap.d.ts.map +1 -0
  16. package/dist/brain/attention-heatmap.js +108 -0
  17. package/dist/brain/attention-heatmap.js.map +1 -0
  18. package/dist/brain/auto-setup.js +19 -14
  19. package/dist/brain/auto-setup.js.map +1 -1
  20. package/dist/brain/auto-update.js +1 -1
  21. package/dist/brain/brain-3d.d.ts +79 -0
  22. package/dist/brain/brain-3d.d.ts.map +1 -0
  23. package/dist/brain/brain-3d.js +468 -0
  24. package/dist/brain/brain-3d.js.map +1 -0
  25. package/dist/brain/brain-active-learner.d.ts +74 -0
  26. package/dist/brain/brain-active-learner.d.ts.map +1 -0
  27. package/dist/brain/brain-active-learner.js +425 -0
  28. package/dist/brain/brain-active-learner.js.map +1 -0
  29. package/dist/brain/brain-ascii.d.ts +32 -0
  30. package/dist/brain/brain-ascii.d.ts.map +1 -0
  31. package/dist/brain/brain-ascii.js +402 -0
  32. package/dist/brain/brain-ascii.js.map +1 -0
  33. package/dist/brain/brain-chat.d.ts +42 -0
  34. package/dist/brain/brain-chat.d.ts.map +1 -0
  35. package/dist/brain/brain-chat.js +162 -0
  36. package/dist/brain/brain-chat.js.map +1 -0
  37. package/dist/brain/brain-cinema.d.ts +45 -0
  38. package/dist/brain/brain-cinema.d.ts.map +1 -0
  39. package/dist/brain/brain-cinema.js +393 -0
  40. package/dist/brain/brain-cinema.js.map +1 -0
  41. package/dist/brain/brain-coach.d.ts +45 -0
  42. package/dist/brain/brain-coach.d.ts.map +1 -0
  43. package/dist/brain/brain-coach.js +383 -0
  44. package/dist/brain/brain-coach.js.map +1 -0
  45. package/dist/brain/brain-consolidator.d.ts +56 -0
  46. package/dist/brain/brain-consolidator.d.ts.map +1 -0
  47. package/dist/brain/brain-consolidator.js +386 -0
  48. package/dist/brain/brain-consolidator.js.map +1 -0
  49. package/dist/brain/brain-contradictions.d.ts +57 -0
  50. package/dist/brain/brain-contradictions.d.ts.map +1 -0
  51. package/dist/brain/brain-contradictions.js +443 -0
  52. package/dist/brain/brain-contradictions.js.map +1 -0
  53. package/dist/brain/brain-diff.d.ts +61 -0
  54. package/dist/brain/brain-diff.d.ts.map +1 -0
  55. package/dist/brain/brain-diff.js +439 -0
  56. package/dist/brain/brain-diff.js.map +1 -0
  57. package/dist/brain/brain-dna.d.ts +59 -0
  58. package/dist/brain/brain-dna.d.ts.map +1 -0
  59. package/dist/brain/brain-dna.js +336 -0
  60. package/dist/brain/brain-dna.js.map +1 -0
  61. package/dist/brain/brain-doppelganger.d.ts +32 -0
  62. package/dist/brain/brain-doppelganger.d.ts.map +1 -0
  63. package/dist/brain/brain-doppelganger.js +417 -0
  64. package/dist/brain/brain-doppelganger.js.map +1 -0
  65. package/dist/brain/brain-encryption.d.ts +14 -0
  66. package/dist/brain/brain-encryption.d.ts.map +1 -0
  67. package/dist/brain/brain-encryption.js +68 -0
  68. package/dist/brain/brain-encryption.js.map +1 -0
  69. package/dist/brain/brain-exchange.d.ts +30 -0
  70. package/dist/brain/brain-exchange.d.ts.map +1 -0
  71. package/dist/brain/brain-exchange.js +106 -0
  72. package/dist/brain/brain-exchange.js.map +1 -0
  73. package/dist/brain/brain-garden.d.ts +18 -0
  74. package/dist/brain/brain-garden.d.ts.map +1 -0
  75. package/dist/brain/brain-garden.js +81 -0
  76. package/dist/brain/brain-garden.js.map +1 -0
  77. package/dist/brain/brain-graph-walker.d.ts +54 -0
  78. package/dist/brain/brain-graph-walker.d.ts.map +1 -0
  79. package/dist/brain/brain-graph-walker.js +412 -0
  80. package/dist/brain/brain-graph-walker.js.map +1 -0
  81. package/dist/brain/brain-holiday-card.d.ts +50 -0
  82. package/dist/brain/brain-holiday-card.d.ts.map +1 -0
  83. package/dist/brain/brain-holiday-card.js +432 -0
  84. package/dist/brain/brain-holiday-card.js.map +1 -0
  85. package/dist/brain/brain-memes.d.ts +44 -0
  86. package/dist/brain/brain-memes.d.ts.map +1 -0
  87. package/dist/brain/brain-memes.js +438 -0
  88. package/dist/brain/brain-memes.js.map +1 -0
  89. package/dist/brain/brain-memory-tiers.d.ts +67 -0
  90. package/dist/brain/brain-memory-tiers.d.ts.map +1 -0
  91. package/dist/brain/brain-memory-tiers.js +292 -0
  92. package/dist/brain/brain-memory-tiers.js.map +1 -0
  93. package/dist/brain/brain-notebook.d.ts +68 -0
  94. package/dist/brain/brain-notebook.d.ts.map +1 -0
  95. package/dist/brain/brain-notebook.js +379 -0
  96. package/dist/brain/brain-notebook.js.map +1 -0
  97. package/dist/brain/brain-portability.js +1 -1
  98. package/dist/brain/brain-quantize.d.ts +62 -0
  99. package/dist/brain/brain-quantize.d.ts.map +1 -0
  100. package/dist/brain/brain-quantize.js +346 -0
  101. package/dist/brain/brain-quantize.js.map +1 -0
  102. package/dist/brain/brain-query-expander.d.ts +31 -0
  103. package/dist/brain/brain-query-expander.d.ts.map +1 -0
  104. package/dist/brain/brain-query-expander.js +417 -0
  105. package/dist/brain/brain-query-expander.js.map +1 -0
  106. package/dist/brain/brain-quest.d.ts +58 -0
  107. package/dist/brain/brain-quest.d.ts.map +1 -0
  108. package/dist/brain/brain-quest.js +541 -0
  109. package/dist/brain/brain-quest.js.map +1 -0
  110. package/dist/brain/brain-recall-cache.d.ts +73 -0
  111. package/dist/brain/brain-recall-cache.d.ts.map +1 -0
  112. package/dist/brain/brain-recall-cache.js +444 -0
  113. package/dist/brain/brain-recall-cache.js.map +1 -0
  114. package/dist/brain/brain-reflex.d.ts +55 -0
  115. package/dist/brain/brain-reflex.d.ts.map +1 -0
  116. package/dist/brain/brain-reflex.js +364 -0
  117. package/dist/brain/brain-reflex.js.map +1 -0
  118. package/dist/brain/brain-replay.d.ts +74 -0
  119. package/dist/brain/brain-replay.d.ts.map +1 -0
  120. package/dist/brain/brain-replay.js +336 -0
  121. package/dist/brain/brain-replay.js.map +1 -0
  122. package/dist/brain/brain-rerank.d.ts +39 -0
  123. package/dist/brain/brain-rerank.d.ts.map +1 -0
  124. package/dist/brain/brain-rerank.js +216 -0
  125. package/dist/brain/brain-rerank.js.map +1 -0
  126. package/dist/brain/brain-roast.d.ts +44 -0
  127. package/dist/brain/brain-roast.d.ts.map +1 -0
  128. package/dist/brain/brain-roast.js +566 -0
  129. package/dist/brain/brain-roast.js.map +1 -0
  130. package/dist/brain/brain-sonification.d.ts +57 -0
  131. package/dist/brain/brain-sonification.d.ts.map +1 -0
  132. package/dist/brain/brain-sonification.js +286 -0
  133. package/dist/brain/brain-sonification.js.map +1 -0
  134. package/dist/brain/brain-tarot.d.ts +49 -0
  135. package/dist/brain/brain-tarot.d.ts.map +1 -0
  136. package/dist/brain/brain-tarot.js +634 -0
  137. package/dist/brain/brain-tarot.js.map +1 -0
  138. package/dist/brain/brain-time-capsule.d.ts +80 -0
  139. package/dist/brain/brain-time-capsule.d.ts.map +1 -0
  140. package/dist/brain/brain-time-capsule.js +289 -0
  141. package/dist/brain/brain-time-capsule.js.map +1 -0
  142. package/dist/brain/brain-trophies.d.ts +60 -0
  143. package/dist/brain/brain-trophies.d.ts.map +1 -0
  144. package/dist/brain/brain-trophies.js +431 -0
  145. package/dist/brain/brain-trophies.js.map +1 -0
  146. package/dist/brain/brain-working-memory.d.ts +78 -0
  147. package/dist/brain/brain-working-memory.d.ts.map +1 -0
  148. package/dist/brain/brain-working-memory.js +367 -0
  149. package/dist/brain/brain-working-memory.js.map +1 -0
  150. package/dist/brain/branch-brain.d.ts +23 -0
  151. package/dist/brain/branch-brain.d.ts.map +1 -0
  152. package/dist/brain/branch-brain.js +159 -0
  153. package/dist/brain/branch-brain.js.map +1 -0
  154. package/dist/brain/calibration-monitor.d.ts +20 -0
  155. package/dist/brain/calibration-monitor.d.ts.map +1 -0
  156. package/dist/brain/calibration-monitor.js +144 -0
  157. package/dist/brain/calibration-monitor.js.map +1 -0
  158. package/dist/brain/causal-chains.d.ts +37 -0
  159. package/dist/brain/causal-chains.d.ts.map +1 -0
  160. package/dist/brain/causal-chains.js +250 -0
  161. package/dist/brain/causal-chains.js.map +1 -0
  162. package/dist/brain/collision-detective.d.ts +32 -0
  163. package/dist/brain/collision-detective.d.ts.map +1 -0
  164. package/dist/brain/collision-detective.js +233 -0
  165. package/dist/brain/collision-detective.js.map +1 -0
  166. package/dist/brain/confidence-gate.d.ts +69 -0
  167. package/dist/brain/confidence-gate.d.ts.map +1 -0
  168. package/dist/brain/confidence-gate.js +244 -0
  169. package/dist/brain/confidence-gate.js.map +1 -0
  170. package/dist/brain/constitution.d.ts +59 -0
  171. package/dist/brain/constitution.d.ts.map +1 -0
  172. package/dist/brain/constitution.js +315 -0
  173. package/dist/brain/constitution.js.map +1 -0
  174. package/dist/brain/cost-aware-spawner.d.ts +7 -0
  175. package/dist/brain/cost-aware-spawner.d.ts.map +1 -0
  176. package/dist/brain/cost-aware-spawner.js +77 -0
  177. package/dist/brain/cost-aware-spawner.js.map +1 -0
  178. package/dist/brain/dream-engine.d.ts +35 -0
  179. package/dist/brain/dream-engine.d.ts.map +1 -0
  180. package/dist/brain/dream-engine.js +273 -0
  181. package/dist/brain/dream-engine.js.map +1 -0
  182. package/dist/brain/embeddings-v2.d.ts +58 -0
  183. package/dist/brain/embeddings-v2.d.ts.map +1 -0
  184. package/dist/brain/embeddings-v2.js +344 -0
  185. package/dist/brain/embeddings-v2.js.map +1 -0
  186. package/dist/brain/embeddings.d.ts +27 -0
  187. package/dist/brain/embeddings.d.ts.map +1 -0
  188. package/dist/brain/embeddings.js +175 -0
  189. package/dist/brain/embeddings.js.map +1 -0
  190. package/dist/brain/forgetting-curve.d.ts +21 -0
  191. package/dist/brain/forgetting-curve.d.ts.map +1 -0
  192. package/dist/brain/forgetting-curve.js +174 -0
  193. package/dist/brain/forgetting-curve.js.map +1 -0
  194. package/dist/brain/formal-verification-bridge.d.ts +26 -0
  195. package/dist/brain/formal-verification-bridge.d.ts.map +1 -0
  196. package/dist/brain/formal-verification-bridge.js +219 -0
  197. package/dist/brain/formal-verification-bridge.js.map +1 -0
  198. package/dist/brain/global-brain.d.ts +8 -1
  199. package/dist/brain/global-brain.d.ts.map +1 -1
  200. package/dist/brain/global-brain.js +24 -0
  201. package/dist/brain/global-brain.js.map +1 -1
  202. package/dist/brain/hallucination-quarantine.d.ts +26 -0
  203. package/dist/brain/hallucination-quarantine.d.ts.map +1 -0
  204. package/dist/brain/hallucination-quarantine.js +135 -0
  205. package/dist/brain/hallucination-quarantine.js.map +1 -0
  206. package/dist/brain/hive-accelerator.d.ts +39 -0
  207. package/dist/brain/hive-accelerator.d.ts.map +1 -0
  208. package/dist/brain/hive-accelerator.js +152 -0
  209. package/dist/brain/hive-accelerator.js.map +1 -0
  210. package/dist/brain/hive-voice.d.ts +71 -0
  211. package/dist/brain/hive-voice.d.ts.map +1 -0
  212. package/dist/brain/hive-voice.js +352 -0
  213. package/dist/brain/hive-voice.js.map +1 -0
  214. package/dist/brain/json-output.d.ts +26 -0
  215. package/dist/brain/json-output.d.ts.map +1 -0
  216. package/dist/brain/json-output.js +112 -0
  217. package/dist/brain/json-output.js.map +1 -0
  218. package/dist/brain/l0-cache.js +1 -1
  219. package/dist/brain/l0-cache.js.map +1 -1
  220. package/dist/brain/local-llm.d.ts +20 -0
  221. package/dist/brain/local-llm.d.ts.map +1 -0
  222. package/dist/brain/local-llm.js +132 -0
  223. package/dist/brain/local-llm.js.map +1 -0
  224. package/dist/brain/markdown-export.d.ts +48 -0
  225. package/dist/brain/markdown-export.d.ts.map +1 -0
  226. package/dist/brain/markdown-export.js +382 -0
  227. package/dist/brain/markdown-export.js.map +1 -0
  228. package/dist/brain/mcp-server.d.ts +40 -0
  229. package/dist/brain/mcp-server.d.ts.map +1 -1
  230. package/dist/brain/mcp-server.js +395 -12
  231. package/dist/brain/mcp-server.js.map +1 -1
  232. package/dist/brain/model-registry.d.ts +96 -0
  233. package/dist/brain/model-registry.d.ts.map +1 -0
  234. package/dist/brain/model-registry.js +585 -0
  235. package/dist/brain/model-registry.js.map +1 -0
  236. package/dist/brain/multimodal-vision.d.ts +38 -0
  237. package/dist/brain/multimodal-vision.d.ts.map +1 -0
  238. package/dist/brain/multimodal-vision.js +257 -0
  239. package/dist/brain/multimodal-vision.js.map +1 -0
  240. package/dist/brain/orchestrator.js +1 -1
  241. package/dist/brain/otel-lite.d.ts +71 -0
  242. package/dist/brain/otel-lite.d.ts.map +1 -0
  243. package/dist/brain/otel-lite.js +232 -0
  244. package/dist/brain/otel-lite.js.map +1 -0
  245. package/dist/brain/pr-auto-review.d.ts +17 -0
  246. package/dist/brain/pr-auto-review.d.ts.map +1 -0
  247. package/dist/brain/pr-auto-review.js +73 -0
  248. package/dist/brain/pr-auto-review.js.map +1 -0
  249. package/dist/brain/pre-mortem.d.ts +21 -0
  250. package/dist/brain/pre-mortem.d.ts.map +1 -0
  251. package/dist/brain/pre-mortem.js +210 -0
  252. package/dist/brain/pre-mortem.js.map +1 -0
  253. package/dist/brain/prompt-cache.d.ts +54 -0
  254. package/dist/brain/prompt-cache.d.ts.map +1 -0
  255. package/dist/brain/prompt-cache.js +105 -0
  256. package/dist/brain/prompt-cache.js.map +1 -0
  257. package/dist/brain/reputation-ledger.d.ts +42 -0
  258. package/dist/brain/reputation-ledger.d.ts.map +1 -0
  259. package/dist/brain/reputation-ledger.js +326 -0
  260. package/dist/brain/reputation-ledger.js.map +1 -0
  261. package/dist/brain/session-hooks.d.ts +19 -0
  262. package/dist/brain/session-hooks.d.ts.map +1 -1
  263. package/dist/brain/session-hooks.js +249 -18
  264. package/dist/brain/session-hooks.js.map +1 -1
  265. package/dist/brain/subagent-bridge.d.ts +46 -0
  266. package/dist/brain/subagent-bridge.d.ts.map +1 -0
  267. package/dist/brain/subagent-bridge.js +364 -0
  268. package/dist/brain/subagent-bridge.js.map +1 -0
  269. package/dist/brain/swarm-debate.d.ts +22 -0
  270. package/dist/brain/swarm-debate.d.ts.map +1 -0
  271. package/dist/brain/swarm-debate.js +134 -0
  272. package/dist/brain/swarm-debate.js.map +1 -0
  273. package/dist/brain/team-brain-sync.d.ts +22 -0
  274. package/dist/brain/team-brain-sync.d.ts.map +1 -0
  275. package/dist/brain/team-brain-sync.js +132 -0
  276. package/dist/brain/team-brain-sync.js.map +1 -0
  277. package/dist/brain/token-economy.d.ts +25 -0
  278. package/dist/brain/token-economy.d.ts.map +1 -0
  279. package/dist/brain/token-economy.js +216 -0
  280. package/dist/brain/token-economy.js.map +1 -0
  281. package/dist/brain/voice-mode.d.ts +15 -0
  282. package/dist/brain/voice-mode.d.ts.map +1 -0
  283. package/dist/brain/voice-mode.js +97 -0
  284. package/dist/brain/voice-mode.js.map +1 -0
  285. package/dist/cli.js +1702 -4
  286. package/dist/cli.js.map +1 -1
  287. package/dist/dashboard/server.d.ts +6 -0
  288. package/dist/dashboard/server.d.ts.map +1 -1
  289. package/dist/dashboard/server.js +1118 -368
  290. package/dist/dashboard/server.js.map +1 -1
  291. package/dist/dashboard/v6-dashboard-html.d.ts +2 -0
  292. package/dist/dashboard/v6-dashboard-html.d.ts.map +1 -0
  293. package/dist/dashboard/v6-dashboard-html.js +2474 -0
  294. package/dist/dashboard/v6-dashboard-html.js.map +1 -0
  295. package/dist/index.d.ts +27 -0
  296. package/dist/index.d.ts.map +1 -1
  297. package/dist/index.js +29 -1
  298. package/dist/index.js.map +1 -1
  299. package/dist/types.d.ts +518 -2
  300. package/dist/types.d.ts.map +1 -1
  301. package/package.json +33 -4
@@ -0,0 +1,634 @@
1
+ // src/brain/brain-tarot.ts — Brain Tarot (viral feature)
2
+ // v6.0.2
3
+ //
4
+ // Tongue-in-cheek mystic interface. Reads git + brain state, then performs a
5
+ // deterministic "tarot reading" against a 78-card deck. Same project + same
6
+ // day + same question = same cards, every time.
7
+ //
8
+ // Pure-stdlib, zero new npm deps. Uses node:crypto for the deterministic draw.
9
+ import * as path from 'path';
10
+ import * as crypto from 'crypto';
11
+ import { GlobalBrain, getGlobalBrain } from './global-brain.js';
12
+ import { getBrainReplay } from './brain-replay.js';
13
+ // ── Deck (78 cards) ──────────────────────────────────────────────────────────
14
+ const MAJOR_ARCANA = [
15
+ { name: 'The Fool', suit: 'major', source: 'major', brainMeaning: 'The first commit. A blank slate, hands trembling at `git init`. {{evidence}}' },
16
+ { name: 'The Magician', suit: 'major', source: 'major', brainMeaning: 'The first refactor that worked. The moment the brain trusted its own recall. {{evidence}}' },
17
+ { name: 'The High Priestess', suit: 'major', source: 'major', brainMeaning: 'The hidden knowledge in your README — read by none, blessed by all. {{evidence}}' },
18
+ { name: 'The Empress', suit: 'major', source: 'major', brainMeaning: 'A fertile season of feature work. New routes bloom. {{evidence}}' },
19
+ { name: 'The Emperor', suit: 'major', source: 'major', brainMeaning: 'The architecture decision that ruled all others. Iron-fisted. Rarely reversed. {{evidence}}' },
20
+ { name: 'The Hierophant', suit: 'major', source: 'major', brainMeaning: 'Convention. Style guides. The senior who said "we always do it this way". {{evidence}}' },
21
+ { name: 'The Lovers', suit: 'major', source: 'major', brainMeaning: 'A decision between two libraries. Both were right. Both were wrong. {{evidence}}' },
22
+ { name: 'The Chariot', suit: 'major', source: 'major', brainMeaning: 'A sprint pulled to victory by sheer force of will. The build passed at 4:47am. {{evidence}}' },
23
+ { name: 'Strength', suit: 'major', source: 'major', brainMeaning: 'The bug that returned ten times and was tamed only on the eleventh. {{evidence}}' },
24
+ { name: 'The Hermit', suit: 'major', source: 'major', brainMeaning: 'A long, quiet refactor. No commits. Only thought. {{evidence}}' },
25
+ { name: 'Wheel of Fortune', suit: 'major', source: 'major', brainMeaning: 'The release. The deploy. The unpredictable spin of CI. {{evidence}}' },
26
+ { name: 'Justice', suit: 'major', source: 'major', brainMeaning: 'Code review. The merciless eye. The reversible verdict. {{evidence}}' },
27
+ { name: 'The Hanged Man', suit: 'major', source: 'major', brainMeaning: 'A pull request that has waited too long. Sacrifice yields perspective. {{evidence}}' },
28
+ { name: 'Death', suit: 'major', source: 'major', brainMeaning: 'A deleted module. A mercy killing. The repository breathes again. {{evidence}}' },
29
+ { name: 'Temperance', suit: 'major', source: 'major', brainMeaning: 'Balance between speed and care. The cache hit rate finds its sweet spot. {{evidence}}' },
30
+ { name: 'The Devil', suit: 'major', source: 'major', brainMeaning: 'Tech debt. A pact signed in haste. Interest compounding nightly. {{evidence}}' },
31
+ { name: 'The Tower', suit: 'major', source: 'major', brainMeaning: 'The migration. The breaking change. Lightning strikes the foundation. {{evidence}}' },
32
+ { name: 'The Star', suit: 'major', source: 'major', brainMeaning: 'Hope returns. The new abstraction feels right. The tests are green. {{evidence}}' },
33
+ { name: 'The Moon', suit: 'major', source: 'major', brainMeaning: 'A heisenbug. A flake. Something only reproducible at midnight. {{evidence}}' },
34
+ { name: 'The Sun', suit: 'major', source: 'major', brainMeaning: 'A clear, simple solution. Joy in code. Worth screenshotting. {{evidence}}' },
35
+ { name: 'Judgement', suit: 'major', source: 'major', brainMeaning: 'A retrospective. Old patterns rise to be reckoned with. {{evidence}}' },
36
+ { name: 'The World', suit: 'major', source: 'major', brainMeaning: 'A complete cycle. v1.0.0 ships. The brain has lived a life. {{evidence}}' },
37
+ ];
38
+ const SUITS = [
39
+ { suit: 'wands', theme: 'features and creative work' },
40
+ { suit: 'cups', theme: 'memories, relationships, emotions' },
41
+ { suit: 'swords', theme: 'bugs, conflict, intellect' },
42
+ { suit: 'pentacles', theme: 'tests, infrastructure, the material' },
43
+ ];
44
+ const COURT = ['Page', 'Knight', 'Queen', 'King'];
45
+ const RANKS = ['Ace', 'Two', 'Three', 'Four', 'Five', 'Six', 'Seven', 'Eight', 'Nine', 'Ten'];
46
+ function buildMinorMeaning(suit, rank) {
47
+ const isAce = rank === 'Ace';
48
+ const isCourt = COURT.includes(rank);
49
+ const suffix = '{{evidence}}';
50
+ if (isAce) {
51
+ if (suit === 'wands')
52
+ return `Ace of Wands — a feature begins. A first sketch. The seed of a route, untested. ${suffix}`;
53
+ if (suit === 'cups')
54
+ return `Ace of Cups — a memory worth keeping. The brain remembered something it had not been asked to. ${suffix}`;
55
+ if (suit === 'swords')
56
+ return `Ace of Swords — a bug named. The first clarity in a storm. ${suffix}`;
57
+ return `Ace of Pentacles — a test passes for the first time. A foundation is laid. ${suffix}`;
58
+ }
59
+ if (isCourt) {
60
+ if (suit === 'wands')
61
+ return `${rank} of Wands — a builder of features. ${rank === 'King' ? 'A mature owner of the roadmap.' : 'A restless creator.'} ${suffix}`;
62
+ if (suit === 'cups')
63
+ return `${rank} of Cups — a keeper of memory. ${rank === 'Queen' ? 'Empathic, recalls every quiet pattern.' : 'Sensitive to the brain\'s moods.'} ${suffix}`;
64
+ if (suit === 'swords')
65
+ return `${rank} of Swords — a hunter of bugs. ${rank === 'Knight' ? 'Charges in headfirst. Sometimes wins.' : 'Sharpens thought into action.'} ${suffix}`;
66
+ return `${rank} of Pentacles — a steward of tests and infra. ${rank === 'King' ? 'Owns the deploy pipeline.' : 'Quietly tends the foundation.'} ${suffix}`;
67
+ }
68
+ // Numbered 2-10
69
+ const tone = {
70
+ wands: 'feature momentum',
71
+ cups: 'memory and meaning',
72
+ swords: 'conflict and clarity',
73
+ pentacles: 'infrastructure and craft',
74
+ major: '',
75
+ };
76
+ const phases = {
77
+ Two: 'a partnership forms — two callers agree',
78
+ Three: 'collaboration: the first three contributors converge',
79
+ Four: 'a moment of rest, a cache warms',
80
+ Five: 'a setback, a regression, a difficult code review',
81
+ Six: 'recovery, a return to harmony with the linter',
82
+ Seven: 'a difficult choice, multiple branches diverge',
83
+ Eight: 'mastery — the pattern has been seen many times',
84
+ Nine: 'near-completion, a feature is almost shipped',
85
+ Ten: 'completion, then weight — success has consequences',
86
+ };
87
+ return `${rank} of ${capitalize(suit)} — ${phases[rank] ?? 'a quiet turn'} in ${tone[suit]}. ${suffix}`;
88
+ }
89
+ function buildDeck() {
90
+ const minor = [];
91
+ for (const { suit } of SUITS) {
92
+ for (const rank of [...RANKS, ...COURT]) {
93
+ const fullName = `${rank} of ${capitalize(suit)}`;
94
+ minor.push({
95
+ name: fullName,
96
+ suit,
97
+ source: 'minor',
98
+ brainMeaning: buildMinorMeaning(suit, rank),
99
+ });
100
+ }
101
+ }
102
+ return [...MAJOR_ARCANA, ...minor];
103
+ }
104
+ const DECK = buildDeck();
105
+ // ── Public deck access ───────────────────────────────────────────────────────
106
+ export function cardDeck() {
107
+ return DECK.map(c => ({
108
+ name: c.name,
109
+ position: '',
110
+ meaning: c.brainMeaning,
111
+ source: c.source,
112
+ orientation: 'upright',
113
+ associatedEvidence: [],
114
+ }));
115
+ }
116
+ // ── Class ────────────────────────────────────────────────────────────────────
117
+ export class BrainTarot {
118
+ constructor(brain, replay) {
119
+ this.brain = brain ?? getGlobalBrain();
120
+ this.replay = replay ?? getBrainReplay();
121
+ }
122
+ async draw(project, opts = {}) {
123
+ const spread = opts.spread ?? 'one-card';
124
+ const question = opts.question ?? '';
125
+ const drawnAt = new Date().toISOString().slice(0, 10);
126
+ const signals = this.gatherSignals(project);
127
+ // Empty-state graceful path.
128
+ if (!signals.hasData) {
129
+ const empty = 'The cards are silent. You have no past for them to read.';
130
+ const card = {
131
+ name: 'No Card Drawn',
132
+ position: 'silence',
133
+ meaning: empty,
134
+ source: 'major',
135
+ orientation: 'upright',
136
+ associatedEvidence: [],
137
+ };
138
+ return {
139
+ spread,
140
+ drawnAt,
141
+ cards: [card],
142
+ synthesis: empty,
143
+ lucky_file: 'README.md',
144
+ advice: 'Make a memory. Then return.',
145
+ svg: this.renderReadingCard(project, [card], empty, 'README.md', spread, question),
146
+ };
147
+ }
148
+ const count = spread === 'one-card' ? 1 : spread === 'three-card' ? 3 : 10;
149
+ const positions = positionsFor(spread);
150
+ const drawn = this.deterministicDraw(project, drawnAt, question, count);
151
+ const cards = drawn.map((deckCard, i) => this.materialize(deckCard, positions[i] ?? `position-${i + 1}`, signals));
152
+ const synthesis = this.synthesize(cards, signals, question);
153
+ const lucky_file = signals.hotFile ?? signals.topEntity ?? 'src/index.ts';
154
+ const advice = this.advise(cards, signals);
155
+ return {
156
+ spread,
157
+ drawnAt,
158
+ cards,
159
+ synthesis,
160
+ lucky_file,
161
+ advice,
162
+ svg: this.renderReadingCard(project, cards, synthesis, lucky_file, spread, question),
163
+ };
164
+ }
165
+ // ── Signals ─────────────────────────────────────────────────────────────────
166
+ gatherSignals(project) {
167
+ let entries = [];
168
+ let events = [];
169
+ try {
170
+ const projectId = GlobalBrain.projectIdFor(project);
171
+ entries = this.brain.recall({ projectId, limit: 5000 });
172
+ }
173
+ catch { /* empty */ }
174
+ try {
175
+ events = this.replay.listEvents(project);
176
+ }
177
+ catch { /* empty */ }
178
+ const agentCounts = new Map();
179
+ const entitySet = new Map();
180
+ const fileEdits = new Map();
181
+ let collisions = 0;
182
+ let decisions = 0;
183
+ let hallucinations = 0;
184
+ let bugs = 0, features = 0, tests = 0, refactors = 0;
185
+ let firstSeen = Number.POSITIVE_INFINITY;
186
+ let lastSeen = 0;
187
+ let firstEvent;
188
+ let firstRefactor;
189
+ let recentDelete;
190
+ const sortedEvents = [...events].sort((a, b) => a.ts - b.ts);
191
+ for (const ev of sortedEvents) {
192
+ if (!firstEvent)
193
+ firstEvent = ev;
194
+ if (Number.isFinite(ev.ts)) {
195
+ if (ev.ts < firstSeen)
196
+ firstSeen = ev.ts;
197
+ if (ev.ts > lastSeen)
198
+ lastSeen = ev.ts;
199
+ }
200
+ if (ev.agent)
201
+ agentCounts.set(ev.agent, (agentCounts.get(ev.agent) ?? 0) + 1);
202
+ const payload = (ev.payload ?? {});
203
+ const entity = (payload.entity ?? payload.id);
204
+ if (entity)
205
+ entitySet.set(entity, (entitySet.get(entity) ?? 0) + 1);
206
+ const file = (payload.file ?? payload.path);
207
+ if (file)
208
+ fileEdits.set(file, (fileEdits.get(file) ?? 0) + 1);
209
+ if (ev.type.includes('refactor') && !firstRefactor)
210
+ firstRefactor = ev;
211
+ if (ev.type.includes('delete') || ev.type.includes('remove'))
212
+ recentDelete = ev;
213
+ if (ev.type.includes('collision'))
214
+ collisions += 1;
215
+ if (ev.type.includes('decision') || ev.type.includes('adr'))
216
+ decisions += 1;
217
+ if (ev.type.includes('hallucination') || ev.type.includes('quarantine'))
218
+ hallucinations += 1;
219
+ }
220
+ for (const e of entries) {
221
+ const t = e.createdAt instanceof Date ? e.createdAt.getTime() : Number(e.createdAt);
222
+ if (Number.isFinite(t)) {
223
+ if (t < firstSeen)
224
+ firstSeen = t;
225
+ if (t > lastSeen)
226
+ lastSeen = t;
227
+ }
228
+ agentCounts.set(e.agentTool, (agentCounts.get(e.agentTool) ?? 0) + 1);
229
+ entitySet.set(e.id, (entitySet.get(e.id) ?? 0) + 1);
230
+ const meta = (e.metadata ?? {});
231
+ const file = (meta.file ?? meta.path);
232
+ if (file)
233
+ fileEdits.set(file, (fileEdits.get(file) ?? 0) + 1);
234
+ const cat = e.category.toLowerCase();
235
+ if (/bug|error|crash|fix/.test(cat))
236
+ bugs += 1;
237
+ if (/feature|route|endpoint|component/.test(cat))
238
+ features += 1;
239
+ if (/test|spec/.test(cat))
240
+ tests += 1;
241
+ if (/refactor|cleanup|rename/.test(cat))
242
+ refactors += 1;
243
+ if (/decision|adr/.test(cat))
244
+ decisions += 1;
245
+ if (/hallucin/.test(cat))
246
+ hallucinations += 1;
247
+ }
248
+ const hasData = entries.length > 0 || events.length > 0;
249
+ const ageDays = hasData && Number.isFinite(firstSeen)
250
+ ? Math.max(0, (lastSeen - firstSeen) / (24 * 60 * 60 * 1000))
251
+ : 0;
252
+ const topAgentEntry = Array.from(agentCounts.entries()).sort((a, b) => b[1] - a[1])[0];
253
+ const topEntityEntry = Array.from(entitySet.entries()).sort((a, b) => b[1] - a[1])[0];
254
+ const hotFileEntry = Array.from(fileEdits.entries()).sort((a, b) => b[1] - a[1])[0];
255
+ return {
256
+ firstEvent,
257
+ firstRefactor,
258
+ recentDelete,
259
+ topEntity: topEntityEntry?.[0],
260
+ topAgent: topAgentEntry?.[0] ?? 'unknown',
261
+ agentVariety: agentCounts.size,
262
+ collisions,
263
+ decisions,
264
+ hallucinations,
265
+ totalMemories: entries.length,
266
+ ageDays: Math.round(ageDays * 10) / 10,
267
+ hotFile: hotFileEntry?.[0],
268
+ bugs,
269
+ features,
270
+ tests,
271
+ refactors,
272
+ hasData,
273
+ };
274
+ }
275
+ // ── Drawing ─────────────────────────────────────────────────────────────────
276
+ deterministicDraw(project, day, question, count) {
277
+ const seedInput = `${path.resolve(project)}::${day}::${question}`;
278
+ const seedHash = crypto.createHash('sha256').update(seedInput).digest();
279
+ // Fisher-Yates shuffle driven by the hash stream (extend hash as needed).
280
+ const deck = [...DECK];
281
+ const stream = this.hashStream(seedHash);
282
+ for (let i = deck.length - 1; i > 0; i--) {
283
+ const j = stream(i + 1);
284
+ const tmp = deck[i];
285
+ deck[i] = deck[j];
286
+ deck[j] = tmp;
287
+ }
288
+ return deck.slice(0, count);
289
+ }
290
+ hashStream(seed) {
291
+ let pool = Buffer.from(seed);
292
+ let offset = 0;
293
+ return (mod) => {
294
+ if (offset + 4 > pool.length) {
295
+ pool = crypto.createHash('sha256').update(pool).digest();
296
+ offset = 0;
297
+ }
298
+ const n = pool.readUInt32BE(offset);
299
+ offset += 4;
300
+ return n % mod;
301
+ };
302
+ }
303
+ materialize(deckCard, position, signals) {
304
+ // Determine orientation from a second hash so it varies with the same daily seed.
305
+ const orientationHash = crypto.createHash('sha256').update(deckCard.name + position).digest()[0];
306
+ const orientation = orientationHash % 3 === 0 ? 'reversed' : 'upright';
307
+ const evidence = this.evidenceFor(deckCard, signals);
308
+ const meaningBase = deckCard.brainMeaning.replace('{{evidence}}', evidence.summary || '');
309
+ const meaning = orientation === 'reversed'
310
+ ? `Reversed — ${meaningBase} The current is running the other way.`
311
+ : meaningBase;
312
+ return {
313
+ name: deckCard.name,
314
+ position,
315
+ meaning: meaning.trim(),
316
+ source: deckCard.source,
317
+ orientation,
318
+ associatedEvidence: evidence.items,
319
+ };
320
+ }
321
+ evidenceFor(card, s) {
322
+ const items = [];
323
+ let summary = '';
324
+ if (card.source === 'major') {
325
+ switch (card.name) {
326
+ case 'The Fool':
327
+ if (s.firstEvent) {
328
+ summary = `Your first event: ${s.firstEvent.type} on ${new Date(s.firstEvent.ts).toISOString().slice(0, 10)}.`;
329
+ items.push(`first-event:${s.firstEvent.type}`);
330
+ }
331
+ break;
332
+ case 'The Magician':
333
+ if (s.firstRefactor) {
334
+ summary = `Your first refactor: ${s.firstRefactor.type}.`;
335
+ items.push(`first-refactor:${s.firstRefactor.type}`);
336
+ }
337
+ break;
338
+ case 'Death':
339
+ if (s.recentDelete) {
340
+ summary = `A recent removal: ${s.recentDelete.type}.`;
341
+ items.push(`delete:${s.recentDelete.type}`);
342
+ }
343
+ break;
344
+ case 'The Tower':
345
+ if (s.collisions > 0) {
346
+ summary = `Your last ${s.collisions} collisions foreshadowed migrations.`;
347
+ items.push(`collisions:${s.collisions}`);
348
+ }
349
+ break;
350
+ case 'The Moon':
351
+ if (s.hallucinations > 0) {
352
+ summary = `Your ${s.hallucinations} hallucinations whisper from the dark.`;
353
+ items.push(`hallucinations:${s.hallucinations}`);
354
+ }
355
+ break;
356
+ case 'The Emperor':
357
+ if (s.decisions > 0) {
358
+ summary = `${s.decisions} architectural decrees stand in the record.`;
359
+ items.push(`decisions:${s.decisions}`);
360
+ }
361
+ break;
362
+ case 'The World':
363
+ summary = `A brain of ${s.ageDays.toFixed(1)} days and ${s.totalMemories} memories has lived a small life.`;
364
+ items.push(`age:${s.ageDays}`);
365
+ break;
366
+ default:
367
+ break;
368
+ }
369
+ }
370
+ else {
371
+ switch (card.suit) {
372
+ case 'wands':
373
+ if (s.features > 0) {
374
+ summary = `You have ${s.features} feature-coded memories.`;
375
+ items.push(`features:${s.features}`);
376
+ }
377
+ break;
378
+ case 'cups':
379
+ if (s.topEntity) {
380
+ summary = `Your most-recalled entity: ${s.topEntity}.`;
381
+ items.push(`top-entity:${s.topEntity}`);
382
+ }
383
+ break;
384
+ case 'swords':
385
+ if (s.bugs > 0) {
386
+ summary = `You have ${s.bugs} bug-coded memories.`;
387
+ items.push(`bugs:${s.bugs}`);
388
+ }
389
+ break;
390
+ case 'pentacles':
391
+ if (s.tests > 0) {
392
+ summary = `${s.tests} test-related memories anchor your foundation.`;
393
+ items.push(`tests:${s.tests}`);
394
+ }
395
+ break;
396
+ }
397
+ }
398
+ return { summary, items };
399
+ }
400
+ // ── Synthesis ───────────────────────────────────────────────────────────────
401
+ synthesize(cards, s, question) {
402
+ const names = cards.map(c => `${c.name}${c.orientation === 'reversed' ? ' (reversed)' : ''}`).join(', ');
403
+ const intro = question
404
+ ? `You asked of "${question}". The brain whispers an answer through ${names}.`
405
+ : `The brain whispers of an unfinished journey through ${names}.`;
406
+ const dominant = cards.find(c => c.source === 'major') ?? cards[0];
407
+ const tone = dominant.orientation === 'reversed'
408
+ ? 'The current runs against you. The lesson is in the friction.'
409
+ : 'The currents are aligned. Move with deliberation.';
410
+ const personalized = s.collisions > 5
411
+ ? `Beware — ${s.collisions} collisions linger in the record. The Tower has not yet finished its work.`
412
+ : s.hallucinations > 0
413
+ ? `The Moon\'s veil is thin: ${s.hallucinations} hallucinations have been quarantined and still murmur.`
414
+ : s.decisions > 5
415
+ ? `The Emperor has spoken ${s.decisions} times in this codebase. Listen for which voice now leads.`
416
+ : `Your ${s.totalMemories} memories are a quiet weight. They will move when you do.`;
417
+ return `${intro} ${tone} ${personalized}`;
418
+ }
419
+ advise(cards, s) {
420
+ const reversedCount = cards.filter(c => c.orientation === 'reversed').length;
421
+ if (reversedCount > cards.length / 2) {
422
+ return 'Pause. Read your most recent decision aloud. The brain wants you to disagree with it before you proceed.';
423
+ }
424
+ if (cards.some(c => c.name === 'The Tower')) {
425
+ return 'Migrate intentionally, not reactively. Branch first. Confess to the changelog second.';
426
+ }
427
+ if (cards.some(c => c.name === 'Death')) {
428
+ return 'Delete the module you have been avoiding. The brain will thank you within three commits.';
429
+ }
430
+ if (s.bugs > s.tests) {
431
+ return 'Write one test for the bug you fear most. The Pentacles want their due.';
432
+ }
433
+ return 'Trust the smallest pattern that has worked twice. Repeat it. Name it. Ship it.';
434
+ }
435
+ // ── SVG card ────────────────────────────────────────────────────────────────
436
+ renderReadingCard(project, cards, synthesis, luckyFile, spread, question) {
437
+ const W = 1200, H = 1600, P = 60;
438
+ const projName = path.basename(path.resolve(project)) || 'unknown-brain';
439
+ const title = spread === 'one-card' ? 'A SINGLE CARD' : spread === 'three-card' ? 'PAST · PRESENT · FUTURE' : 'THE CELTIC CROSS';
440
+ // Layout cards: one (center), three (row), celtic-cross (cross + staff).
441
+ const cardSvgs = spread === 'celtic-cross'
442
+ ? this.layoutCeltic(cards, W, 220)
443
+ : spread === 'three-card'
444
+ ? this.layoutThree(cards, W, 280)
445
+ : this.layoutOne(cards, W, 320);
446
+ const synthWrapped = wrapText(synthesis, 64, 6);
447
+ const synthY = 1100;
448
+ const synthSvg = synthWrapped.map((ln, i) => `<text x="${P}" y="${synthY + i * 32}" fill="#e6d9ff" font-size="22" font-family="ui-serif,Georgia,serif" opacity="0.95">${esc(ln)}</text>`).join('\n ');
449
+ const questionLine = question
450
+ ? `<text x="${P}" y="180" fill="#d4af37" font-size="18" font-family="ui-serif,Georgia,serif" font-style="italic" opacity="0.9">"${esc(question)}"</text>`
451
+ : '';
452
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}">
453
+ <!-- BRAIN TAROT · v6.0.2 -->
454
+ <defs>
455
+ <linearGradient id="t-bg" x1="0" y1="0" x2="0" y2="1">
456
+ <stop offset="0" stop-color="#15052a"/>
457
+ <stop offset="0.5" stop-color="#240b3e"/>
458
+ <stop offset="1" stop-color="#0a0218"/>
459
+ </linearGradient>
460
+ <radialGradient id="t-halo" cx="0.5" cy="0.35" r="0.6">
461
+ <stop offset="0" stop-color="#d4af37" stop-opacity="0.22"/>
462
+ <stop offset="1" stop-color="#d4af37" stop-opacity="0"/>
463
+ </radialGradient>
464
+ <linearGradient id="t-card" x1="0" y1="0" x2="0" y2="1">
465
+ <stop offset="0" stop-color="#3a1d5e"/>
466
+ <stop offset="1" stop-color="#1c0838"/>
467
+ </linearGradient>
468
+ <linearGradient id="t-gold" x1="0" y1="0" x2="1" y2="0">
469
+ <stop offset="0" stop-color="#d4af37"/>
470
+ <stop offset="1" stop-color="#fff1a8"/>
471
+ </linearGradient>
472
+ </defs>
473
+ <rect width="${W}" height="${H}" fill="url(#t-bg)"/>
474
+ <rect width="${W}" height="${H}" fill="url(#t-halo)"/>
475
+ <text x="${P}" y="92" fill="#d4af37" font-size="14" font-family="ui-monospace,monospace" letter-spacing="4">BRAIN TAROT · v6.0.2</text>
476
+ <text x="${P}" y="140" fill="url(#t-gold)" font-size="44" font-family="ui-serif,Georgia,serif" font-weight="700">${esc(projName)}</text>
477
+ ${questionLine}
478
+ <text x="${W - P}" y="92" fill="#d4af37" font-size="14" font-family="ui-monospace,monospace" text-anchor="end" letter-spacing="3">${esc(title)}</text>
479
+ ${cardSvgs}
480
+ ${synthSvg}
481
+ <text x="${P}" y="${H - 130}" fill="#d4af37" font-size="14" font-family="ui-monospace,monospace" opacity="0.85" letter-spacing="2">LUCKY FILE</text>
482
+ <text x="${P}" y="${H - 100}" fill="#fff1a8" font-size="22" font-family="ui-monospace,monospace">${esc(luckyFile)}</text>
483
+ <text x="${P}" y="${H - 56}" fill="#d4af37" font-size="13" font-family="ui-monospace,monospace" opacity="0.8">Powered by @shadow-brain</text>
484
+ <text x="${P}" y="${H - 32}" fill="#e6d9ff" font-size="14" font-family="ui-monospace,monospace">npx @theihtisham/agent-shadow-brain tarot</text>
485
+ </svg>`;
486
+ }
487
+ layoutOne(cards, W, top) {
488
+ const card = cards[0];
489
+ if (!card)
490
+ return '';
491
+ const cw = 360, ch = 540;
492
+ const x = (W - cw) / 2;
493
+ return this.drawCard(card, x, top, cw, ch, 1.0);
494
+ }
495
+ layoutThree(cards, W, top) {
496
+ const cw = 280, ch = 420;
497
+ const gap = 60;
498
+ const totalW = cw * 3 + gap * 2;
499
+ const startX = (W - totalW) / 2;
500
+ return cards.slice(0, 3).map((c, i) => this.drawCard(c, startX + i * (cw + gap), top, cw, ch, 1.0)).join('\n');
501
+ }
502
+ layoutCeltic(cards, W, top) {
503
+ // 10-card Celtic cross: 1 center, 2 across center, 3 below, 4 above, 5 left,
504
+ // 6 right, 7-10 vertical staff on the right.
505
+ const cw = 160, ch = 240;
506
+ const crossCx = W / 2 - 140;
507
+ const crossCy = top + 280;
508
+ const places = [
509
+ { x: crossCx - cw / 2, y: crossCy - ch / 2 }, // 1 center
510
+ { x: crossCx - cw / 2, y: crossCy - ch / 2, rot: 90 }, // 2 across
511
+ { x: crossCx - cw / 2, y: crossCy + ch / 2 + 12 }, // 3 below
512
+ { x: crossCx - cw / 2, y: crossCy - ch * 1.5 - 12 }, // 4 above
513
+ { x: crossCx - cw * 1.5 - 12, y: crossCy - ch / 2 }, // 5 left
514
+ { x: crossCx + cw / 2 + 12, y: crossCy - ch / 2 }, // 6 right
515
+ { x: W - cw - 80, y: top + 80 }, // 7 staff
516
+ { x: W - cw - 80, y: top + 80 + ch + 12 }, // 8 staff
517
+ { x: W - cw - 80, y: top + 80 + (ch + 12) * 2 }, // 9 staff
518
+ { x: W - cw - 80, y: top + 80 + (ch + 12) * 3 }, // 10 staff
519
+ ];
520
+ return cards.slice(0, 10).map((c, i) => {
521
+ const p = places[i];
522
+ return this.drawCard(c, p.x, p.y, cw, ch, 0.75, p.rot ?? 0);
523
+ }).join('\n');
524
+ }
525
+ drawCard(card, x, y, w, h, scale, rotate = 0) {
526
+ const transform = rotate
527
+ ? `transform="translate(${x + w / 2}, ${y + h / 2}) rotate(${rotate}) translate(${-w / 2}, ${-h / 2})"`
528
+ : `transform="translate(${x}, ${y})"`;
529
+ const flip = card.orientation === 'reversed' ? `transform="translate(${w / 2}, ${h / 2}) rotate(180) translate(${-w / 2}, ${-h / 2})"` : '';
530
+ const nameFont = Math.round(18 * scale);
531
+ const positionFont = Math.round(11 * scale);
532
+ const glyph = brainGlyph(card);
533
+ return `<g ${transform}>
534
+ <rect x="0" y="0" width="${w}" height="${h}" rx="${10 * scale}" ry="${10 * scale}" fill="url(#t-card)" stroke="#d4af37" stroke-width="${1.5 * scale}"/>
535
+ <rect x="${6 * scale}" y="${6 * scale}" width="${w - 12 * scale}" height="${h - 12 * scale}" rx="${6 * scale}" ry="${6 * scale}" fill="none" stroke="#d4af37" stroke-width="${0.6 * scale}" opacity="0.55"/>
536
+ <g ${flip}>
537
+ <text x="${w / 2}" y="${28 * scale}" fill="#d4af37" font-size="${positionFont}" font-family="ui-monospace,monospace" text-anchor="middle" letter-spacing="2" opacity="0.85">${esc((card.position || ' ').toUpperCase())}</text>
538
+ <g transform="translate(${w / 2}, ${h / 2}) scale(${scale})">${glyph}</g>
539
+ <text x="${w / 2}" y="${h - 18 * scale}" fill="#fff1a8" font-size="${nameFont}" font-family="ui-serif,Georgia,serif" text-anchor="middle">${esc(card.name)}</text>
540
+ </g>
541
+ </g>`;
542
+ }
543
+ }
544
+ // ── Helpers ──────────────────────────────────────────────────────────────────
545
+ function positionsFor(spread) {
546
+ switch (spread) {
547
+ case 'one-card': return ['the moment'];
548
+ case 'three-card': return ['past', 'present', 'future'];
549
+ case 'celtic-cross': return ['present', 'challenge', 'foundation', 'recent past', 'crown', 'near future', 'self', 'environment', 'hopes & fears', 'outcome'];
550
+ }
551
+ }
552
+ function brainGlyph(card) {
553
+ // Brain-inspired iconography replacing traditional tarot art. Pure SVG paths.
554
+ // Each suit has a distinct glyph; major arcana share a neural sigil.
555
+ const stroke = '#d4af37';
556
+ if (card.source === 'major') {
557
+ return `<g stroke="${stroke}" stroke-width="1.6" fill="none" opacity="0.9">
558
+ <circle cx="0" cy="0" r="38"/>
559
+ <circle cx="0" cy="0" r="22"/>
560
+ <path d="M -38 0 Q -10 -28 0 0 Q 10 28 38 0"/>
561
+ <path d="M 0 -38 Q -28 -10 0 0 Q 28 10 0 38"/>
562
+ <circle cx="0" cy="0" r="4" fill="${stroke}"/>
563
+ </g>`;
564
+ }
565
+ // Determine suit from card.name suffix.
566
+ if (/Wands$/.test(card.name)) {
567
+ return `<g stroke="${stroke}" stroke-width="1.8" fill="none" opacity="0.9">
568
+ <path d="M -20 30 L 20 -30"/>
569
+ <path d="M -8 -22 L 8 -38"/>
570
+ <path d="M -28 18 L -16 28"/>
571
+ <circle cx="0" cy="0" r="3" fill="${stroke}"/>
572
+ </g>`;
573
+ }
574
+ if (/Cups$/.test(card.name)) {
575
+ return `<g stroke="${stroke}" stroke-width="1.6" fill="none" opacity="0.9">
576
+ <path d="M -22 -16 Q 0 -28 22 -16 L 16 18 Q 0 26 -16 18 Z"/>
577
+ <path d="M -10 -10 Q 0 -4 10 -10"/>
578
+ <circle cx="0" cy="-2" r="2.5" fill="${stroke}"/>
579
+ </g>`;
580
+ }
581
+ if (/Swords$/.test(card.name)) {
582
+ return `<g stroke="${stroke}" stroke-width="1.8" fill="none" opacity="0.9">
583
+ <path d="M 0 -32 L 0 28"/>
584
+ <path d="M -14 -16 L 14 -16"/>
585
+ <path d="M -4 24 L 4 24"/>
586
+ <circle cx="0" cy="0" r="2.5" fill="${stroke}"/>
587
+ </g>`;
588
+ }
589
+ // Pentacles
590
+ return `<g stroke="${stroke}" stroke-width="1.6" fill="none" opacity="0.9">
591
+ <circle cx="0" cy="0" r="26"/>
592
+ <path d="M 0 -26 L 15 24 L -22 -8 L 22 -8 L -15 24 Z"/>
593
+ <circle cx="0" cy="0" r="3" fill="${stroke}"/>
594
+ </g>`;
595
+ }
596
+ function wrapText(s, maxChars, maxLines) {
597
+ if (!s)
598
+ return [];
599
+ const words = s.split(/\s+/);
600
+ const lines = [];
601
+ let cur = '';
602
+ for (const w of words) {
603
+ if (!cur) {
604
+ cur = w;
605
+ continue;
606
+ }
607
+ if (cur.length + 1 + w.length <= maxChars)
608
+ cur += ' ' + w;
609
+ else {
610
+ lines.push(cur);
611
+ cur = w;
612
+ if (lines.length >= maxLines)
613
+ break;
614
+ }
615
+ }
616
+ if (cur && lines.length < maxLines)
617
+ lines.push(cur);
618
+ return lines;
619
+ }
620
+ function capitalize(s) {
621
+ return s.length === 0 ? s : s[0].toUpperCase() + s.slice(1);
622
+ }
623
+ function esc(s) {
624
+ return String(s).replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
625
+ }
626
+ // ── Singleton ────────────────────────────────────────────────────────────────
627
+ let _instance = null;
628
+ export function getBrainTarot() {
629
+ if (!_instance)
630
+ _instance = new BrainTarot();
631
+ return _instance;
632
+ }
633
+ export function resetBrainTarotForTests() { _instance = null; }
634
+ //# sourceMappingURL=brain-tarot.js.map