@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,44 @@
1
+ import { GlobalBrain } from './global-brain.js';
2
+ import { BrainReplay } from './brain-replay.js';
3
+ export type RoastPersona = 'gordon-ramsay' | 'simon-cowell' | 'mom' | 'comedian' | 'critic' | 'philosopher';
4
+ export type RoastIntensity = 'gentle' | 'medium' | 'savage';
5
+ export type RoastGrade = 'A+' | 'B-' | 'C' | 'F' | 'M for Mature';
6
+ export interface RoastEvidence {
7
+ memory: string;
8
+ observation: string;
9
+ }
10
+ export interface RoastResult {
11
+ lines: string[];
12
+ grade: RoastGrade;
13
+ persona: RoastPersona;
14
+ intensity: RoastIntensity;
15
+ evidence: RoastEvidence[];
16
+ svg: string;
17
+ }
18
+ export interface RoastOptions {
19
+ target?: string;
20
+ intensity?: RoastIntensity;
21
+ persona?: RoastPersona;
22
+ }
23
+ export declare class BrainRoast {
24
+ private brain;
25
+ private replay;
26
+ constructor(brain?: GlobalBrain, replay?: BrainReplay);
27
+ roast(project: string, opts?: RoastOptions): Promise<RoastResult>;
28
+ /** Re-render the top line as a share card. */
29
+ roastCard(result: RoastResult): {
30
+ svg: string;
31
+ };
32
+ private gatherFindings;
33
+ private templateUsable;
34
+ private pickTemplates;
35
+ private templateScore;
36
+ private renderTemplate;
37
+ private buildEvidence;
38
+ private evidenceFor;
39
+ private computeGrade;
40
+ private renderCard;
41
+ }
42
+ export declare function getBrainRoast(): BrainRoast;
43
+ export declare function resetBrainRoastForTests(): void;
44
+ //# sourceMappingURL=brain-roast.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"brain-roast.d.ts","sourceRoot":"","sources":["../../src/brain/brain-roast.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,WAAW,EAAkB,MAAM,mBAAmB,CAAC;AAChE,OAAO,EAAE,WAAW,EAA+B,MAAM,mBAAmB,CAAC;AAI7E,MAAM,MAAM,YAAY,GACpB,eAAe,GACf,cAAc,GACd,KAAK,GACL,UAAU,GACV,QAAQ,GACR,aAAa,CAAC;AAElB,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAC5D,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG,IAAI,GAAG,GAAG,GAAG,GAAG,GAAG,cAAc,CAAC;AAElE,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,KAAK,EAAE,UAAU,CAAC;IAClB,OAAO,EAAE,YAAY,CAAC;IACtB,SAAS,EAAE,cAAc,CAAC;IAC1B,QAAQ,EAAE,aAAa,EAAE,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,cAAc,CAAC;IAC3B,OAAO,CAAC,EAAE,YAAY,CAAC;CACxB;AAwLD,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAc;IAC3B,OAAO,CAAC,MAAM,CAAc;gBAEhB,KAAK,CAAC,EAAE,WAAW,EAAE,MAAM,CAAC,EAAE,WAAW;IAK/C,KAAK,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,OAAO,CAAC,WAAW,CAAC;IA0C3E,8CAA8C;IAC9C,SAAS,CAAC,MAAM,EAAE,WAAW,GAAG;QAAE,GAAG,EAAE,MAAM,CAAA;KAAE;IAM/C,OAAO,CAAC,cAAc;IA0HtB,OAAO,CAAC,cAAc;IAYtB,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,aAAa;IAYrB,OAAO,CAAC,cAAc;IAqBtB,OAAO,CAAC,aAAa;IAarB,OAAO,CAAC,WAAW;IAoBnB,OAAO,CAAC,YAAY;IAyBpB,OAAO,CAAC,UAAU;CA2DnB;AAoCD,wBAAgB,aAAa,IAAI,UAAU,CAG1C;AACD,wBAAgB,uBAAuB,IAAI,IAAI,CAAsB"}
@@ -0,0 +1,566 @@
1
+ // src/brain/brain-roast.ts — Brain Roast (viral feature)
2
+ // v6.0.2
3
+ //
4
+ // Comedy code-review mode. Reads brain events/memories, finds embarrassing
5
+ // patterns (TODO mountains, rename loops, abandoned refactors, recurring bugs),
6
+ // and generates spicy-but-affectionate roasts in one of six personas.
7
+ //
8
+ // Pure-stdlib, zero new npm deps. Template-based (no LLM). Graceful empty-state.
9
+ import * as path from 'path';
10
+ import { GlobalBrain, getGlobalBrain } from './global-brain.js';
11
+ import { getBrainReplay } from './brain-replay.js';
12
+ const PERSONAS = {
13
+ 'gordon-ramsay': [
14
+ { text: 'You have {{todoCount}} TODOs in this codebase. {{todoCount}}. That is not a backlog, that is a CRIME SCENE.', needs: ['todoCount'] },
15
+ { text: 'I see {{renameLoop}}. Tell me, chef — who hurt you?', needs: ['renameLoops'] },
16
+ { text: '{{hotFile}} has been edited {{hotEdits}} times. {{hotEdits}}! It is not a file, it is a HOSTAGE.', needs: ['hotFiles'] },
17
+ { text: 'You shipped this with {{hallucinations}} hallucinations? I would not feed this to my DOG.', needs: ['hallucinations'] },
18
+ { text: 'This brain is {{ageDays}} days old and you still cannot agree with yourself? {{decisionFlips}} flipped decisions. SHAMEFUL.', needs: ['ageDays', 'decisionFlips'] },
19
+ { text: 'You call THIS a refactor? You abandoned {{abandonedCount}} of them. ABANDONED. Like my hopes for this kitchen.', needs: ['abandonedRefactors'] },
20
+ { text: 'No tests. NO TESTS. Get OUT of my kitchen.', needs: ['noTests'] },
21
+ { text: '{{midnightCommits}} commits after midnight. Stop. Go to bed. The code will still be broken tomorrow.', needs: ['midnightCommits'] },
22
+ { text: 'Bland. Dry. Overcomplicated. Like everything in your {{topAgent}} log.', needs: ['topAgent'] },
23
+ { text: 'I have seen pasta sauce with more structure than this.', needs: [] },
24
+ { text: '{{collisions}} collisions and you call this a codebase? It is a PILE-UP.', needs: ['collisions'] },
25
+ { text: 'You wrote this. You. Look me in the eye and TELL ME you wrote this.', needs: [] },
26
+ { text: 'The bug pattern {{recurringBug}} appeared {{recurringBugCount}} times. Are you doing it on PURPOSE?', needs: ['recurringBugs'] },
27
+ { text: 'Donkey. Absolute. Donkey.', needs: [] },
28
+ { text: '{{totalMemories}} memories and not one of them is "do not ship at 3am". SORT IT OUT.', needs: ['totalMemories', 'midnightCommits'] },
29
+ { text: 'This codebase is more confused than my grandmother at a TikTok shop.', needs: [] },
30
+ { text: 'You have {{agentVariety}} different agents and they ALL disagree. Pick a lane.', needs: ['agentVariety'] },
31
+ { text: 'The file {{hotFile}} is touched more than my microphone. KNOCK IT OFF.', needs: ['hotFiles'] },
32
+ { text: 'I have tasted regret. It tastes like this codebase.', needs: [] },
33
+ { text: 'Wake up. WAKE UP. This is not a feature, it is a CRY for help.', needs: [] },
34
+ { text: 'You renamed it three times and STILL got it wrong. THREE TIMES.', needs: ['renameLoops'] },
35
+ ],
36
+ 'simon-cowell': [
37
+ { text: 'I am going to be honest with you. That was dreadful.', needs: [] },
38
+ { text: 'You have {{todoCount}} TODOs. It is a karaoke night, not a codebase.', needs: ['todoCount'] },
39
+ { text: 'I have seen better architecture in a IKEA catalogue. And IKEA, I am not a fan.', needs: [] },
40
+ { text: '{{hotFile}}? {{hotEdits}} edits? Darling, even Britney took breaks.', needs: ['hotFiles'] },
41
+ { text: 'It was a no from me. It is still a no.', needs: [] },
42
+ { text: 'You sing one song. You commit one feature. Pick one.', needs: [] },
43
+ { text: '{{renameLoop}} is the worst stage name I have ever seen.', needs: ['renameLoops'] },
44
+ { text: 'There is no x-factor in this code. There is no factor at all.', needs: [] },
45
+ { text: '{{hallucinations}} hallucinations? The judges are concerned. Specifically me.', needs: ['hallucinations'] },
46
+ { text: 'This is not a refactor. This is a hostage video.', needs: ['abandonedRefactors'] },
47
+ { text: 'You walked in here {{ageDays}} days ago with a dream. The dream is now a nightmare.', needs: ['ageDays'] },
48
+ { text: 'I am bored. The codebase is bored. The compiler is bored.', needs: [] },
49
+ { text: '{{topAgent}} did all the work. Be honest. You just rode the elevator.', needs: ['topAgent'] },
50
+ { text: '{{collisions}} collisions. It is not a duet, it is a hostage situation.', needs: ['collisions'] },
51
+ { text: 'I want you to listen to me very carefully. This is not good.', needs: [] },
52
+ { text: 'It is forgettable. Like a Eurovision interval act.', needs: [] },
53
+ { text: 'You will not be going through to the next round of code review.', needs: [] },
54
+ { text: '{{decisionFlips}} decision reversals. Pick a key and sing in it.', needs: ['decisionFlips'] },
55
+ { text: 'The recurring bug {{recurringBug}} appears {{recurringBugCount}} times. Encore? Hard pass.', needs: ['recurringBugs'] },
56
+ { text: 'The lights are on. Nobody is home. The code knows it.', needs: [] },
57
+ { text: 'Do you have ANY tests? No? Then this is a karaoke bar.', needs: ['noTests'] },
58
+ { text: 'I have seen reality TV with more reality than this.', needs: [] },
59
+ ],
60
+ 'mom': [
61
+ { text: 'Honey. {{todoCount}} TODOs. We are going to sit down and talk about this.', needs: ['todoCount'] },
62
+ { text: 'I am not mad. I am just disappointed. {{hotEdits}} edits to one file, sweetie.', needs: ['hotFiles'] },
63
+ { text: 'When you have time, could you maybe close one of the {{abandonedCount}} refactors? Just one. For mom.', needs: ['abandonedRefactors'] },
64
+ { text: 'You did NOT just commit at 3am again. Did you eat?', needs: ['midnightCommits'] },
65
+ { text: 'I love you. The codebase loves you. We just want to help.', needs: [] },
66
+ { text: 'You named it {{renameLoop}}? Oh honey. Honey.', needs: ['renameLoops'] },
67
+ { text: 'I cleaned your repo. Do not ask which {{abandonedCount}} branches I deleted.', needs: ['abandonedRefactors'] },
68
+ { text: 'No tests? In this house? Under MY roof?', needs: ['noTests'] },
69
+ { text: 'I told your father about the {{collisions}} collisions. He is very quiet right now.', needs: ['collisions'] },
70
+ { text: 'You used to write such clean code. What happened.', needs: [] },
71
+ { text: 'Have you eaten? Have you slept? Have you removed any of the {{todoCount}} TODOs?', needs: ['todoCount'] },
72
+ { text: 'Sweetie, your brother\'s codebase has {{topAgent}} doing all the work too. I am not comparing. I am observing.', needs: ['topAgent'] },
73
+ { text: '{{ageDays}} days old. So mature. So messy.', needs: ['ageDays'] },
74
+ { text: 'I framed your first commit. The recent ones I will not be framing.', needs: [] },
75
+ { text: 'You have a beautiful mind. The code is not currently reflecting it.', needs: [] },
76
+ { text: 'I packed you a lunch. I cannot pack you a working test suite.', needs: ['noTests'] },
77
+ { text: 'The {{recurringBug}} keeps coming back. Like the cat. Please address it.', needs: ['recurringBugs'] },
78
+ { text: 'Take a deep breath. The hallucinations are NOT real. Even if there are {{hallucinations}} of them.', needs: ['hallucinations'] },
79
+ { text: 'You are SO talented. The proof is somewhere in this {{totalMemories}}-memory archive. I am sure.', needs: ['totalMemories'] },
80
+ { text: 'I am proud of you. I am also locking your laptop after midnight.', needs: ['midnightCommits'] },
81
+ { text: 'There is a hole in your codebase the shape of {{hotFile}}. Please patch it.', needs: ['hotFiles'] },
82
+ { text: 'Did you flip {{decisionFlips}} decisions today? On purpose? Tell mommy.', needs: ['decisionFlips'] },
83
+ ],
84
+ 'comedian': [
85
+ { text: 'You know you got {{todoCount}} TODOs? Yeah. That\'s not a backlog, that\'s a TED Talk.', needs: ['todoCount'] },
86
+ { text: '{{renameLoop}}? I\'ve seen prison nicknames with more dignity.', needs: ['renameLoops'] },
87
+ { text: 'Your hottest file is {{hotFile}}. {{hotEdits}} edits. That file\'s seen things.', needs: ['hotFiles'] },
88
+ { text: '{{abandonedCount}} abandoned refactors. You don\'t have a repo, you have a Goodwill drop-off.', needs: ['abandonedRefactors'] },
89
+ { text: '{{midnightCommits}} commits after midnight. That ain\'t productivity. That\'s a hostage situation.', needs: ['midnightCommits'] },
90
+ { text: 'No tests? Bold strategy, Cotton. Let\'s see if it pays off in production.', needs: ['noTests'] },
91
+ { text: '{{hallucinations}} hallucinations. The brain isn\'t lying — it\'s just freestyling.', needs: ['hallucinations'] },
92
+ { text: '{{topAgent}} did all the heavy lifting. Give that bot a raise.', needs: ['topAgent'] },
93
+ { text: 'The {{recurringBug}} bug came back {{recurringBugCount}} times. It\'s not a bug, it\'s a recurring character.', needs: ['recurringBugs'] },
94
+ { text: 'Your collision rate is {{collisions}}. That\'s not a codebase, that\'s a Mario Kart match.', needs: ['collisions'] },
95
+ { text: 'You flipped {{decisionFlips}} decisions. Pick a side. The fence is collapsing.', needs: ['decisionFlips'] },
96
+ { text: '{{ageDays}} days alive and the brain still has trust issues with itself.', needs: ['ageDays'] },
97
+ { text: '{{totalMemories}} memories. Half are good. Half I\'m not allowed to mention on stage.', needs: ['totalMemories'] },
98
+ { text: 'You named a file {{renameLoop}}. That\'s not a filename, that\'s a confession.', needs: ['renameLoops'] },
99
+ { text: 'I want to roast you but the codebase already did it for me.', needs: [] },
100
+ { text: 'Your codebase has the energy of a Wi-Fi password at a coffee shop. Long and unnecessary.', needs: [] },
101
+ { text: 'Look, every coder has a hot file. Yours is just hot like a stovetop.', needs: ['hotFiles'] },
102
+ { text: '{{agentVariety}} agents working on this and they all walked out for different reasons.', needs: ['agentVariety'] },
103
+ { text: 'I read your git log. It reads like a true-crime podcast.', needs: [] },
104
+ { text: 'Your linter is not "off". It quit. It moved to Bali. It does pottery now.', needs: [] },
105
+ { text: 'You\'ve got {{todoCount}} TODOs. That\'s not a list. That\'s a bucket list. For the codebase. Before it dies.', needs: ['todoCount'] },
106
+ { text: '{{hotFile}} called. It wants a vacation. And alimony.', needs: ['hotFiles'] },
107
+ ],
108
+ 'critic': [
109
+ { text: 'The brain demonstrates {{todoCount}} unresolved intentions — an ambitious archive of inaction.', needs: ['todoCount'] },
110
+ { text: 'The recurring motif of {{renameLoop}} suggests an unresolved identity crisis at the file system level.', needs: ['renameLoops'] },
111
+ { text: '{{hotFile}} bears the wear of {{hotEdits}} revisits — a palimpsest of indecision.', needs: ['hotFiles'] },
112
+ { text: '{{abandonedCount}} half-completed refactors form a poignant tableau of intent without follow-through.', needs: ['abandonedRefactors'] },
113
+ { text: '{{hallucinations}} hallucinations caught — the brain\'s relationship with truth remains aspirational.', needs: ['hallucinations'] },
114
+ { text: 'A codebase {{ageDays}} days old, oscillating between {{decisionFlips}} contradictory verdicts. Compelling.', needs: ['ageDays', 'decisionFlips'] },
115
+ { text: '{{collisions}} collisions — the work of a brain unafraid to disagree with itself, often, publicly.', needs: ['collisions'] },
116
+ { text: 'The absence of tests reads as a stylistic choice. A bold one. A documented one.', needs: ['noTests'] },
117
+ { text: 'In {{midnightCommits}} after-midnight commits, one perceives the auteur at their most exposed.', needs: ['midnightCommits'] },
118
+ { text: '{{topAgent}} emerges as the dominant voice — collaborator, ghost, or sole author? The text refuses to clarify.', needs: ['topAgent'] },
119
+ { text: 'A pattern is not a flaw if you repeat it {{recurringBugCount}} times. It becomes a signature.', needs: ['recurringBugs'] },
120
+ { text: 'The codebase resists closure with the determination of a postmodern novel.', needs: [] },
121
+ { text: 'Every file is a draft. Every commit is a confession. Every TODO is a vow unkept.', needs: [] },
122
+ { text: 'The {{totalMemories}} memories form less of an archive and more of an inventory of doubt.', needs: ['totalMemories'] },
123
+ { text: 'One does not "read" this codebase. One survives it.', needs: [] },
124
+ { text: 'The {{recurringBug}} returns like a memory the brain cannot integrate. Therapy, perhaps.', needs: ['recurringBugs'] },
125
+ { text: 'The work is brave. The work is exhausting. The work needs a structural editor.', needs: [] },
126
+ { text: '{{agentVariety}} authorial voices — a chorus, or a committee. Different things.', needs: ['agentVariety'] },
127
+ { text: 'There is a Beckettian quality to the abandoned {{abandonedCount}} refactors.', needs: ['abandonedRefactors'] },
128
+ { text: 'It is rare to encounter such commitment to the unresolved.', needs: [] },
129
+ { text: 'The brand of this codebase is consistency — in its refusal to converge.', needs: [] },
130
+ { text: 'Recommendation: enroll in a writing workshop. Or a git workshop. Either is fine.', needs: [] },
131
+ ],
132
+ 'philosopher': [
133
+ { text: 'What is a TODO, if not a promise we make to a future self who never arrives? You have {{todoCount}}.', needs: ['todoCount'] },
134
+ { text: 'You renamed {{renameLoop}} thrice. Ship of Theseus would weep. Is it still the same file?', needs: ['renameLoops'] },
135
+ { text: '{{hotFile}} bears {{hotEdits}} marks of your touch. The Stoics would say: change one thing, or change yourself.', needs: ['hotFiles'] },
136
+ { text: '{{abandonedCount}} refactors abandoned. Each a road not taken. Frost would understand.', needs: ['abandonedRefactors'] },
137
+ { text: '{{midnightCommits}} commits at midnight — the hour Nietzsche warned us about.', needs: ['midnightCommits'] },
138
+ { text: 'A codebase without tests is a leap of faith. Kierkegaard would approve. Senior engineers, less so.', needs: ['noTests'] },
139
+ { text: '{{collisions}} collisions in the brain. Hegel called this dialectic. Your CI calls it broken.', needs: ['collisions'] },
140
+ { text: 'To flip {{decisionFlips}} decisions is to be honest. To stop flipping them is to be free.', needs: ['decisionFlips'] },
141
+ { text: '{{hallucinations}} hallucinations — the brain dreams of code that does not exist. Cogito, perhaps not ergo sum.', needs: ['hallucinations'] },
142
+ { text: '{{ageDays}} days alive. In the grand sweep of time, even this codebase is brief. Almost merciful.', needs: ['ageDays'] },
143
+ { text: 'The {{recurringBug}} returns like Sisyphus\' boulder. We must imagine it happy. We must fix it.', needs: ['recurringBugs'] },
144
+ { text: 'You are not your codebase. The codebase is just what you do when you cannot sleep.', needs: [] },
145
+ { text: 'In {{totalMemories}} memories, the brain has not yet remembered how to forget.', needs: ['totalMemories'] },
146
+ { text: '{{topAgent}} writes the most. But who watches the watcher?', needs: ['topAgent'] },
147
+ { text: 'Every git push is a small mortality. Every revert, a denial of death.', needs: [] },
148
+ { text: 'There is no truth in this codebase, only contradictions. Marx would call this revolutionary.', needs: [] },
149
+ { text: 'The unexamined codebase is not worth shipping.', needs: [] },
150
+ { text: 'Heraclitus said: you cannot step in the same git diff twice. {{hotFile}} disagrees.', needs: ['hotFiles'] },
151
+ { text: 'Each {{recurringBug}} is the eternal return. Embrace it. Or fix the index.', needs: ['recurringBugs'] },
152
+ { text: '{{agentVariety}} agents have shaped this. The self is a committee. You are merely the chair.', needs: ['agentVariety'] },
153
+ { text: 'A senior engineer once said: simplicity is the ultimate sophistication. They did not see your {{hotFile}}.', needs: ['hotFiles'] },
154
+ { text: 'When you stare into the codebase, the codebase stares back. {{collisions}} collisions are merely the eye contact.', needs: ['collisions'] },
155
+ ],
156
+ };
157
+ const INTENSITY_LINE_COUNTS = {
158
+ gentle: 3,
159
+ medium: 5,
160
+ savage: 8,
161
+ };
162
+ // ── Class ────────────────────────────────────────────────────────────────────
163
+ export class BrainRoast {
164
+ constructor(brain, replay) {
165
+ this.brain = brain ?? getGlobalBrain();
166
+ this.replay = replay ?? getBrainReplay();
167
+ }
168
+ async roast(project, opts = {}) {
169
+ const persona = opts.persona ?? 'comedian';
170
+ const intensity = opts.intensity ?? 'medium';
171
+ const findings = this.gatherFindings(project);
172
+ // Empty-state graceful path.
173
+ if (findings.totalMemories === 0 && findings.ageDays === 0) {
174
+ const emptyLine = 'Nothing to roast yet — your brain is too new. Come back when you have sinned.';
175
+ return {
176
+ lines: [emptyLine],
177
+ grade: 'A+',
178
+ persona,
179
+ intensity,
180
+ evidence: [],
181
+ svg: this.renderCard(project, [emptyLine], 'A+', persona, intensity),
182
+ };
183
+ }
184
+ const templates = PERSONAS[persona];
185
+ const usable = templates.filter(t => this.templateUsable(t, findings));
186
+ const lineCount = INTENSITY_LINE_COUNTS[intensity];
187
+ const picked = this.pickTemplates(usable, lineCount, findings);
188
+ const lines = picked.map(t => this.renderTemplate(t.text, findings)).filter(s => s.length <= 200);
189
+ if (lines.length === 0) {
190
+ lines.push('Surprisingly clean. Suspiciously clean. Are you hiding something?');
191
+ }
192
+ const grade = this.computeGrade(findings, intensity);
193
+ const evidence = this.buildEvidence(findings, picked);
194
+ return {
195
+ lines,
196
+ grade,
197
+ persona,
198
+ intensity,
199
+ evidence,
200
+ svg: this.renderCard(project, lines, grade, persona, intensity),
201
+ };
202
+ }
203
+ /** Re-render the top line as a share card. */
204
+ roastCard(result) {
205
+ return { svg: result.svg };
206
+ }
207
+ // ── Findings ────────────────────────────────────────────────────────────────
208
+ gatherFindings(project) {
209
+ let entries = [];
210
+ let events = [];
211
+ try {
212
+ const projectId = GlobalBrain.projectIdFor(project);
213
+ entries = this.brain.recall({ projectId, limit: 5000 });
214
+ }
215
+ catch { /* empty */ }
216
+ try {
217
+ events = this.replay.listEvents(project);
218
+ }
219
+ catch { /* empty */ }
220
+ const fileEdits = new Map();
221
+ const renameSet = new Set();
222
+ const bugPatterns = new Map();
223
+ const abandonedSet = new Set();
224
+ const agentCounts = new Map();
225
+ let firstSeen = Number.POSITIVE_INFINITY;
226
+ let lastSeen = 0;
227
+ let todoCount = 0;
228
+ let hallucinations = 0;
229
+ let collisions = 0;
230
+ let decisionFlips = 0;
231
+ let midnightCommits = 0;
232
+ let hasTests = false;
233
+ const recordFile = (raw) => {
234
+ if (typeof raw !== 'string' || raw.length === 0)
235
+ return;
236
+ const name = raw.split(/[\\/]/).pop() ?? raw;
237
+ fileEdits.set(name, (fileEdits.get(name) ?? 0) + 1);
238
+ // Detect rename loops: "auth-v2-FINAL", "auth-FINAL-FINAL", "auth-actual"
239
+ if (/(final|actual|new|fixed|real|v2|v3|copy|backup|old|tmp|temp)/i.test(name)) {
240
+ renameSet.add(name);
241
+ }
242
+ if (/\.(test|spec)\.(t|j)sx?$/i.test(name) || /__tests?__/.test(raw))
243
+ hasTests = true;
244
+ };
245
+ for (const e of entries) {
246
+ const t = e.createdAt instanceof Date ? e.createdAt.getTime() : Number(e.createdAt);
247
+ if (Number.isFinite(t)) {
248
+ if (t < firstSeen)
249
+ firstSeen = t;
250
+ if (t > lastSeen)
251
+ lastSeen = t;
252
+ const hour = new Date(t).getHours();
253
+ if (hour >= 0 && hour < 5)
254
+ midnightCommits += 1;
255
+ }
256
+ agentCounts.set(e.agentTool, (agentCounts.get(e.agentTool) ?? 0) + 1);
257
+ const meta = (e.metadata ?? {});
258
+ recordFile(meta.file ?? meta.path ?? meta.filename);
259
+ const body = String(e.content ?? '');
260
+ if (/\bTODO\b|\bFIXME\b/i.test(body))
261
+ todoCount += 1;
262
+ if (/abandon|paused|on hold|deferred/i.test(body) && /refactor|rewrite|migration/i.test(body)) {
263
+ const m = body.match(/refactor[^.\n]{0,60}/i);
264
+ if (m)
265
+ abandonedSet.add(m[0].slice(0, 80));
266
+ }
267
+ if (/hallucin|fabricat/i.test(e.category) || /hallucin/i.test(body))
268
+ hallucinations += 1;
269
+ if (/collision/i.test(e.category))
270
+ collisions += 1;
271
+ if (/reversed|flipped|reconsider|backtrack/i.test(body) && /decision|adr/i.test(e.category))
272
+ decisionFlips += 1;
273
+ // Recurring bug fingerprint = lowercased "bug:" or "error:" head fragment
274
+ const bugMatch = body.match(/(?:bug|error|exception|crash|null|undefined|panic)[^.\n]{3,60}/i);
275
+ if (bugMatch) {
276
+ const key = bugMatch[0].toLowerCase().replace(/\s+/g, ' ').trim().slice(0, 60);
277
+ bugPatterns.set(key, (bugPatterns.get(key) ?? 0) + 1);
278
+ }
279
+ }
280
+ for (const ev of events) {
281
+ if (Number.isFinite(ev.ts)) {
282
+ if (ev.ts < firstSeen)
283
+ firstSeen = ev.ts;
284
+ if (ev.ts > lastSeen)
285
+ lastSeen = ev.ts;
286
+ const hour = new Date(ev.ts).getHours();
287
+ if (hour >= 0 && hour < 5)
288
+ midnightCommits += 1;
289
+ }
290
+ if (ev.agent)
291
+ agentCounts.set(ev.agent, (agentCounts.get(ev.agent) ?? 0) + 1);
292
+ const payload = (ev.payload ?? {});
293
+ recordFile(payload.file ?? payload.path ?? payload.entity);
294
+ if (ev.type.includes('hallucination') || ev.type.includes('quarantine'))
295
+ hallucinations += 1;
296
+ if (ev.type.includes('collision'))
297
+ collisions += 1;
298
+ if (ev.type.includes('decision') && (ev.type.includes('reverse') || ev.type.includes('flip')))
299
+ decisionFlips += 1;
300
+ }
301
+ const hotFiles = Array.from(fileEdits.entries())
302
+ .filter(([, n]) => n >= 3)
303
+ .sort((a, b) => b[1] - a[1])
304
+ .slice(0, 5)
305
+ .map(([file, edits]) => ({ file, edits }));
306
+ const recurringBugs = Array.from(bugPatterns.entries())
307
+ .filter(([, n]) => n >= 2)
308
+ .sort((a, b) => b[1] - a[1])
309
+ .slice(0, 5)
310
+ .map(([pattern, count]) => ({ pattern, count }));
311
+ const hasData = entries.length > 0 || events.length > 0;
312
+ const ageDays = hasData && Number.isFinite(firstSeen)
313
+ ? Math.max(0, (lastSeen - firstSeen) / (24 * 60 * 60 * 1000))
314
+ : 0;
315
+ const totalMemories = entries.length
316
+ + events.filter(e => e.type === 'memory.write' || e.type === 'brain.remember').length;
317
+ const topAgentEntry = Array.from(agentCounts.entries()).sort((a, b) => b[1] - a[1])[0];
318
+ return {
319
+ todoCount,
320
+ renameLoops: Array.from(renameSet).slice(0, 5),
321
+ hotFiles,
322
+ abandonedRefactors: Array.from(abandonedSet).slice(0, 5),
323
+ recurringBugs,
324
+ hallucinations,
325
+ collisions,
326
+ decisionFlips,
327
+ ageDays: Math.round(ageDays * 10) / 10,
328
+ totalMemories,
329
+ topAgent: topAgentEntry?.[0] ?? 'unknown',
330
+ agentVariety: agentCounts.size,
331
+ noTests: !hasTests,
332
+ midnightCommits,
333
+ };
334
+ }
335
+ // ── Template engine ─────────────────────────────────────────────────────────
336
+ templateUsable(t, f) {
337
+ if (!t.needs || t.needs.length === 0)
338
+ return true;
339
+ for (const key of t.needs) {
340
+ const v = f[key];
341
+ if (typeof v === 'number') {
342
+ if (v <= 0)
343
+ return false;
344
+ }
345
+ else if (typeof v === 'boolean') {
346
+ if (!v)
347
+ return false;
348
+ }
349
+ else if (Array.isArray(v)) {
350
+ if (v.length === 0)
351
+ return false;
352
+ }
353
+ else if (typeof v === 'string') {
354
+ if (!v || v === 'unknown')
355
+ return false;
356
+ }
357
+ }
358
+ return true;
359
+ }
360
+ pickTemplates(pool, count, findings) {
361
+ if (pool.length === 0)
362
+ return [];
363
+ // Deterministic-ish ordering by a small priority score: prefer templates that
364
+ // reference high-signal findings. Falls back to insertion order ties.
365
+ const scored = pool.map((t, i) => ({ t, score: this.templateScore(t, findings) - i * 0.001 }));
366
+ scored.sort((a, b) => b.score - a.score);
367
+ const seen = new Set();
368
+ const picked = [];
369
+ for (const { t } of scored) {
370
+ const k = t.text.slice(0, 40);
371
+ if (seen.has(k))
372
+ continue;
373
+ seen.add(k);
374
+ picked.push(t);
375
+ if (picked.length >= count)
376
+ break;
377
+ }
378
+ return picked;
379
+ }
380
+ templateScore(t, f) {
381
+ if (!t.needs)
382
+ return 0.1;
383
+ let s = 0;
384
+ for (const k of t.needs) {
385
+ const v = f[k];
386
+ if (typeof v === 'number')
387
+ s += Math.min(v, 10);
388
+ else if (Array.isArray(v))
389
+ s += Math.min(v.length, 5) * 2;
390
+ else if (typeof v === 'boolean' && v)
391
+ s += 3;
392
+ }
393
+ return s;
394
+ }
395
+ renderTemplate(text, f) {
396
+ const subs = {
397
+ todoCount: String(f.todoCount),
398
+ hallucinations: String(f.hallucinations),
399
+ collisions: String(f.collisions),
400
+ decisionFlips: String(f.decisionFlips),
401
+ ageDays: f.ageDays.toFixed(1),
402
+ totalMemories: String(f.totalMemories),
403
+ topAgent: f.topAgent,
404
+ agentVariety: String(f.agentVariety),
405
+ midnightCommits: String(f.midnightCommits),
406
+ abandonedCount: String(f.abandonedRefactors.length),
407
+ renameLoop: f.renameLoops[0] ?? 'a-file-named-final-final',
408
+ hotFile: f.hotFiles[0]?.file ?? 'unknown-file',
409
+ hotEdits: String(f.hotFiles[0]?.edits ?? 0),
410
+ recurringBug: f.recurringBugs[0]?.pattern ?? 'an unnamed bug',
411
+ recurringBugCount: String(f.recurringBugs[0]?.count ?? 0),
412
+ };
413
+ return text.replace(/\{\{(\w+)\}\}/g, (_, k) => subs[k] ?? k);
414
+ }
415
+ buildEvidence(f, picked) {
416
+ const out = [];
417
+ const seen = new Set();
418
+ for (const t of picked) {
419
+ for (const k of t.needs ?? []) {
420
+ if (seen.has(k))
421
+ continue;
422
+ seen.add(k);
423
+ out.push(this.evidenceFor(k, f));
424
+ }
425
+ }
426
+ return out.slice(0, 6);
427
+ }
428
+ evidenceFor(key, f) {
429
+ switch (key) {
430
+ case 'todoCount': return { memory: 'TODO/FIXME tags in recall', observation: `${f.todoCount} unresolved TODOs across memories` };
431
+ case 'renameLoops': return { memory: 'file rename pattern', observation: `Files matching rename-loop suffix: ${f.renameLoops.join(', ') || 'none'}` };
432
+ case 'hotFiles': return { memory: 'file edit counts', observation: `Hottest: ${f.hotFiles[0]?.file ?? 'n/a'} with ${f.hotFiles[0]?.edits ?? 0} edits` };
433
+ case 'abandonedRefactors': return { memory: 'paused/deferred refactor entries', observation: `${f.abandonedRefactors.length} abandoned refactors observed` };
434
+ case 'recurringBugs': return { memory: 'recurring bug fingerprints', observation: `Top recurring: "${f.recurringBugs[0]?.pattern ?? 'none'}" x${f.recurringBugs[0]?.count ?? 0}` };
435
+ case 'hallucinations': return { memory: 'hallucination quarantine log', observation: `${f.hallucinations} hallucinations recorded` };
436
+ case 'collisions': return { memory: 'collision detective events', observation: `${f.collisions} collisions detected` };
437
+ case 'decisionFlips': return { memory: 'ADR / decision reversals', observation: `${f.decisionFlips} decision flips observed` };
438
+ case 'ageDays': return { memory: 'first vs last event timestamp', observation: `Brain age ~${f.ageDays.toFixed(1)} days` };
439
+ case 'totalMemories': return { memory: 'recall + replay counts', observation: `${f.totalMemories} stored memories` };
440
+ case 'topAgent': return { memory: 'agent contribution histogram', observation: `${f.topAgent} is the dominant author` };
441
+ case 'agentVariety': return { memory: 'distinct agents seen', observation: `${f.agentVariety} distinct agents` };
442
+ case 'noTests': return { memory: 'absence of *.test.* / __tests__', observation: f.noTests ? 'No test files surfaced in memories' : 'Tests detected' };
443
+ case 'midnightCommits': return { memory: 'event timestamps 00:00-04:59', observation: `${f.midnightCommits} late-night events` };
444
+ default: return { memory: String(key), observation: 'pattern observed' };
445
+ }
446
+ }
447
+ computeGrade(f, intensity) {
448
+ let demerits = 0;
449
+ demerits += Math.min(f.todoCount, 30) / 5;
450
+ demerits += f.hallucinations * 2;
451
+ demerits += f.collisions;
452
+ demerits += f.decisionFlips * 1.5;
453
+ demerits += f.midnightCommits * 0.4;
454
+ demerits += f.abandonedRefactors.length * 2;
455
+ demerits += f.renameLoops.length * 1.5;
456
+ demerits += (f.hotFiles[0]?.edits ?? 0) > 10 ? 3 : 0;
457
+ if (f.noTests && f.totalMemories > 5)
458
+ demerits += 6;
459
+ // Intensity tilts the grade toward harsher labels on the same demerits.
460
+ const tilt = intensity === 'savage' ? 1.4 : intensity === 'medium' ? 1.0 : 0.7;
461
+ const score = demerits * tilt;
462
+ if (score >= 40)
463
+ return 'M for Mature';
464
+ if (score >= 25)
465
+ return 'F';
466
+ if (score >= 14)
467
+ return 'C';
468
+ if (score >= 6)
469
+ return 'B-';
470
+ return 'A+';
471
+ }
472
+ // ── SVG card ────────────────────────────────────────────────────────────────
473
+ renderCard(project, lines, grade, persona, intensity) {
474
+ const W = 1200, H = 630, P = 56;
475
+ const headline = lines[0] ?? '';
476
+ const sub = lines[1] ?? '';
477
+ const accent = personaAccent(persona);
478
+ const projName = path.basename(path.resolve(project)) || 'unknown-brain';
479
+ const headlineWrapped = wrapText(headline, 38, 3);
480
+ const headlineY = 230;
481
+ const headlineSvg = headlineWrapped.map((ln, i) => `<text x="${P}" y="${headlineY + i * 64}" fill="url(#r-hero)" font-size="56" font-family="ui-sans-serif,system-ui" font-weight="800">${esc(ln)}</text>`).join('\n ');
482
+ const subWrapped = wrapText(sub, 70, 2);
483
+ const subY = headlineY + headlineWrapped.length * 64 + 32;
484
+ const subSvg = subWrapped.map((ln, i) => `<text x="${P}" y="${subY + i * 28}" fill="#cfe9ff" font-size="20" font-family="ui-sans-serif,system-ui" opacity="0.85">${esc(ln)}</text>`).join('\n ');
485
+ return `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 ${W} ${H}" width="${W}" height="${H}">
486
+ <!-- BRAIN ROAST · v6.0.2 -->
487
+ <defs>
488
+ <linearGradient id="r-bg" x1="0" y1="0" x2="1" y2="1">
489
+ <stop offset="0" stop-color="#0a0612"/>
490
+ <stop offset="0.5" stop-color="#150a1f"/>
491
+ <stop offset="1" stop-color="#240e2e"/>
492
+ </linearGradient>
493
+ <radialGradient id="r-halo" cx="0.75" cy="0.2" r="0.7">
494
+ <stop offset="0" stop-color="${accent}" stop-opacity="0.32"/>
495
+ <stop offset="1" stop-color="${accent}" stop-opacity="0"/>
496
+ </radialGradient>
497
+ <linearGradient id="r-hero" x1="0" y1="0" x2="1" y2="0">
498
+ <stop offset="0" stop-color="${accent}"/>
499
+ <stop offset="1" stop-color="#ffb86b"/>
500
+ </linearGradient>
501
+ </defs>
502
+ <rect width="${W}" height="${H}" fill="url(#r-bg)"/>
503
+ <rect width="${W}" height="${H}" fill="url(#r-halo)"/>
504
+ <text x="${P}" y="80" fill="${accent}" font-size="14" font-family="ui-monospace,monospace" letter-spacing="4">BRAIN ROAST · v6.0.2</text>
505
+ <text x="${P}" y="118" fill="#e2f5ff" font-size="32" font-family="ui-sans-serif,system-ui" font-weight="600">${esc(projName)}</text>
506
+ <text x="${P}" y="148" fill="#cfe9ff" font-size="14" font-family="ui-monospace,monospace" opacity="0.7">persona: ${esc(persona)} · intensity: ${esc(intensity)}</text>
507
+ ${headlineSvg}
508
+ ${subSvg}
509
+ <g transform="translate(${W - P - 140}, ${P + 20})">
510
+ <rect x="0" y="0" width="140" height="80" rx="10" ry="10" fill="${accent}" opacity="0.15" stroke="${accent}" stroke-width="2"/>
511
+ <text x="70" y="32" fill="${accent}" font-size="12" font-family="ui-monospace,monospace" text-anchor="middle" opacity="0.8">GRADE</text>
512
+ <text x="70" y="64" fill="${accent}" font-size="28" font-family="ui-monospace,monospace" text-anchor="middle" font-weight="800">${esc(grade)}</text>
513
+ </g>
514
+ <text x="${P}" y="${H - 56}" fill="${accent}" font-size="13" font-family="ui-monospace,monospace" opacity="0.8">Powered by @shadow-brain</text>
515
+ <text x="${P}" y="${H - 32}" fill="#cfe9ff" font-size="14" font-family="ui-monospace,monospace">npx @theihtisham/agent-shadow-brain roast</text>
516
+ <text x="${W - P}" y="${H - 32}" fill="${accent}" font-size="14" font-family="ui-monospace,monospace" text-anchor="end" font-weight="700">${esc(persona.toUpperCase())}</text>
517
+ </svg>`;
518
+ }
519
+ }
520
+ // ── Helpers ──────────────────────────────────────────────────────────────────
521
+ function personaAccent(persona) {
522
+ switch (persona) {
523
+ case 'gordon-ramsay': return '#ff4444';
524
+ case 'simon-cowell': return '#ffd84d';
525
+ case 'mom': return '#ff9ec4';
526
+ case 'comedian': return '#ffb86b';
527
+ case 'critic': return '#a78bfa';
528
+ case 'philosopher': return '#7dd3fc';
529
+ }
530
+ }
531
+ function wrapText(s, maxChars, maxLines) {
532
+ if (!s)
533
+ return [];
534
+ const words = s.split(/\s+/);
535
+ const lines = [];
536
+ let cur = '';
537
+ for (const w of words) {
538
+ if (!cur) {
539
+ cur = w;
540
+ continue;
541
+ }
542
+ if (cur.length + 1 + w.length <= maxChars)
543
+ cur += ' ' + w;
544
+ else {
545
+ lines.push(cur);
546
+ cur = w;
547
+ if (lines.length >= maxLines)
548
+ break;
549
+ }
550
+ }
551
+ if (cur && lines.length < maxLines)
552
+ lines.push(cur);
553
+ return lines;
554
+ }
555
+ function esc(s) {
556
+ return String(s).replace(/[&<>"']/g, c => ({ '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#39;' }[c]));
557
+ }
558
+ // ── Singleton ────────────────────────────────────────────────────────────────
559
+ let _instance = null;
560
+ export function getBrainRoast() {
561
+ if (!_instance)
562
+ _instance = new BrainRoast();
563
+ return _instance;
564
+ }
565
+ export function resetBrainRoastForTests() { _instance = null; }
566
+ //# sourceMappingURL=brain-roast.js.map