@monoes/monomindcli 1.8.0 → 1.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (844) hide show
  1. package/.claude/agents/design/design-monodesign.md +121 -0
  2. package/.claude/agents/github/issue-tracker.md +12 -12
  3. package/.claude/agents/github/pr-manager.md +10 -10
  4. package/.claude/agents/github/release-manager.md +49 -105
  5. package/.claude/agents/github/repo-architect.md +73 -92
  6. package/.claude/agents/github/sync-coordinator.md +55 -123
  7. package/.claude/agents/marketing/marketing-competitive-content.md +155 -0
  8. package/.claude/agents/marketing/marketing-content-creator.md +13 -0
  9. package/.claude/agents/marketing/marketing-cro-specialist.md +147 -0
  10. package/.claude/agents/marketing/marketing-email-specialist.md +90 -0
  11. package/.claude/agents/marketing/marketing-launch-strategist.md +129 -0
  12. package/.claude/agents/marketing/marketing-pricing-strategist.md +127 -0
  13. package/.claude/agents/specialists/integration-architect.md +94 -0
  14. package/.claude/commands/agents/README.md +4 -0
  15. package/.claude/commands/agents/agent-capabilities.md +6 -2
  16. package/.claude/commands/agents/agent-coordination.md +4 -0
  17. package/.claude/commands/agents/agent-spawning.md +4 -0
  18. package/.claude/commands/agents/agent-types.md +6 -2
  19. package/.claude/commands/analysis/README.md +14 -5
  20. package/.claude/commands/analysis/bottleneck-detect.md +30 -123
  21. package/.claude/commands/analysis/performance-bottlenecks.md +14 -14
  22. package/.claude/commands/analysis/performance-report.md +38 -11
  23. package/.claude/commands/analysis/token-efficiency.md +13 -16
  24. package/.claude/commands/analysis/token-usage.md +34 -12
  25. package/.claude/commands/automation/README.md +15 -5
  26. package/.claude/commands/automation/auto-agent.md +49 -85
  27. package/.claude/commands/automation/self-healing.md +20 -18
  28. package/.claude/commands/automation/session-memory.md +28 -29
  29. package/.claude/commands/automation/smart-agents.md +17 -9
  30. package/.claude/commands/automation/smart-spawn.md +52 -11
  31. package/.claude/commands/automation/workflow-select.md +46 -11
  32. package/.claude/commands/browse.md +5 -0
  33. package/.claude/commands/coordination/README.md +9 -5
  34. package/.claude/commands/coordination/agent-spawn.md +53 -9
  35. package/.claude/commands/coordination/swarm-init.md +39 -42
  36. package/.claude/commands/coordination/task-orchestrate.md +65 -11
  37. package/.claude/commands/github/README.md +21 -8
  38. package/.claude/commands/github/github-modes.md +9 -5
  39. package/.claude/commands/github/issue-tracker.md +34 -33
  40. package/.claude/commands/github/pr-manager.md +20 -17
  41. package/.claude/commands/github/release-manager.md +37 -49
  42. package/.claude/commands/github/repo-architect.md +39 -41
  43. package/.claude/commands/github/sync-coordinator.md +45 -49
  44. package/.claude/commands/hive-mind/README.md +42 -17
  45. package/.claude/commands/hive-mind/hive-mind-consensus.md +68 -4
  46. package/.claude/commands/hive-mind/hive-mind-init.md +55 -5
  47. package/.claude/commands/hive-mind/hive-mind-memory.md +69 -4
  48. package/.claude/commands/hive-mind/hive-mind-spawn.md +71 -10
  49. package/.claude/commands/hive-mind/hive-mind-status.md +52 -4
  50. package/.claude/commands/hive-mind/hive-mind-stop.md +51 -4
  51. package/.claude/commands/hive-mind/hive-mind.md +74 -14
  52. package/.claude/commands/hooks/README.md +62 -7
  53. package/.claude/commands/hooks/overview.md +94 -35
  54. package/.claude/commands/hooks/post-edit.md +48 -87
  55. package/.claude/commands/hooks/post-task.md +37 -87
  56. package/.claude/commands/hooks/pre-edit.md +52 -84
  57. package/.claude/commands/hooks/pre-task.md +46 -81
  58. package/.claude/commands/hooks/session-end.md +49 -85
  59. package/.claude/commands/hooks/setup.md +87 -58
  60. package/.claude/commands/mastermind/_repeat.md +308 -0
  61. package/.claude/commands/mastermind/architect.md +49 -0
  62. package/.claude/commands/mastermind/brain.md +98 -0
  63. package/.claude/commands/mastermind/build.md +22 -0
  64. package/.claude/commands/mastermind/content.md +22 -0
  65. package/.claude/commands/mastermind/createorg.md +94 -0
  66. package/.claude/commands/mastermind/finance.md +22 -0
  67. package/.claude/commands/mastermind/idea.md +22 -0
  68. package/.claude/commands/mastermind/marketing.md +22 -0
  69. package/.claude/commands/mastermind/master.md +379 -0
  70. package/.claude/commands/mastermind/ops.md +22 -0
  71. package/.claude/commands/mastermind/release.md +22 -0
  72. package/.claude/commands/mastermind/research.md +22 -0
  73. package/.claude/commands/mastermind/review.md +22 -0
  74. package/.claude/commands/mastermind/runorg.md +106 -0
  75. package/.claude/commands/mastermind/sales.md +22 -0
  76. package/.claude/commands/mastermind/techport.md +17 -0
  77. package/.claude/commands/memory/README.md +75 -5
  78. package/.claude/commands/memory/memory-search.md +63 -11
  79. package/.claude/commands/monitoring/README.md +64 -4
  80. package/.claude/commands/monitoring/agent-metrics.md +50 -10
  81. package/.claude/commands/monitoring/agents.md +59 -32
  82. package/.claude/commands/monitoring/status.md +96 -34
  83. package/.claude/commands/monograph/README.md +102 -0
  84. package/.claude/commands/monograph/monograph-build.md +79 -0
  85. package/.claude/commands/monograph/monograph-search.md +96 -0
  86. package/.claude/commands/monograph/monograph-stats.md +53 -0
  87. package/.claude/commands/monograph/monograph-watch.md +63 -0
  88. package/.claude/commands/monograph/monograph-wiki.md +91 -0
  89. package/.claude/commands/monomind/createtask.md +277 -0
  90. package/.claude/commands/{monomind-do.md → monomind/do.md} +21 -8
  91. package/.claude/commands/monomind/help.md +118 -0
  92. package/.claude/commands/{monomind-idea.md → monomind/idea.md} +22 -28
  93. package/.claude/commands/{monomind-improve.md → monomind/improve.md} +21 -27
  94. package/.claude/commands/monomind/memory.md +230 -0
  95. package/.claude/commands/monomind/repeat.md +201 -0
  96. package/.claude/commands/monomind/review.md +313 -0
  97. package/.claude/commands/monomind/specialagents.md +125 -0
  98. package/.claude/commands/monomind/swarm.md +161 -0
  99. package/.claude/commands/monomind/understand.md +148 -0
  100. package/.claude/commands/optimization/README.md +69 -5
  101. package/.claude/commands/optimization/auto-topology.md +66 -43
  102. package/.claude/commands/optimization/parallel-execution.md +65 -39
  103. package/.claude/commands/optimization/performance-optimize.md +79 -0
  104. package/.claude/commands/pair/README.md +48 -230
  105. package/.claude/commands/pair/examples.md +85 -441
  106. package/.claude/commands/pair/modes.md +77 -303
  107. package/.claude/commands/pair/session.md +76 -359
  108. package/.claude/commands/sparc/analyzer.md +9 -26
  109. package/.claude/commands/sparc/architect.md +8 -25
  110. package/.claude/commands/sparc/ask.md +27 -68
  111. package/.claude/commands/sparc/batch-executor.md +8 -25
  112. package/.claude/commands/sparc/code.md +12 -53
  113. package/.claude/commands/sparc/coder.md +8 -25
  114. package/.claude/commands/sparc/debug.md +12 -53
  115. package/.claude/commands/sparc/debugger.md +8 -25
  116. package/.claude/commands/sparc/designer.md +8 -25
  117. package/.claude/commands/sparc/devops.md +16 -57
  118. package/.claude/commands/sparc/docs-writer.md +12 -53
  119. package/.claude/commands/sparc/documenter.md +8 -25
  120. package/.claude/commands/sparc/innovator.md +8 -25
  121. package/.claude/commands/sparc/integration.md +12 -53
  122. package/.claude/commands/sparc/mcp.md +12 -53
  123. package/.claude/commands/sparc/memory-manager.md +28 -25
  124. package/.claude/commands/sparc/optimizer.md +8 -25
  125. package/.claude/commands/sparc/orchestrator.md +35 -97
  126. package/.claude/commands/sparc/post-deployment-monitoring-mode.md +13 -54
  127. package/.claude/commands/sparc/refinement-optimization-mode.md +13 -54
  128. package/.claude/commands/sparc/researcher.md +8 -25
  129. package/.claude/commands/sparc/reviewer.md +8 -25
  130. package/.claude/commands/sparc/security-review.md +13 -54
  131. package/.claude/commands/sparc/sparc-modes.md +97 -151
  132. package/.claude/commands/sparc/sparc.md +16 -56
  133. package/.claude/commands/sparc/spec-pseudocode.md +13 -54
  134. package/.claude/commands/sparc/supabase-admin.md +19 -66
  135. package/.claude/commands/sparc/swarm-coordinator.md +21 -25
  136. package/.claude/commands/sparc/tdd.md +8 -25
  137. package/.claude/commands/sparc/tester.md +8 -25
  138. package/.claude/commands/sparc/tutorial.md +12 -53
  139. package/.claude/commands/sparc/workflow-manager.md +8 -25
  140. package/.claude/commands/sparc.md +76 -130
  141. package/.claude/commands/stream-chain/pipeline.md +72 -77
  142. package/.claude/commands/stream-chain/run.md +133 -47
  143. package/.claude/commands/swarm/README.md +37 -12
  144. package/.claude/commands/swarm/analysis.md +47 -69
  145. package/.claude/commands/swarm/development.md +45 -69
  146. package/.claude/commands/swarm/examples.md +77 -142
  147. package/.claude/commands/swarm/maintenance.md +47 -74
  148. package/.claude/commands/swarm/optimization.md +54 -87
  149. package/.claude/commands/swarm/research.md +47 -107
  150. package/.claude/commands/swarm/swarm-analysis.md +58 -4
  151. package/.claude/commands/swarm/swarm-background.md +61 -4
  152. package/.claude/commands/swarm/swarm-modes.md +63 -4
  153. package/.claude/commands/swarm/swarm-monitor.md +50 -4
  154. package/.claude/commands/swarm/swarm-status.md +40 -4
  155. package/.claude/commands/swarm/swarm-strategies.md +73 -5
  156. package/.claude/commands/swarm/swarm.md +70 -18
  157. package/.claude/commands/swarm/testing.md +51 -102
  158. package/.claude/commands/tokens.md +6 -1
  159. package/.claude/commands/training/README.md +36 -6
  160. package/.claude/commands/training/model-update.md +68 -15
  161. package/.claude/commands/training/neural-patterns.md +54 -55
  162. package/.claude/commands/training/neural-train.md +70 -16
  163. package/.claude/commands/training/pattern-learn.md +60 -16
  164. package/.claude/commands/training/specialization.md +78 -49
  165. package/.claude/commands/truth/start.md +87 -109
  166. package/.claude/commands/ts.md +7 -2
  167. package/.claude/commands/verify/check.md +90 -34
  168. package/.claude/commands/verify/start.md +71 -94
  169. package/.claude/commands/workflows/README.md +62 -6
  170. package/.claude/commands/workflows/development.md +69 -61
  171. package/.claude/commands/workflows/research.md +73 -47
  172. package/.claude/commands/workflows/workflow-create.md +75 -16
  173. package/.claude/commands/workflows/workflow-execute.md +94 -16
  174. package/.claude/commands/workflows/workflow-export.md +81 -16
  175. package/.claude/helpers/control-start.cjs +91 -0
  176. package/.claude/helpers/extras-registry.json +4104 -1991
  177. package/.claude/helpers/graphify-freshen.cjs +44 -13
  178. package/.claude/helpers/hook-handler.cjs +256 -1
  179. package/.claude/helpers/learning-service.mjs +0 -0
  180. package/.claude/helpers/loop-tracker.cjs +107 -0
  181. package/.claude/helpers/metrics-db.mjs +0 -0
  182. package/.claude/helpers/router.cjs +48 -68
  183. package/.claude/helpers/skill-registry.json +89 -104
  184. package/.claude/helpers/statusline.cjs +33 -2
  185. package/.claude/helpers/swarm-hooks.sh +0 -0
  186. package/.claude/skills/.monomind/data/ranked-context.json +5 -0
  187. package/.claude/skills/.monomind/sessions/current.json +13 -0
  188. package/.claude/skills/.monomind/sessions/session-1777829336455.json +15 -0
  189. package/.claude/skills/.monomind/sessions/session-1777831614725.json +15 -0
  190. package/.claude/skills/.monomind/sessions/session-1777832095857.json +15 -0
  191. package/.claude/skills/.monomind/sessions/session-1777839814183.json +15 -0
  192. package/.claude/skills/.monomind/sessions/session-1777841847131.json +15 -0
  193. package/.claude/skills/.monomind/sessions/session-1777843309463.json +15 -0
  194. package/.claude/skills/.monomind/sessions/session-1777880867159.json +15 -0
  195. package/.claude/skills/.monomind/sessions/session-1777881884593.json +15 -0
  196. package/.claude/skills/.monomind/sessions/session-1777884090471.json +15 -0
  197. package/.claude/skills/.monomind/sessions/session-1777884808221.json +15 -0
  198. package/.claude/skills/.monomind/sessions/session-1777885672155.json +15 -0
  199. package/.claude/skills/.monomind/sessions/session-1777886852818.json +15 -0
  200. package/.claude/skills/.monomind/sessions/session-1777896532690.json +15 -0
  201. package/.claude/skills/agentdb-advanced/SKILL.md +11 -12
  202. package/.claude/skills/agentdb-learning/SKILL.md +20 -21
  203. package/.claude/skills/agentdb-memory-patterns/SKILL.md +28 -30
  204. package/.claude/skills/agentdb-optimization/SKILL.md +11 -12
  205. package/.claude/skills/agentdb-vector-search/SKILL.md +37 -41
  206. package/.claude/skills/{v3-integration-deep → agentic-integration}/SKILL.md +20 -13
  207. package/.claude/skills/agentic-jujutsu/SKILL.md +22 -22
  208. package/.claude/skills/{v3-cli-modernization → cli-modernization}/SKILL.md +17 -8
  209. package/.claude/skills/{v3-core-implementation → core-implementation}/SKILL.md +33 -8
  210. package/.claude/skills/{v3-ddd-architecture → ddd-architecture}/SKILL.md +18 -25
  211. package/.claude/skills/github-code-review/SKILL.md +82 -83
  212. package/.claude/skills/github-multi-repo/SKILL.md +42 -46
  213. package/.claude/skills/github-project-management/SKILL.md +83 -88
  214. package/.claude/skills/github-release-management/SKILL.md +12 -18
  215. package/.claude/skills/github-workflow-automation/SKILL.md +70 -74
  216. package/.claude/skills/hooks-automation/SKILL.md +9 -13
  217. package/.claude/skills/mastermind/_intake.md +83 -0
  218. package/.claude/skills/mastermind/_protocol.md +275 -0
  219. package/.claude/skills/mastermind/architect.md +847 -0
  220. package/.claude/skills/mastermind/build.md +158 -0
  221. package/.claude/skills/mastermind/content.md +185 -0
  222. package/.claude/skills/mastermind/createorg.md +318 -0
  223. package/.claude/skills/mastermind/finance.md +154 -0
  224. package/.claude/skills/mastermind/idea.md +158 -0
  225. package/.claude/skills/mastermind/marketing.md +216 -0
  226. package/.claude/skills/mastermind/monotask.md +350 -0
  227. package/.claude/skills/mastermind/ops.md +156 -0
  228. package/.claude/skills/mastermind/references/copywriting-frameworks.md +181 -0
  229. package/.claude/skills/mastermind/references/persuasion-psychology.md +158 -0
  230. package/.claude/skills/mastermind/release.md +156 -0
  231. package/.claude/skills/mastermind/research.md +156 -0
  232. package/.claude/skills/mastermind/review.md +157 -0
  233. package/.claude/skills/mastermind/runorg.md +308 -0
  234. package/.claude/skills/mastermind/sales.md +158 -0
  235. package/.claude/skills/mastermind/techport.md +743 -0
  236. package/.claude/skills/{v3-mcp-optimization → mcp-optimization}/SKILL.md +35 -14
  237. package/.claude/skills/{v3-memory-unification → memory-unification}/SKILL.md +20 -4
  238. package/.claude/skills/monodesign/SKILL.md +302 -0
  239. package/.claude/skills/monodesign/reference/adapt.md +190 -0
  240. package/.claude/skills/monodesign/reference/animate.md +175 -0
  241. package/.claude/skills/monodesign/reference/antipatterns-catalog.md +187 -0
  242. package/.claude/skills/monodesign/reference/audit.md +133 -0
  243. package/.claude/skills/monodesign/reference/bolder.md +113 -0
  244. package/.claude/skills/monodesign/reference/brand-workflow.md +180 -0
  245. package/.claude/skills/monodesign/reference/brand.md +114 -0
  246. package/.claude/skills/monodesign/reference/clarify.md +174 -0
  247. package/.claude/skills/monodesign/reference/cognitive-load.md +106 -0
  248. package/.claude/skills/monodesign/reference/color-and-contrast.md +105 -0
  249. package/.claude/skills/monodesign/reference/colorize.md +154 -0
  250. package/.claude/skills/monodesign/reference/component-specs.md +260 -0
  251. package/.claude/skills/monodesign/reference/component-states.md +274 -0
  252. package/.claude/skills/monodesign/reference/component-system.md +358 -0
  253. package/.claude/skills/monodesign/reference/copy-formulas.md +160 -0
  254. package/.claude/skills/monodesign/reference/craft.md +193 -0
  255. package/.claude/skills/monodesign/reference/critique.md +213 -0
  256. package/.claude/skills/monodesign/reference/delight.md +302 -0
  257. package/.claude/skills/monodesign/reference/design-principles.md +246 -0
  258. package/.claude/skills/monodesign/reference/distill.md +111 -0
  259. package/.claude/skills/monodesign/reference/document.md +427 -0
  260. package/.claude/skills/monodesign/reference/extract.md +69 -0
  261. package/.claude/skills/monodesign/reference/harden.md +347 -0
  262. package/.claude/skills/monodesign/reference/heuristics-scoring.md +234 -0
  263. package/.claude/skills/monodesign/reference/image-prompts.md +118 -0
  264. package/.claude/skills/monodesign/reference/interaction-design.md +195 -0
  265. package/.claude/skills/monodesign/reference/layout.md +141 -0
  266. package/.claude/skills/monodesign/reference/live.md +622 -0
  267. package/.claude/skills/monodesign/reference/motion-design.md +109 -0
  268. package/.claude/skills/monodesign/reference/onboard.md +234 -0
  269. package/.claude/skills/monodesign/reference/optimize.md +258 -0
  270. package/.claude/skills/monodesign/reference/overdrive.md +130 -0
  271. package/.claude/skills/monodesign/reference/personas.md +179 -0
  272. package/.claude/skills/monodesign/reference/polish.md +233 -0
  273. package/.claude/skills/monodesign/reference/pre-delivery-checklist.md +108 -0
  274. package/.claude/skills/monodesign/reference/product.md +62 -0
  275. package/.claude/skills/monodesign/reference/quieter.md +99 -0
  276. package/.claude/skills/monodesign/reference/responsive-design.md +114 -0
  277. package/.claude/skills/monodesign/reference/shape.md +151 -0
  278. package/.claude/skills/monodesign/reference/spatial-design.md +100 -0
  279. package/.claude/skills/monodesign/reference/teach.md +156 -0
  280. package/.claude/skills/monodesign/reference/token-architecture.md +222 -0
  281. package/.claude/skills/monodesign/reference/typeset.md +124 -0
  282. package/.claude/skills/monodesign/reference/typography.md +159 -0
  283. package/.claude/skills/monodesign/reference/ux-research.md +143 -0
  284. package/.claude/skills/monodesign/reference/ux-rules.md +211 -0
  285. package/.claude/skills/monodesign/reference/ux-writing.md +107 -0
  286. package/.claude/skills/monomotion/SKILL.md +145 -0
  287. package/.claude/skills/monomotion/rules/api-control.md +139 -0
  288. package/.claude/skills/monomotion/rules/effects.md +109 -0
  289. package/.claude/skills/monomotion/rules/integration.md +140 -0
  290. package/.claude/skills/monomotion/rules/scroll.md +131 -0
  291. package/.claude/skills/monomotion/rules/sequencing.md +105 -0
  292. package/.claude/skills/monomotion/rules/svg.md +101 -0
  293. package/.claude/skills/monomotion/rules/text.md +119 -0
  294. package/.claude/skills/pair-programming/SKILL.md +1 -1
  295. package/.claude/skills/performance-analysis/SKILL.md +3 -3
  296. package/.claude/skills/{v3-performance-optimization → performance-optimization}/SKILL.md +16 -8
  297. package/.claude/skills/reasoningbank-agentdb/SKILL.md +17 -19
  298. package/.claude/skills/reasoningbank-intelligence/SKILL.md +4 -6
  299. package/.claude/skills/{v3-security-overhaul → security-hardening}/SKILL.md +13 -3
  300. package/.claude/skills/skill-builder/SKILL.md +19 -19
  301. package/.claude/skills/sparc-methodology/SKILL.md +55 -211
  302. package/.claude/skills/stop-slop/SKILL.md +67 -0
  303. package/.claude/skills/stop-slop/references/examples.md +61 -0
  304. package/.claude/skills/stop-slop/references/phrases.md +130 -0
  305. package/.claude/skills/stop-slop/references/structures.md +136 -0
  306. package/.claude/skills/swarm-advanced/SKILL.md +13 -43
  307. package/.claude/skills/{v3-swarm-coordination → swarm-coordination}/SKILL.md +39 -21
  308. package/.claude/skills/swarm-orchestration/SKILL.md +12 -12
  309. package/.claude/skills/verification-quality/SKILL.md +5 -5
  310. package/README.md +5 -5
  311. package/bin/cli.js +78 -13
  312. package/dist/src/agents/halt-signal.d.ts.map +1 -1
  313. package/dist/src/agents/halt-signal.js +33 -7
  314. package/dist/src/agents/halt-signal.js.map +1 -1
  315. package/dist/src/agents/managed-agent.d.ts.map +1 -1
  316. package/dist/src/agents/managed-agent.js +5 -2
  317. package/dist/src/agents/managed-agent.js.map +1 -1
  318. package/dist/src/agents/prompt-experiment.d.ts +3 -2
  319. package/dist/src/agents/prompt-experiment.d.ts.map +1 -1
  320. package/dist/src/agents/prompt-experiment.js +1 -1
  321. package/dist/src/agents/prompt-experiment.js.map +1 -1
  322. package/dist/src/agents/prompt-version-manager.d.ts +5 -2
  323. package/dist/src/agents/prompt-version-manager.d.ts.map +1 -1
  324. package/dist/src/agents/prompt-version-manager.js +26 -4
  325. package/dist/src/agents/prompt-version-manager.js.map +1 -1
  326. package/dist/src/agents/specialization-scorer.d.ts.map +1 -1
  327. package/dist/src/agents/specialization-scorer.js +17 -9
  328. package/dist/src/agents/specialization-scorer.js.map +1 -1
  329. package/dist/src/agents/trigger-scanner.d.ts +5 -3
  330. package/dist/src/agents/trigger-scanner.d.ts.map +1 -1
  331. package/dist/src/agents/trigger-scanner.js +58 -10
  332. package/dist/src/agents/trigger-scanner.js.map +1 -1
  333. package/dist/src/agents/version-store.d.ts +0 -1
  334. package/dist/src/agents/version-store.d.ts.map +1 -1
  335. package/dist/src/agents/version-store.js +44 -21
  336. package/dist/src/agents/version-store.js.map +1 -1
  337. package/dist/src/autopilot-state.d.ts.map +1 -1
  338. package/dist/src/autopilot-state.js +79 -28
  339. package/dist/src/autopilot-state.js.map +1 -1
  340. package/dist/src/benchmarks/benchmark-runner.d.ts +7 -2
  341. package/dist/src/benchmarks/benchmark-runner.d.ts.map +1 -1
  342. package/dist/src/benchmarks/benchmark-runner.js +20 -8
  343. package/dist/src/benchmarks/benchmark-runner.js.map +1 -1
  344. package/dist/src/benchmarks/metric-evaluators.d.ts +2 -1
  345. package/dist/src/benchmarks/metric-evaluators.d.ts.map +1 -1
  346. package/dist/src/benchmarks/metric-evaluators.js +25 -2
  347. package/dist/src/benchmarks/metric-evaluators.js.map +1 -1
  348. package/dist/src/commands/agent.d.ts.map +1 -1
  349. package/dist/src/commands/agent.js +6 -4
  350. package/dist/src/commands/agent.js.map +1 -1
  351. package/dist/src/commands/appliance-advanced.d.ts.map +1 -1
  352. package/dist/src/commands/appliance-advanced.js +23 -0
  353. package/dist/src/commands/appliance-advanced.js.map +1 -1
  354. package/dist/src/commands/autopilot.d.ts.map +1 -1
  355. package/dist/src/commands/autopilot.js +3 -3
  356. package/dist/src/commands/autopilot.js.map +1 -1
  357. package/dist/src/commands/benchmark.d.ts.map +1 -1
  358. package/dist/src/commands/benchmark.js +119 -8
  359. package/dist/src/commands/benchmark.js.map +1 -1
  360. package/dist/src/commands/claims.d.ts.map +1 -1
  361. package/dist/src/commands/claims.js +22 -14
  362. package/dist/src/commands/claims.js.map +1 -1
  363. package/dist/src/commands/config.d.ts.map +1 -1
  364. package/dist/src/commands/config.js +32 -0
  365. package/dist/src/commands/config.js.map +1 -1
  366. package/dist/src/commands/daemon.d.ts.map +1 -1
  367. package/dist/src/commands/daemon.js +13 -11
  368. package/dist/src/commands/daemon.js.map +1 -1
  369. package/dist/src/commands/deployment.d.ts.map +1 -1
  370. package/dist/src/commands/deployment.js +21 -2
  371. package/dist/src/commands/deployment.js.map +1 -1
  372. package/dist/src/commands/doctor.d.ts.map +1 -1
  373. package/dist/src/commands/doctor.js +5 -4
  374. package/dist/src/commands/doctor.js.map +1 -1
  375. package/dist/src/commands/embeddings.d.ts.map +1 -1
  376. package/dist/src/commands/embeddings.js +124 -48
  377. package/dist/src/commands/embeddings.js.map +1 -1
  378. package/dist/src/commands/hive-mind.d.ts.map +1 -1
  379. package/dist/src/commands/hive-mind.js +15 -14
  380. package/dist/src/commands/hive-mind.js.map +1 -1
  381. package/dist/src/commands/hooks.d.ts.map +1 -1
  382. package/dist/src/commands/hooks.js +45 -41
  383. package/dist/src/commands/hooks.js.map +1 -1
  384. package/dist/src/commands/index.d.ts +2 -0
  385. package/dist/src/commands/index.d.ts.map +1 -1
  386. package/dist/src/commands/index.js +20 -7
  387. package/dist/src/commands/index.js.map +1 -1
  388. package/dist/src/commands/init.d.ts.map +1 -1
  389. package/dist/src/commands/init.js +53 -19
  390. package/dist/src/commands/init.js.map +1 -1
  391. package/dist/src/commands/mcp.d.ts.map +1 -1
  392. package/dist/src/commands/mcp.js +31 -44
  393. package/dist/src/commands/mcp.js.map +1 -1
  394. package/dist/src/commands/memory.d.ts.map +1 -1
  395. package/dist/src/commands/memory.js +47 -15
  396. package/dist/src/commands/memory.js.map +1 -1
  397. package/dist/src/commands/migrate.d.ts.map +1 -1
  398. package/dist/src/commands/migrate.js +156 -108
  399. package/dist/src/commands/migrate.js.map +1 -1
  400. package/dist/src/commands/monograph.d.ts +8 -0
  401. package/dist/src/commands/monograph.d.ts.map +1 -0
  402. package/dist/src/commands/monograph.js +526 -0
  403. package/dist/src/commands/monograph.js.map +1 -0
  404. package/dist/src/commands/neural.d.ts.map +1 -1
  405. package/dist/src/commands/neural.js +96 -56
  406. package/dist/src/commands/neural.js.map +1 -1
  407. package/dist/src/commands/performance.d.ts.map +1 -1
  408. package/dist/src/commands/performance.js +30 -8
  409. package/dist/src/commands/performance.js.map +1 -1
  410. package/dist/src/commands/plugins.d.ts.map +1 -1
  411. package/dist/src/commands/plugins.js +13 -37
  412. package/dist/src/commands/plugins.js.map +1 -1
  413. package/dist/src/commands/process.d.ts.map +1 -1
  414. package/dist/src/commands/process.js +25 -2
  415. package/dist/src/commands/process.js.map +1 -1
  416. package/dist/src/commands/providers.d.ts.map +1 -1
  417. package/dist/src/commands/providers.js +37 -5
  418. package/dist/src/commands/providers.js.map +1 -1
  419. package/dist/src/commands/replay.js +4 -4
  420. package/dist/src/commands/replay.js.map +1 -1
  421. package/dist/src/commands/route.d.ts.map +1 -1
  422. package/dist/src/commands/route.js +37 -5
  423. package/dist/src/commands/route.js.map +1 -1
  424. package/dist/src/commands/ruvector/import.d.ts.map +1 -1
  425. package/dist/src/commands/ruvector/import.js +12 -2
  426. package/dist/src/commands/ruvector/import.js.map +1 -1
  427. package/dist/src/commands/ruvector/init.d.ts.map +1 -1
  428. package/dist/src/commands/ruvector/init.js +15 -0
  429. package/dist/src/commands/ruvector/init.js.map +1 -1
  430. package/dist/src/commands/ruvector/status.d.ts.map +1 -1
  431. package/dist/src/commands/ruvector/status.js +16 -3
  432. package/dist/src/commands/ruvector/status.js.map +1 -1
  433. package/dist/src/commands/security.d.ts.map +1 -1
  434. package/dist/src/commands/security.js +342 -193
  435. package/dist/src/commands/security.js.map +1 -1
  436. package/dist/src/commands/session.d.ts.map +1 -1
  437. package/dist/src/commands/session.js +51 -8
  438. package/dist/src/commands/session.js.map +1 -1
  439. package/dist/src/commands/start.d.ts.map +1 -1
  440. package/dist/src/commands/start.js +18 -4
  441. package/dist/src/commands/start.js.map +1 -1
  442. package/dist/src/commands/swarm.d.ts.map +1 -1
  443. package/dist/src/commands/swarm.js +47 -36
  444. package/dist/src/commands/swarm.js.map +1 -1
  445. package/dist/src/commands/tokens.js +11 -11
  446. package/dist/src/commands/tokens.js.map +1 -1
  447. package/dist/src/commands/transfer-store.js +1 -1
  448. package/dist/src/commands/transfer-store.js.map +1 -1
  449. package/dist/src/commands/workflow.d.ts.map +1 -1
  450. package/dist/src/commands/workflow.js +31 -4
  451. package/dist/src/commands/workflow.js.map +1 -1
  452. package/dist/src/config-adapter.d.ts +2 -1
  453. package/dist/src/config-adapter.d.ts.map +1 -1
  454. package/dist/src/config-adapter.js.map +1 -1
  455. package/dist/src/consensus/audit-writer.d.ts.map +1 -1
  456. package/dist/src/consensus/audit-writer.js +46 -13
  457. package/dist/src/consensus/audit-writer.js.map +1 -1
  458. package/dist/src/consensus/vote-signer.d.ts +0 -3
  459. package/dist/src/consensus/vote-signer.d.ts.map +1 -1
  460. package/dist/src/consensus/vote-signer.js +9 -1
  461. package/dist/src/consensus/vote-signer.js.map +1 -1
  462. package/dist/src/dlq/dlq-reader.d.ts +4 -2
  463. package/dist/src/dlq/dlq-reader.d.ts.map +1 -1
  464. package/dist/src/dlq/dlq-reader.js +25 -8
  465. package/dist/src/dlq/dlq-reader.js.map +1 -1
  466. package/dist/src/dlq/dlq-replayer.d.ts +10 -3
  467. package/dist/src/dlq/dlq-replayer.d.ts.map +1 -1
  468. package/dist/src/dlq/dlq-replayer.js +50 -16
  469. package/dist/src/dlq/dlq-replayer.js.map +1 -1
  470. package/dist/src/dlq/dlq-writer.d.ts.map +1 -1
  471. package/dist/src/dlq/dlq-writer.js +27 -5
  472. package/dist/src/dlq/dlq-writer.js.map +1 -1
  473. package/dist/src/eval/dataset-manager.d.ts +2 -2
  474. package/dist/src/eval/dataset-manager.d.ts.map +1 -1
  475. package/dist/src/eval/dataset-manager.js +26 -16
  476. package/dist/src/eval/dataset-manager.js.map +1 -1
  477. package/dist/src/eval/trace-collector.d.ts.map +1 -1
  478. package/dist/src/eval/trace-collector.js +23 -3
  479. package/dist/src/eval/trace-collector.js.map +1 -1
  480. package/dist/src/index.d.ts.map +1 -1
  481. package/dist/src/index.js +12 -10
  482. package/dist/src/index.js.map +1 -1
  483. package/dist/src/init/claudemd-generator.js +8 -8
  484. package/dist/src/init/claudemd-generator.js.map +1 -1
  485. package/dist/src/init/executor.d.ts.map +1 -1
  486. package/dist/src/init/executor.js +153 -70
  487. package/dist/src/init/executor.js.map +1 -1
  488. package/dist/src/init/helpers-generator.d.ts.map +1 -1
  489. package/dist/src/init/helpers-generator.js +35 -22
  490. package/dist/src/init/helpers-generator.js.map +1 -1
  491. package/dist/src/init/mcp-generator.js +3 -3
  492. package/dist/src/init/mcp-generator.js.map +1 -1
  493. package/dist/src/init/settings-generator.d.ts.map +1 -1
  494. package/dist/src/init/settings-generator.js +10 -3
  495. package/dist/src/init/settings-generator.js.map +1 -1
  496. package/dist/src/init/shared-instructions-generator.d.ts.map +1 -1
  497. package/dist/src/init/shared-instructions-generator.js +18 -3
  498. package/dist/src/init/shared-instructions-generator.js.map +1 -1
  499. package/dist/src/init/statusline-generator.d.ts.map +1 -1
  500. package/dist/src/init/statusline-generator.js +3 -1
  501. package/dist/src/init/statusline-generator.js.map +1 -1
  502. package/dist/src/init/types.d.ts +35 -11
  503. package/dist/src/init/types.d.ts.map +1 -1
  504. package/dist/src/init/types.js +5 -9
  505. package/dist/src/init/types.js.map +1 -1
  506. package/dist/src/interactive/interrupt.d.ts.map +1 -1
  507. package/dist/src/interactive/interrupt.js +8 -3
  508. package/dist/src/interactive/interrupt.js.map +1 -1
  509. package/dist/src/mcp/tool-registry.d.ts.map +1 -1
  510. package/dist/src/mcp/tool-registry.js +38 -4
  511. package/dist/src/mcp/tool-registry.js.map +1 -1
  512. package/dist/src/mcp-client.d.ts.map +1 -1
  513. package/dist/src/mcp-client.js +10 -4
  514. package/dist/src/mcp-client.js.map +1 -1
  515. package/dist/src/mcp-server.d.ts +9 -2
  516. package/dist/src/mcp-server.d.ts.map +1 -1
  517. package/dist/src/mcp-server.js +182 -35
  518. package/dist/src/mcp-server.js.map +1 -1
  519. package/dist/src/mcp-tools/agent-tools.d.ts.map +1 -1
  520. package/dist/src/mcp-tools/agent-tools.js +66 -34
  521. package/dist/src/mcp-tools/agent-tools.js.map +1 -1
  522. package/dist/src/mcp-tools/agentdb-tools.d.ts.map +1 -1
  523. package/dist/src/mcp-tools/agentdb-tools.js +34 -7
  524. package/dist/src/mcp-tools/agentdb-tools.js.map +1 -1
  525. package/dist/src/mcp-tools/analyze-tools.d.ts.map +1 -1
  526. package/dist/src/mcp-tools/analyze-tools.js +25 -16
  527. package/dist/src/mcp-tools/analyze-tools.js.map +1 -1
  528. package/dist/src/mcp-tools/auto-install.d.ts.map +1 -1
  529. package/dist/src/mcp-tools/auto-install.js +4 -6
  530. package/dist/src/mcp-tools/auto-install.js.map +1 -1
  531. package/dist/src/mcp-tools/autopilot-tools.d.ts.map +1 -1
  532. package/dist/src/mcp-tools/autopilot-tools.js +12 -2
  533. package/dist/src/mcp-tools/autopilot-tools.js.map +1 -1
  534. package/dist/src/mcp-tools/browser-tools.d.ts.map +1 -1
  535. package/dist/src/mcp-tools/browser-tools.js +199 -20
  536. package/dist/src/mcp-tools/browser-tools.js.map +1 -1
  537. package/dist/src/mcp-tools/claims-tools.d.ts.map +1 -1
  538. package/dist/src/mcp-tools/claims-tools.js +68 -18
  539. package/dist/src/mcp-tools/claims-tools.js.map +1 -1
  540. package/dist/src/mcp-tools/config-tools.d.ts.map +1 -1
  541. package/dist/src/mcp-tools/config-tools.js +33 -5
  542. package/dist/src/mcp-tools/config-tools.js.map +1 -1
  543. package/dist/src/mcp-tools/coordination-tools.d.ts.map +1 -1
  544. package/dist/src/mcp-tools/coordination-tools.js +59 -4
  545. package/dist/src/mcp-tools/coordination-tools.js.map +1 -1
  546. package/dist/src/mcp-tools/daa-tools.d.ts.map +1 -1
  547. package/dist/src/mcp-tools/daa-tools.js +46 -10
  548. package/dist/src/mcp-tools/daa-tools.js.map +1 -1
  549. package/dist/src/mcp-tools/embeddings-tools.d.ts.map +1 -1
  550. package/dist/src/mcp-tools/embeddings-tools.js +46 -5
  551. package/dist/src/mcp-tools/embeddings-tools.js.map +1 -1
  552. package/dist/src/mcp-tools/github-tools.d.ts.map +1 -1
  553. package/dist/src/mcp-tools/github-tools.js +29 -16
  554. package/dist/src/mcp-tools/github-tools.js.map +1 -1
  555. package/dist/src/mcp-tools/guidance-tools.d.ts.map +1 -1
  556. package/dist/src/mcp-tools/guidance-tools.js +38 -10
  557. package/dist/src/mcp-tools/guidance-tools.js.map +1 -1
  558. package/dist/src/mcp-tools/hive-mind-tools.d.ts.map +1 -1
  559. package/dist/src/mcp-tools/hive-mind-tools.js +96 -33
  560. package/dist/src/mcp-tools/hive-mind-tools.js.map +1 -1
  561. package/dist/src/mcp-tools/hooks-tools.d.ts.map +1 -1
  562. package/dist/src/mcp-tools/hooks-tools.js +70 -37
  563. package/dist/src/mcp-tools/hooks-tools.js.map +1 -1
  564. package/dist/src/mcp-tools/memory-tools.d.ts.map +1 -1
  565. package/dist/src/mcp-tools/memory-tools.js +29 -13
  566. package/dist/src/mcp-tools/memory-tools.js.map +1 -1
  567. package/dist/src/mcp-tools/monograph-tools.d.ts.map +1 -1
  568. package/dist/src/mcp-tools/monograph-tools.js +5867 -56
  569. package/dist/src/mcp-tools/monograph-tools.js.map +1 -1
  570. package/dist/src/mcp-tools/neural-tools.d.ts.map +1 -1
  571. package/dist/src/mcp-tools/neural-tools.js +121 -37
  572. package/dist/src/mcp-tools/neural-tools.js.map +1 -1
  573. package/dist/src/mcp-tools/performance-tools.d.ts.map +1 -1
  574. package/dist/src/mcp-tools/performance-tools.js +21 -8
  575. package/dist/src/mcp-tools/performance-tools.js.map +1 -1
  576. package/dist/src/mcp-tools/progress-tools.d.ts.map +1 -1
  577. package/dist/src/mcp-tools/progress-tools.js +10 -8
  578. package/dist/src/mcp-tools/progress-tools.js.map +1 -1
  579. package/dist/src/mcp-tools/request-tracker.d.ts.map +1 -1
  580. package/dist/src/mcp-tools/request-tracker.js +4 -1
  581. package/dist/src/mcp-tools/request-tracker.js.map +1 -1
  582. package/dist/src/mcp-tools/ruvllm-tools.d.ts.map +1 -1
  583. package/dist/src/mcp-tools/ruvllm-tools.js +19 -8
  584. package/dist/src/mcp-tools/ruvllm-tools.js.map +1 -1
  585. package/dist/src/mcp-tools/session-tools.d.ts.map +1 -1
  586. package/dist/src/mcp-tools/session-tools.js +57 -17
  587. package/dist/src/mcp-tools/session-tools.js.map +1 -1
  588. package/dist/src/mcp-tools/swarm-tools.d.ts.map +1 -1
  589. package/dist/src/mcp-tools/swarm-tools.js +35 -17
  590. package/dist/src/mcp-tools/swarm-tools.js.map +1 -1
  591. package/dist/src/mcp-tools/system-tools.d.ts.map +1 -1
  592. package/dist/src/mcp-tools/system-tools.js +4 -3
  593. package/dist/src/mcp-tools/system-tools.js.map +1 -1
  594. package/dist/src/mcp-tools/task-tools.d.ts.map +1 -1
  595. package/dist/src/mcp-tools/task-tools.js +53 -13
  596. package/dist/src/mcp-tools/task-tools.js.map +1 -1
  597. package/dist/src/mcp-tools/terminal-tools.d.ts.map +1 -1
  598. package/dist/src/mcp-tools/terminal-tools.js +63 -14
  599. package/dist/src/mcp-tools/terminal-tools.js.map +1 -1
  600. package/dist/src/mcp-tools/transfer-tools.d.ts.map +1 -1
  601. package/dist/src/mcp-tools/transfer-tools.js +21 -16
  602. package/dist/src/mcp-tools/transfer-tools.js.map +1 -1
  603. package/dist/src/mcp-tools/workflow-tools.d.ts.map +1 -1
  604. package/dist/src/mcp-tools/workflow-tools.js +92 -23
  605. package/dist/src/mcp-tools/workflow-tools.js.map +1 -1
  606. package/dist/src/memory/ewc-consolidation.d.ts.map +1 -1
  607. package/dist/src/memory/ewc-consolidation.js +41 -10
  608. package/dist/src/memory/ewc-consolidation.js.map +1 -1
  609. package/dist/src/memory/intelligence.d.ts +2 -2
  610. package/dist/src/memory/intelligence.d.ts.map +1 -1
  611. package/dist/src/memory/intelligence.js +39 -13
  612. package/dist/src/memory/intelligence.js.map +1 -1
  613. package/dist/src/memory/memory-bridge.d.ts +1 -0
  614. package/dist/src/memory/memory-bridge.d.ts.map +1 -1
  615. package/dist/src/memory/memory-bridge.js +149 -56
  616. package/dist/src/memory/memory-bridge.js.map +1 -1
  617. package/dist/src/memory/memory-initializer.d.ts.map +1 -1
  618. package/dist/src/memory/memory-initializer.js +107 -45
  619. package/dist/src/memory/memory-initializer.js.map +1 -1
  620. package/dist/src/memory/sona-optimizer.d.ts +8 -1
  621. package/dist/src/memory/sona-optimizer.d.ts.map +1 -1
  622. package/dist/src/memory/sona-optimizer.js +25 -8
  623. package/dist/src/memory/sona-optimizer.js.map +1 -1
  624. package/dist/src/observability/replay-reader.d.ts +40 -0
  625. package/dist/src/observability/replay-reader.d.ts.map +1 -0
  626. package/dist/src/observability/replay-reader.js +138 -0
  627. package/dist/src/observability/replay-reader.js.map +1 -0
  628. package/dist/src/orchestration/routing-modes.d.ts.map +1 -1
  629. package/dist/src/orchestration/routing-modes.js +35 -5
  630. package/dist/src/orchestration/routing-modes.js.map +1 -1
  631. package/dist/src/parser.d.ts +8 -0
  632. package/dist/src/parser.d.ts.map +1 -1
  633. package/dist/src/parser.js +48 -14
  634. package/dist/src/parser.js.map +1 -1
  635. package/dist/src/plugins/manager.d.ts.map +1 -1
  636. package/dist/src/plugins/manager.js +112 -19
  637. package/dist/src/plugins/manager.js.map +1 -1
  638. package/dist/src/plugins/store/discovery.d.ts +1 -1
  639. package/dist/src/plugins/store/discovery.d.ts.map +1 -1
  640. package/dist/src/plugins/store/discovery.js +80 -62
  641. package/dist/src/plugins/store/discovery.js.map +1 -1
  642. package/dist/src/production/circuit-breaker.d.ts.map +1 -1
  643. package/dist/src/production/circuit-breaker.js +8 -1
  644. package/dist/src/production/circuit-breaker.js.map +1 -1
  645. package/dist/src/production/error-handler.d.ts +4 -2
  646. package/dist/src/production/error-handler.d.ts.map +1 -1
  647. package/dist/src/production/error-handler.js +27 -5
  648. package/dist/src/production/error-handler.js.map +1 -1
  649. package/dist/src/production/monitoring.d.ts.map +1 -1
  650. package/dist/src/production/monitoring.js +8 -4
  651. package/dist/src/production/monitoring.js.map +1 -1
  652. package/dist/src/production/rate-limiter.d.ts.map +1 -1
  653. package/dist/src/production/rate-limiter.js +30 -22
  654. package/dist/src/production/rate-limiter.js.map +1 -1
  655. package/dist/src/ruvector/agent-wasm.js +2 -2
  656. package/dist/src/ruvector/agent-wasm.js.map +1 -1
  657. package/dist/src/ruvector/coverage-router.d.ts.map +1 -1
  658. package/dist/src/ruvector/coverage-router.js +19 -9
  659. package/dist/src/ruvector/coverage-router.js.map +1 -1
  660. package/dist/src/ruvector/diff-classifier.d.ts +1 -0
  661. package/dist/src/ruvector/diff-classifier.d.ts.map +1 -1
  662. package/dist/src/ruvector/diff-classifier.js +26 -6
  663. package/dist/src/ruvector/diff-classifier.js.map +1 -1
  664. package/dist/src/ruvector/enhanced-model-router.d.ts.map +1 -1
  665. package/dist/src/ruvector/enhanced-model-router.js +24 -2
  666. package/dist/src/ruvector/enhanced-model-router.js.map +1 -1
  667. package/dist/src/ruvector/index.d.ts +1 -2
  668. package/dist/src/ruvector/index.d.ts.map +1 -1
  669. package/dist/src/ruvector/index.js +2 -2
  670. package/dist/src/ruvector/index.js.map +1 -1
  671. package/dist/src/ruvector/model-router.d.ts +4 -2
  672. package/dist/src/ruvector/model-router.d.ts.map +1 -1
  673. package/dist/src/ruvector/model-router.js +30 -6
  674. package/dist/src/ruvector/model-router.js.map +1 -1
  675. package/dist/src/ruvector/moe-router.d.ts +7 -0
  676. package/dist/src/ruvector/moe-router.d.ts.map +1 -1
  677. package/dist/src/ruvector/moe-router.js +35 -12
  678. package/dist/src/ruvector/moe-router.js.map +1 -1
  679. package/dist/src/ruvector/q-learning-router.d.ts +7 -1
  680. package/dist/src/ruvector/q-learning-router.d.ts.map +1 -1
  681. package/dist/src/ruvector/q-learning-router.js +40 -9
  682. package/dist/src/ruvector/q-learning-router.js.map +1 -1
  683. package/dist/src/services/claim-service.d.ts +3 -1
  684. package/dist/src/services/claim-service.d.ts.map +1 -1
  685. package/dist/src/services/claim-service.js +33 -2
  686. package/dist/src/services/claim-service.js.map +1 -1
  687. package/dist/src/services/config-file-manager.d.ts +16 -2
  688. package/dist/src/services/config-file-manager.d.ts.map +1 -1
  689. package/dist/src/services/config-file-manager.js +105 -17
  690. package/dist/src/services/config-file-manager.js.map +1 -1
  691. package/dist/src/services/container-worker-pool.d.ts.map +1 -1
  692. package/dist/src/services/container-worker-pool.js +51 -11
  693. package/dist/src/services/container-worker-pool.js.map +1 -1
  694. package/dist/src/services/headless-worker-executor.d.ts +7 -0
  695. package/dist/src/services/headless-worker-executor.d.ts.map +1 -1
  696. package/dist/src/services/headless-worker-executor.js +188 -45
  697. package/dist/src/services/headless-worker-executor.js.map +1 -1
  698. package/dist/src/services/registry-api.d.ts.map +1 -1
  699. package/dist/src/services/registry-api.js +62 -9
  700. package/dist/src/services/registry-api.js.map +1 -1
  701. package/dist/src/services/ruvector-training.d.ts.map +1 -1
  702. package/dist/src/services/ruvector-training.js +8 -0
  703. package/dist/src/services/ruvector-training.js.map +1 -1
  704. package/dist/src/services/worker-daemon.d.ts +4 -1
  705. package/dist/src/services/worker-daemon.d.ts.map +1 -1
  706. package/dist/src/services/worker-daemon.js +112 -28
  707. package/dist/src/services/worker-daemon.js.map +1 -1
  708. package/dist/src/services/worker-queue.d.ts +9 -2
  709. package/dist/src/services/worker-queue.d.ts.map +1 -1
  710. package/dist/src/services/worker-queue.js +86 -5
  711. package/dist/src/services/worker-queue.js.map +1 -1
  712. package/dist/src/suggest.d.ts.map +1 -1
  713. package/dist/src/suggest.js +9 -0
  714. package/dist/src/suggest.js.map +1 -1
  715. package/dist/src/swarm/flow-enforcer.d.ts +5 -3
  716. package/dist/src/swarm/flow-enforcer.d.ts.map +1 -1
  717. package/dist/src/swarm/flow-enforcer.js +17 -5
  718. package/dist/src/swarm/flow-enforcer.js.map +1 -1
  719. package/dist/src/swarm/flow-visualizer.d.ts +3 -0
  720. package/dist/src/swarm/flow-visualizer.d.ts.map +1 -1
  721. package/dist/src/swarm/flow-visualizer.js +30 -6
  722. package/dist/src/swarm/flow-visualizer.js.map +1 -1
  723. package/dist/src/transfer/anonymization/index.d.ts.map +1 -1
  724. package/dist/src/transfer/anonymization/index.js +5 -3
  725. package/dist/src/transfer/anonymization/index.js.map +1 -1
  726. package/dist/src/transfer/export.d.ts.map +1 -1
  727. package/dist/src/transfer/export.js +5 -3
  728. package/dist/src/transfer/export.js.map +1 -1
  729. package/dist/src/transfer/ipfs/client.d.ts.map +1 -1
  730. package/dist/src/transfer/ipfs/client.js +84 -7
  731. package/dist/src/transfer/ipfs/client.js.map +1 -1
  732. package/dist/src/transfer/ipfs/upload.d.ts.map +1 -1
  733. package/dist/src/transfer/ipfs/upload.js +13 -4
  734. package/dist/src/transfer/ipfs/upload.js.map +1 -1
  735. package/dist/src/transfer/storage/gcs.d.ts.map +1 -1
  736. package/dist/src/transfer/storage/gcs.js +19 -10
  737. package/dist/src/transfer/storage/gcs.js.map +1 -1
  738. package/dist/src/transfer/store/discovery.d.ts +9 -2
  739. package/dist/src/transfer/store/discovery.d.ts.map +1 -1
  740. package/dist/src/transfer/store/discovery.js +68 -13
  741. package/dist/src/transfer/store/discovery.js.map +1 -1
  742. package/dist/src/transfer/store/download.d.ts +15 -6
  743. package/dist/src/transfer/store/download.d.ts.map +1 -1
  744. package/dist/src/transfer/store/download.js +113 -24
  745. package/dist/src/transfer/store/download.js.map +1 -1
  746. package/dist/src/transfer/store/publish.d.ts +1 -1
  747. package/dist/src/transfer/store/publish.d.ts.map +1 -1
  748. package/dist/src/transfer/store/publish.js +13 -14
  749. package/dist/src/transfer/store/publish.js.map +1 -1
  750. package/dist/src/transfer/store/registry.d.ts +3 -3
  751. package/dist/src/transfer/store/registry.d.ts.map +1 -1
  752. package/dist/src/transfer/store/registry.js +32 -16
  753. package/dist/src/transfer/store/registry.js.map +1 -1
  754. package/dist/src/ui/.monomind/sessions/current.json +2 -2
  755. package/dist/src/ui/dashboard.html +2507 -187
  756. package/dist/src/ui/server.mjs +1338 -149
  757. package/dist/src/update/checker.d.ts.map +1 -1
  758. package/dist/src/update/checker.js +17 -4
  759. package/dist/src/update/checker.js.map +1 -1
  760. package/dist/src/update/executor.d.ts.map +1 -1
  761. package/dist/src/update/executor.js +25 -20
  762. package/dist/src/update/executor.js.map +1 -1
  763. package/dist/src/update/rate-limiter.d.ts +11 -0
  764. package/dist/src/update/rate-limiter.d.ts.map +1 -1
  765. package/dist/src/update/rate-limiter.js +23 -3
  766. package/dist/src/update/rate-limiter.js.map +1 -1
  767. package/dist/src/utils/parse-jsonl.d.ts +6 -0
  768. package/dist/src/utils/parse-jsonl.d.ts.map +1 -0
  769. package/dist/src/utils/parse-jsonl.js +22 -0
  770. package/dist/src/utils/parse-jsonl.js.map +1 -0
  771. package/dist/src/workflow/condition-evaluator.d.ts.map +1 -1
  772. package/dist/src/workflow/condition-evaluator.js +37 -3
  773. package/dist/src/workflow/condition-evaluator.js.map +1 -1
  774. package/dist/src/workflow/dag-builder.d.ts.map +1 -1
  775. package/dist/src/workflow/dag-builder.js +27 -11
  776. package/dist/src/workflow/dag-builder.js.map +1 -1
  777. package/dist/src/workflow/dag-executor.d.ts.map +1 -1
  778. package/dist/src/workflow/dag-executor.js +51 -13
  779. package/dist/src/workflow/dag-executor.js.map +1 -1
  780. package/dist/src/workflow/dsl-schema.d.ts +3 -0
  781. package/dist/src/workflow/dsl-schema.d.ts.map +1 -1
  782. package/dist/src/workflow/dsl-schema.js +6 -2
  783. package/dist/src/workflow/dsl-schema.js.map +1 -1
  784. package/dist/src/workflow/template-engine.js +7 -0
  785. package/dist/src/workflow/template-engine.js.map +1 -1
  786. package/dist/src/workflow/workflow-executor.d.ts.map +1 -1
  787. package/dist/src/workflow/workflow-executor.js +95 -14
  788. package/dist/src/workflow/workflow-executor.js.map +1 -1
  789. package/dist/tsconfig.tsbuildinfo +1 -1
  790. package/package.json +15 -12
  791. package/.claude/agents/design/design-brand-guardian.md +0 -323
  792. package/.claude/agents/design/design-image-prompt-engineer.md +0 -237
  793. package/.claude/agents/design/design-inclusive-visuals-specialist.md +0 -72
  794. package/.claude/agents/design/design-ui-designer.md +0 -384
  795. package/.claude/agents/design/design-ux-architect.md +0 -470
  796. package/.claude/agents/design/design-ux-researcher.md +0 -330
  797. package/.claude/agents/design/design-visual-storyteller.md +0 -150
  798. package/.claude/agents/design/design-whimsy-injector.md +0 -439
  799. package/.claude/agents/v3/integration-architect.md +0 -338
  800. package/.claude/commands/analysis/COMMAND_COMPLIANCE_REPORT.md +0 -54
  801. package/.claude/commands/coordination/init.md +0 -44
  802. package/.claude/commands/coordination/orchestrate.md +0 -43
  803. package/.claude/commands/coordination/spawn.md +0 -45
  804. package/.claude/commands/github/code-review-swarm.md +0 -550
  805. package/.claude/commands/github/code-review.md +0 -25
  806. package/.claude/commands/github/github-swarm.md +0 -121
  807. package/.claude/commands/github/issue-triage.md +0 -25
  808. package/.claude/commands/github/multi-repo-swarm.md +0 -519
  809. package/.claude/commands/github/pr-enhance.md +0 -26
  810. package/.claude/commands/github/project-board-sync.md +0 -471
  811. package/.claude/commands/github/release-swarm.md +0 -590
  812. package/.claude/commands/github/repo-analyze.md +0 -25
  813. package/.claude/commands/github/swarm-issue.md +0 -482
  814. package/.claude/commands/github/swarm-pr.md +0 -310
  815. package/.claude/commands/github/workflow-automation.md +0 -468
  816. package/.claude/commands/hive-mind/hive-mind-metrics.md +0 -8
  817. package/.claude/commands/hive-mind/hive-mind-resume.md +0 -8
  818. package/.claude/commands/hive-mind/hive-mind-sessions.md +0 -8
  819. package/.claude/commands/hive-mind/hive-mind-wizard.md +0 -8
  820. package/.claude/commands/list-agents.md +0 -17
  821. package/.claude/commands/memory/memory-persist.md +0 -25
  822. package/.claude/commands/memory/memory-usage.md +0 -25
  823. package/.claude/commands/memory/neural.md +0 -47
  824. package/.claude/commands/metrics.md +0 -11
  825. package/.claude/commands/monitoring/real-time-view.md +0 -25
  826. package/.claude/commands/monitoring/swarm-monitor.md +0 -25
  827. package/.claude/commands/monomind-createtask.md +0 -302
  828. package/.claude/commands/monomind-help.md +0 -103
  829. package/.claude/commands/monomind-memory.md +0 -107
  830. package/.claude/commands/monomind-repeat.md +0 -149
  831. package/.claude/commands/monomind-swarm.md +0 -205
  832. package/.claude/commands/optimization/cache-manage.md +0 -25
  833. package/.claude/commands/optimization/topology-optimize.md +0 -25
  834. package/.claude/commands/pair/commands.md +0 -546
  835. package/.claude/commands/pair/config.md +0 -510
  836. package/.claude/commands/pair/start.md +0 -209
  837. package/.claude/commands/use-agent.md +0 -67
  838. package/.claude/skills/monomind-createtask/SKILL.md +0 -269
  839. package/.claude/skills/monomind-task-engine/SKILL.md +0 -358
  840. package/LICENSE +0 -21
  841. /package/.claude/agents/{v3 → specialists}/memory-specialist.md +0 -0
  842. /package/.claude/agents/{v3 → specialists}/performance-engineer.md +0 -0
  843. /package/.claude/agents/{v3 → specialists}/queen-coordinator.md +0 -0
  844. /package/.claude/agents/{v3 → specialists}/security-architect.md +0 -0
@@ -154,6 +154,9 @@
154
154
  /* Row 3: activity (full width) */
155
155
  #panel-activity { grid-column: 1 / 5; grid-row: 4; }
156
156
 
157
+ /* Row 4: orgs (full width) */
158
+ #panel-orgs { grid-column: 1 / 5; grid-row: 5; }
159
+
157
160
  /* All grid children must not overflow their columns */
158
161
  #grid > * { min-width: 0; }
159
162
 
@@ -304,19 +307,19 @@
304
307
  /* Override display for specific panels that need block */
305
308
  #panel-knowledge.open .panel-body { display: block; }
306
309
 
307
- /* Graphify panel inner layout */
308
- .graphify-layout { display: grid; grid-template-columns: 220px 1fr; gap: 12px; }
309
- .graphify-stats { display: flex; flex-direction: column; gap: 6px; }
310
- .graphify-stat-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
311
- .graphify-stat-key { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
312
- .graphify-stat-val { font-size: 11px; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
313
- .graphify-report { overflow-y: auto; max-height: 320px; }
314
- .graphify-section { margin-bottom: 10px; }
315
- .graphify-section-title { font-size: 9px; font-weight: 700; letter-spacing: 0.08em; color: var(--muted); text-transform: uppercase; margin-bottom: 4px; padding-bottom: 3px; border-bottom: 1px solid var(--border); }
316
- .graphify-section-body { font-size: 10px; color: var(--dim); line-height: 1.6; white-space: pre-wrap; word-break: break-word; }
317
- .graphify-section-body code { font-family: inherit; color: var(--teal); }
318
- .graphify-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; padding: 24px; color: var(--muted); font-size: 11px; text-align: center; }
319
- .graphify-build-cmd { font-family: monospace; font-size: 10px; background: var(--panel-bg); border: 1px solid var(--border); border-radius: 4px; padding: 6px 10px; color: var(--teal); margin-top: 4px; }
310
+ /* Monograph panel inner layout */
311
+ .monograph-layout { display: grid; grid-template-columns: 220px 1fr; gap: 12px; }
312
+ .monograph-stats { display: flex; flex-direction: column; gap: 6px; }
313
+ .monograph-stat-row { display: flex; justify-content: space-between; align-items: center; gap: 8px; }
314
+ .monograph-stat-key { font-size: 10px; color: var(--muted); text-transform: uppercase; letter-spacing: 0.04em; }
315
+ .monograph-stat-val { font-size: 11px; font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
316
+ .monograph-report { overflow-y: auto; max-height: 320px; }
317
+ .monograph-section { margin-bottom: 10px; }
318
+ .monograph-section-title { font-size: 9px; font-weight: 700; letter-spacing: 0.08em; color: var(--muted); text-transform: uppercase; margin-bottom: 4px; padding-bottom: 3px; border-bottom: 1px solid var(--border); }
319
+ .monograph-section-body { font-size: 10px; color: var(--dim); line-height: 1.6; white-space: pre-wrap; word-break: break-word; }
320
+ .monograph-section-body code { font-family: inherit; color: var(--teal); }
321
+ .monograph-empty { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 8px; padding: 24px; color: var(--muted); font-size: 11px; text-align: center; }
322
+ .monograph-build-cmd { font-family: monospace; font-size: 10px; background: var(--panel-bg); border: 1px solid var(--border); border-radius: 4px; padding: 6px 10px; color: var(--teal); margin-top: 4px; }
320
323
 
321
324
  /* Fixed heights for canvas-based panels */
322
325
 
@@ -903,6 +906,78 @@
903
906
  .loop-status-dot.stopped { background: rgba(220,60,60,0.7); }
904
907
  .loops-empty { font-size: 10px; color: var(--dim); text-align: center; padding: 18px 0; letter-spacing: 0.08em; }
905
908
 
909
+ /* ─── ORGS PANEL ─────────────────────────────────────────────────── */
910
+ #orgs-body { display: flex; flex-direction: column; gap: 0; }
911
+ .orgs-empty {
912
+ display: flex; flex-direction: column; align-items: center;
913
+ padding: 22px 16px 18px; gap: 6px; text-align: center;
914
+ }
915
+ .orgs-empty-icon {
916
+ font-size: 22px; line-height: 1;
917
+ animation: orgs-breathe 3.5s ease-in-out infinite;
918
+ opacity: 0.55;
919
+ }
920
+ .orgs-empty-label {
921
+ font-size: 9px; letter-spacing: 0.15em; color: rgba(210,140,255,0.55);
922
+ margin-top: 2px;
923
+ }
924
+ .orgs-empty-sub {
925
+ font-size: 9px; color: var(--dim); line-height: 1.6; max-width: 190px;
926
+ opacity: 0.65;
927
+ }
928
+ .orgs-empty-cmd {
929
+ margin-top: 4px; font-size: 8px; letter-spacing: 0.06em;
930
+ color: rgba(200,120,255,0.6);
931
+ border: 1px solid rgba(200,120,255,0.18);
932
+ border-radius: 4px; padding: 3px 8px;
933
+ background: rgba(200,120,255,0.05);
934
+ animation: orgs-cmd-shimmer 4s ease-in-out infinite;
935
+ cursor: default;
936
+ }
937
+ @keyframes orgs-breathe {
938
+ 0%, 100% { opacity: 0.45; transform: scale(1); }
939
+ 50% { opacity: 0.75; transform: scale(1.08); }
940
+ }
941
+ @keyframes orgs-cmd-shimmer {
942
+ 0%, 100% { color: rgba(200,120,255,0.5); border-color: rgba(200,120,255,0.15); }
943
+ 50% { color: rgba(210,150,255,0.8); border-color: rgba(210,150,255,0.3); }
944
+ }
945
+ .org-row {
946
+ display: grid;
947
+ grid-template-columns: 16px 1fr 80px 56px 80px 90px;
948
+ gap: 0;
949
+ font-size: 10px;
950
+ border-bottom: 1px solid rgba(255,255,255,0.04);
951
+ align-items: center;
952
+ }
953
+ .org-row:hover { background: rgba(0,229,200,0.04); }
954
+ .org-cell { padding: 5px 8px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
955
+ .org-name { color: var(--text); font-weight: 500; font-size: 11px; }
956
+ .org-goal { color: var(--muted); font-size: 9px; }
957
+ .org-status-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--dim); margin: 0 5px; flex-shrink: 0; }
958
+ .org-status-dot.running { background: var(--green); box-shadow: 0 0 5px var(--green); animation: ldp 2s ease-in-out infinite; }
959
+ .org-header { color: var(--dim); font-size: 9px; letter-spacing: 0.12em; text-transform: uppercase; padding: 4px 8px; border-bottom: 1px solid rgba(0,229,200,0.10); }
960
+ .org-btn {
961
+ font-size: 9px; font-family: inherit; letter-spacing: 0.06em;
962
+ border: 1px solid var(--border); background: transparent; color: var(--muted);
963
+ padding: 2px 8px; border-radius: 3px; cursor: pointer; text-transform: uppercase;
964
+ transition: all 0.15s;
965
+ }
966
+ .org-btn:hover { border-color: var(--teal); color: var(--teal); background: var(--glow); }
967
+ .org-btn.stop { border-color: rgba(255,85,119,0.4); color: var(--red); }
968
+ .org-btn.stop:hover { background: rgba(255,85,119,0.08); }
969
+ .org-live-badge {
970
+ font-size: 8px; letter-spacing: 0.1em; font-family: inherit;
971
+ background: rgba(0,229,135,0.12); color: var(--green); border: 1px solid rgba(0,229,135,0.25);
972
+ padding: 1px 6px; border-radius: 2px; text-transform: uppercase;
973
+ }
974
+ .org-event-log {
975
+ font-size: 9px; color: var(--muted); padding: 4px 8px 4px 24px;
976
+ border-bottom: 1px solid rgba(255,255,255,0.03); font-family: inherit; line-height: 1.5;
977
+ }
978
+ .org-event-time { color: var(--dim); margin-right: 6px; }
979
+ .org-event-type { color: var(--teal); margin-right: 6px; font-weight: 500; }
980
+
906
981
  /* ─── METRICS PANEL ─────────────────────────────────────────────── */
907
982
  .metric-block {
908
983
  background: rgba(0,0,0,0.25);
@@ -1135,6 +1210,341 @@
1135
1210
  overflow: hidden;
1136
1211
  }
1137
1212
  #palace-overlay.open { display: flex; }
1213
+
1214
+ /* ─── MASTERMIND OVERLAY ─────────────────────────────────────────── */
1215
+ #mastermind-overlay {
1216
+ position: fixed; inset: 0; z-index: 1002;
1217
+ background: #07071a;
1218
+ display: none; flex-direction: column;
1219
+ overflow: hidden;
1220
+ }
1221
+ #mastermind-overlay.open { display: flex; }
1222
+ #mm-header {
1223
+ display: flex; align-items: center; gap: 14px;
1224
+ padding: 10px 16px; border-bottom: 1px solid rgba(200,120,255,0.18);
1225
+ flex-shrink: 0; background: rgba(7,5,20,0.97);
1226
+ backdrop-filter: blur(16px); z-index: 10; position: relative;
1227
+ }
1228
+ #mm-back {
1229
+ background: none; border: 1px solid rgba(200,120,255,0.4); color: rgba(210,140,255,0.9);
1230
+ font-family: 'Azeret Mono', monospace; font-size: 11px; letter-spacing: 0.08em;
1231
+ padding: 4px 10px; cursor: pointer; border-radius: 3px; transition: background 0.15s;
1232
+ }
1233
+ #mm-back:hover { background: rgba(200,120,255,0.1); }
1234
+ #mm-header-title {
1235
+ font-family: 'Syne', sans-serif; font-weight: 700; font-size: 13px;
1236
+ color: rgba(210,140,255,0.9); letter-spacing: 0.14em; text-transform: uppercase;
1237
+ }
1238
+ #mm-header-sub {
1239
+ font-family: 'Azeret Mono', monospace; font-size: 9px;
1240
+ color: rgba(150,100,200,0.5); letter-spacing: 0.12em;
1241
+ }
1242
+ #mm-frame {
1243
+ flex: 1; border: none; width: 100%; display: block;
1244
+ background: #07071a;
1245
+ }
1246
+
1247
+ /* ─── MONOGRAPH OVERLAY ──────────────────────────────────────────── */
1248
+ #monograph-overlay {
1249
+ position: fixed; inset: 0; z-index: 1001;
1250
+ background: var(--bg);
1251
+ display: none; flex-direction: column;
1252
+ overflow: hidden;
1253
+ }
1254
+ #monograph-overlay.open { display: flex; }
1255
+ #mg-header {
1256
+ display: flex; align-items: center; gap: 14px;
1257
+ padding: 10px 16px; border-bottom: 1px solid var(--border);
1258
+ flex-shrink: 0; background: rgba(7,8,15,0.97);
1259
+ backdrop-filter: blur(16px);
1260
+ }
1261
+ #mg-back {
1262
+ background: none; border: 1px solid rgba(123,97,255,0.4); color: rgba(123,97,255,0.9);
1263
+ font-family: 'Azeret Mono', monospace; font-size: 11px; letter-spacing: 0.08em;
1264
+ padding: 4px 10px; cursor: pointer; border-radius: 3px; transition: background 0.15s;
1265
+ }
1266
+ #mg-back:hover { background: rgba(123,97,255,0.1); }
1267
+ #mg-title {
1268
+ font-family: 'Syne', sans-serif; font-weight: 700; font-size: 13px;
1269
+ color: rgba(123,97,255,0.9); letter-spacing: 0.14em; text-transform: uppercase;
1270
+ }
1271
+ #mg-tabs { display: flex; gap: 2px; margin-left: 4px; }
1272
+ .mg-tab {
1273
+ background: none; border: 1px solid transparent; color: var(--muted);
1274
+ font-family: 'Azeret Mono', monospace; font-size: 10px; letter-spacing: 0.1em;
1275
+ padding: 4px 12px; cursor: pointer; border-radius: 3px; transition: all 0.15s;
1276
+ text-transform: uppercase;
1277
+ }
1278
+ .mg-tab:hover { color: var(--text); border-color: var(--border); }
1279
+ .mg-tab.active { color: rgba(123,97,255,0.9); border-color: rgba(123,97,255,0.4); background: rgba(123,97,255,0.08); }
1280
+ #mg-stats {
1281
+ margin-left: auto; display: flex; gap: 12px; font-family: 'Azeret Mono', monospace;
1282
+ font-size: 9px; color: var(--muted); letter-spacing: 0.08em;
1283
+ }
1284
+ #mg-stats span { color: var(--dim); }
1285
+ #mg-body {
1286
+ flex: 1; overflow: hidden; display: flex; flex-direction: column;
1287
+ }
1288
+ .mg-pane { display: none; flex: 1; overflow: hidden; flex-direction: column; }
1289
+ .mg-pane.active { display: flex; }
1290
+
1291
+ /* Overview tab */
1292
+ #mg-overview { overflow-y: auto; padding: 20px; }
1293
+ .mg-section-title {
1294
+ font-family: 'Syne', sans-serif; font-size: 11px; font-weight: 700;
1295
+ letter-spacing: 0.14em; text-transform: uppercase; color: rgba(123,97,255,0.7);
1296
+ margin: 20px 0 10px; padding-bottom: 4px; border-bottom: 1px solid rgba(123,97,255,0.15);
1297
+ }
1298
+ .mg-section-title:first-child { margin-top: 0; }
1299
+ .mg-feature-grid {
1300
+ display: grid; grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
1301
+ gap: 8px; margin-bottom: 6px;
1302
+ }
1303
+ .mg-feature-card {
1304
+ background: rgba(123,97,255,0.04); border: 1px solid rgba(123,97,255,0.15);
1305
+ border-radius: 5px; padding: 10px 12px; cursor: default;
1306
+ transition: border-color 0.15s, background 0.15s;
1307
+ }
1308
+ .mg-feature-card:hover { border-color: rgba(123,97,255,0.35); background: rgba(123,97,255,0.08); }
1309
+ .mg-card-name {
1310
+ font-family: 'Azeret Mono', monospace; font-size: 10px; font-weight: 700;
1311
+ color: var(--text); letter-spacing: 0.06em; margin-bottom: 4px;
1312
+ }
1313
+ .mg-card-desc {
1314
+ font-family: 'Azeret Mono', monospace; font-size: 9px; color: var(--muted);
1315
+ line-height: 1.5; letter-spacing: 0.02em;
1316
+ }
1317
+ .mg-card-badge {
1318
+ display: inline-block; font-size: 8px; font-family: 'Azeret Mono', monospace;
1319
+ padding: 1px 5px; border-radius: 2px; margin-top: 5px;
1320
+ background: rgba(123,97,255,0.15); color: rgba(123,97,255,0.8);
1321
+ letter-spacing: 0.06em;
1322
+ }
1323
+
1324
+ /* Wiki tab */
1325
+ #mg-wiki-layout { display: flex; flex: 1; overflow: hidden; flex-direction: row; }
1326
+ #mg-wiki-sidebar {
1327
+ width: 280px; flex-shrink: 0; border-right: 1px solid var(--border);
1328
+ display: flex; flex-direction: column; overflow: hidden; background: rgba(0,0,0,0.15);
1329
+ }
1330
+ #mg-wiki-search-bar {
1331
+ padding: 10px 10px 0; flex-shrink: 0;
1332
+ }
1333
+ #mg-wiki-q {
1334
+ width: 100%; background: rgba(255,255,255,0.05); border: 1px solid var(--border);
1335
+ border-radius: 3px; padding: 7px 10px; font-size: 10px; font-family: 'Azeret Mono', monospace;
1336
+ color: var(--text); outline: none; box-sizing: border-box;
1337
+ }
1338
+ #mg-wiki-q:focus { border-color: rgba(0,229,135,0.4); background: rgba(0,229,135,0.04); }
1339
+ #mg-wiki-q::placeholder { color: var(--dim); }
1340
+ #mg-wiki-pills {
1341
+ display: flex; flex-wrap: wrap; gap: 4px; padding: 8px 10px 6px;
1342
+ }
1343
+ .wiki-pill {
1344
+ background: none; border: 1px solid var(--border); border-radius: 2px;
1345
+ color: var(--dim); font-family: 'Azeret Mono', monospace; font-size: 8px;
1346
+ letter-spacing: 0.07em; padding: 2px 7px; cursor: pointer; transition: all 0.12s;
1347
+ text-transform: uppercase;
1348
+ }
1349
+ .wiki-pill:hover { color: var(--text); border-color: rgba(0,229,135,0.3); }
1350
+ .wiki-pill.active { background: rgba(0,229,135,0.12); border-color: rgba(0,229,135,0.5); color: rgba(0,229,135,0.9); }
1351
+ #mg-wiki-list {
1352
+ flex: 1; overflow-y: auto; padding: 4px 6px 12px;
1353
+ }
1354
+ .wiki-card {
1355
+ padding: 8px 10px; border-radius: 3px; cursor: pointer; border: 1px solid transparent;
1356
+ margin-bottom: 3px; transition: all 0.12s;
1357
+ }
1358
+ .wiki-card:hover { background: rgba(255,255,255,0.04); border-color: var(--border); }
1359
+ .wiki-card.selected { background: rgba(0,229,135,0.07); border-color: rgba(0,229,135,0.3); }
1360
+ .wiki-card-header { display: flex; align-items: center; gap: 6px; margin-bottom: 2px; }
1361
+ .wiki-type-badge {
1362
+ font-size: 7px; letter-spacing: 0.1em; padding: 1px 5px; border-radius: 2px;
1363
+ font-family: 'Azeret Mono', monospace; flex-shrink: 0; text-transform: uppercase;
1364
+ }
1365
+ .wiki-card-name { font-size: 10px; color: var(--text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex: 1; }
1366
+ .wiki-card-path { font-size: 8px; color: var(--dim); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1367
+ .wiki-deg { margin-left: auto; font-size: 8px; color: var(--muted); flex-shrink: 0; }
1368
+ #mg-wiki-detail {
1369
+ flex: 1; overflow-y: auto; padding: 20px 24px; font-family: 'Azeret Mono', monospace;
1370
+ }
1371
+ .wiki-detail-header { margin-bottom: 16px; padding-bottom: 14px; border-bottom: 1px solid var(--border); }
1372
+ .wiki-detail-type { font-size: 8px; letter-spacing: 0.12em; text-transform: uppercase; margin-bottom: 6px; }
1373
+ .wiki-detail-name { font-family: 'Syne', sans-serif; font-size: 18px; font-weight: 700; color: var(--text); margin-bottom: 4px; word-break: break-all; }
1374
+ .wiki-detail-path { font-size: 9px; color: var(--dim); }
1375
+ .wiki-detail-path span { color: rgba(0,229,135,0.7); }
1376
+ .wiki-stat-row { display: flex; gap: 16px; margin-bottom: 16px; flex-wrap: wrap; }
1377
+ .wiki-stat { display: flex; flex-direction: column; gap: 2px; }
1378
+ .wiki-stat-label { font-size: 8px; letter-spacing: 0.1em; color: var(--muted); text-transform: uppercase; }
1379
+ .wiki-stat-val { font-size: 14px; font-weight: 700; color: var(--text); }
1380
+ .wiki-section-title { font-size: 8px; letter-spacing: 0.12em; color: var(--muted); text-transform: uppercase; margin-bottom: 8px; margin-top: 16px; padding-top: 12px; border-top: 1px solid var(--border); }
1381
+ .wiki-edge-list { display: flex; flex-direction: column; gap: 4px; }
1382
+ .wiki-edge-item {
1383
+ display: flex; align-items: center; gap: 8px; padding: 6px 10px;
1384
+ background: rgba(255,255,255,0.03); border: 1px solid var(--border);
1385
+ border-radius: 3px; cursor: pointer; transition: all 0.12s;
1386
+ }
1387
+ .wiki-edge-item:hover { background: rgba(255,255,255,0.06); border-color: rgba(123,97,255,0.3); }
1388
+ .wiki-edge-rel { font-size: 7px; letter-spacing: 0.1em; color: var(--dim); text-transform: uppercase; flex-shrink: 0; width: 72px; }
1389
+ .wiki-edge-name { font-size: 10px; color: var(--text); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1390
+ .wiki-edge-type { font-size: 8px; color: var(--muted); flex-shrink: 0; }
1391
+ .wiki-empty { display: flex; align-items: center; justify-content: center; height: 100%; color: var(--border); font-size: 11px; letter-spacing: 0.1em; flex-direction: column; gap: 8px; }
1392
+ /* Content preview */
1393
+ .wiki-content-block {
1394
+ background: rgba(0,0,0,0.25); border: 1px solid var(--border); border-radius: 4px;
1395
+ padding: 10px 12px; font-size: 9px; line-height: 1.7; color: rgba(200,220,255,0.7);
1396
+ overflow: auto; max-height: 220px; white-space: pre-wrap; word-break: break-all;
1397
+ font-family: 'Azeret Mono', monospace;
1398
+ }
1399
+ .wiki-content-block.collapsed { max-height: 80px; overflow: hidden; position: relative; }
1400
+ .wiki-content-block.collapsed::after {
1401
+ content: ''; position: absolute; bottom: 0; left: 0; right: 0; height: 30px;
1402
+ background: linear-gradient(transparent, rgba(10,12,20,0.95));
1403
+ }
1404
+ /* Action buttons row */
1405
+ .wiki-action-row { display: flex; gap: 6px; flex-wrap: wrap; margin-bottom: 4px; }
1406
+ .wiki-action-btn {
1407
+ font-family: 'Azeret Mono', monospace; font-size: 8px; letter-spacing: 0.08em;
1408
+ padding: 4px 10px; border-radius: 3px; cursor: pointer; border: 1px solid;
1409
+ text-transform: uppercase; transition: all 0.12s;
1410
+ }
1411
+ .wiki-action-btn.copy { color: rgba(0,229,135,0.8); border-color: rgba(0,229,135,0.3); background: rgba(0,229,135,0.06); }
1412
+ .wiki-action-btn.copy:hover { background: rgba(0,229,135,0.14); border-color: rgba(0,229,135,0.6); }
1413
+ .wiki-action-btn.explain { color: rgba(123,97,255,0.9); border-color: rgba(123,97,255,0.3); background: rgba(123,97,255,0.06); }
1414
+ .wiki-action-btn.explain:hover { background: rgba(123,97,255,0.15); border-color: rgba(123,97,255,0.6); }
1415
+ .wiki-action-btn.related { color: rgba(251,191,36,0.85); border-color: rgba(251,191,36,0.3); background: rgba(251,191,36,0.06); }
1416
+ .wiki-action-btn.related:hover { background: rgba(251,191,36,0.14); border-color: rgba(251,191,36,0.6); }
1417
+ /* Explain modal */
1418
+ #wiki-explain-modal {
1419
+ display: none; position: fixed; inset: 0; z-index: 9999;
1420
+ background: rgba(0,0,0,0.7); backdrop-filter: blur(6px);
1421
+ align-items: center; justify-content: center;
1422
+ }
1423
+ #wiki-explain-modal.open { display: flex; }
1424
+ .wiki-modal-box {
1425
+ background: #0d0f1a; border: 1px solid rgba(123,97,255,0.4); border-radius: 8px;
1426
+ padding: 20px 24px; max-width: 640px; width: 90%; max-height: 80vh;
1427
+ display: flex; flex-direction: column; gap: 12px;
1428
+ box-shadow: 0 8px 40px rgba(0,0,0,0.6);
1429
+ }
1430
+ .wiki-modal-title { font-size: 9px; letter-spacing: 0.15em; color: rgba(123,97,255,0.8); text-transform: uppercase; }
1431
+ .wiki-modal-body {
1432
+ font-size: 10px; line-height: 1.7; color: rgba(180,200,240,0.85);
1433
+ background: rgba(0,0,0,0.25); border: 1px solid var(--border); border-radius: 4px;
1434
+ padding: 12px; overflow-y: auto; flex: 1; white-space: pre-wrap; word-break: break-word;
1435
+ font-family: 'Azeret Mono', monospace;
1436
+ }
1437
+ .wiki-modal-actions { display: flex; gap: 8px; justify-content: flex-end; }
1438
+ /* FTS search mode indicator */
1439
+ .wiki-search-mode {
1440
+ font-size: 7px; color: var(--muted); letter-spacing: 0.1em; text-align: right;
1441
+ padding: 2px 10px 0; min-height: 14px;
1442
+ }
1443
+ /* Related distance badge */
1444
+ .wiki-dist-badge {
1445
+ font-size: 7px; padding: 1px 5px; border-radius: 2px; margin-left: auto;
1446
+ background: rgba(251,191,36,0.12); color: rgba(251,191,36,0.8);
1447
+ font-family: 'Azeret Mono', monospace; flex-shrink: 0;
1448
+ }
1449
+ #mg-wiki-statsbar {
1450
+ display: flex; gap: 0; flex-shrink: 0; border-bottom: 1px solid var(--border);
1451
+ font-family: 'Azeret Mono', monospace;
1452
+ }
1453
+ .wiki-stat-cell {
1454
+ flex: 1; padding: 7px 10px; border-right: 1px solid var(--border);
1455
+ display: flex; flex-direction: column; gap: 1px;
1456
+ }
1457
+ .wiki-stat-cell:last-child { border-right: none; }
1458
+ .wiki-stat-cell-label { font-size: 7px; letter-spacing: 0.1em; color: var(--dim); text-transform: uppercase; }
1459
+ .wiki-stat-cell-val { font-size: 11px; font-weight: 700; color: rgba(0,229,135,0.9); }
1460
+
1461
+ /* Analyze tab */
1462
+ #mg-analyze { display: flex; flex-direction: row; flex: 1; overflow: hidden; }
1463
+ #mg-analyze-sidebar {
1464
+ width: 220px; flex-shrink: 0; border-right: 1px solid var(--border);
1465
+ overflow-y: auto; padding: 12px 8px;
1466
+ display: flex; flex-direction: column; gap: 4px;
1467
+ }
1468
+ .mg-analyze-btn {
1469
+ background: none; border: 1px solid var(--border); color: var(--dim);
1470
+ font-family: 'Azeret Mono', monospace; font-size: 9px; letter-spacing: 0.08em;
1471
+ padding: 7px 10px; cursor: pointer; border-radius: 3px; text-align: left;
1472
+ transition: all 0.15s; text-transform: uppercase; width: 100%;
1473
+ }
1474
+ .mg-analyze-btn:hover { color: var(--text); border-color: rgba(123,97,255,0.4); background: rgba(123,97,255,0.06); }
1475
+ .mg-analyze-btn.active { color: rgba(123,97,255,0.9); border-color: rgba(123,97,255,0.5); background: rgba(123,97,255,0.1); }
1476
+ #mg-analyze-result {
1477
+ flex: 1; overflow-y: auto; padding: 16px 20px;
1478
+ font-family: 'Azeret Mono', monospace; font-size: 10px; color: var(--dim);
1479
+ line-height: 1.7;
1480
+ }
1481
+
1482
+ /* Query tab */
1483
+ #mg-query-pane { overflow-y: auto; padding: 16px 20px; }
1484
+ .mg-query-section { margin-bottom: 24px; }
1485
+ .mg-query-label {
1486
+ font-family: 'Syne', sans-serif; font-size: 9px; font-weight: 700;
1487
+ letter-spacing: 0.12em; text-transform: uppercase; color: var(--muted);
1488
+ margin-bottom: 8px;
1489
+ }
1490
+ .mg-input-row { display: flex; gap: 8px; align-items: center; margin-bottom: 8px; }
1491
+ .mg-input {
1492
+ flex: 1; background: rgba(255,255,255,0.04); border: 1px solid var(--border);
1493
+ border-radius: 3px; padding: 7px 10px; color: var(--text);
1494
+ font-family: 'Azeret Mono', monospace; font-size: 11px; outline: none;
1495
+ }
1496
+ .mg-input:focus { border-color: rgba(123,97,255,0.5); }
1497
+ .mg-action-btn {
1498
+ background: rgba(123,97,255,0.12); border: 1px solid rgba(123,97,255,0.35);
1499
+ color: rgba(123,97,255,0.9); font-family: 'Azeret Mono', monospace;
1500
+ font-size: 9px; letter-spacing: 0.08em; padding: 5px 14px;
1501
+ cursor: pointer; border-radius: 3px; transition: all 0.15s; white-space: nowrap;
1502
+ }
1503
+ .mg-action-btn:hover { background: rgba(123,97,255,0.2); }
1504
+ .mg-result-box {
1505
+ background: rgba(0,0,0,0.3); border: 1px solid var(--border); border-radius: 4px;
1506
+ padding: 12px 14px; font-size: 10px; color: var(--dim); line-height: 1.7;
1507
+ white-space: pre-wrap; word-break: break-word; max-height: 300px; overflow-y: auto;
1508
+ min-height: 40px;
1509
+ }
1510
+
1511
+ /* Export tab */
1512
+ #mg-export-pane { overflow-y: auto; padding: 20px; }
1513
+ .mg-export-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 10px; margin-top: 12px; }
1514
+ .mg-export-card {
1515
+ background: rgba(255,255,255,0.02); border: 1px solid var(--border);
1516
+ border-radius: 5px; padding: 14px 16px; cursor: pointer; transition: all 0.15s;
1517
+ }
1518
+ .mg-export-card:hover { border-color: rgba(123,97,255,0.4); background: rgba(123,97,255,0.06); }
1519
+ .mg-export-name {
1520
+ font-family: 'Syne', sans-serif; font-size: 12px; font-weight: 700;
1521
+ color: var(--text); letter-spacing: 0.06em; margin-bottom: 4px;
1522
+ }
1523
+ .mg-export-desc { font-family: 'Azeret Mono', monospace; font-size: 9px; color: var(--muted); line-height: 1.5; }
1524
+ .mg-export-ext {
1525
+ display: inline-block; margin-top: 8px; font-size: 8px; font-family: 'Azeret Mono', monospace;
1526
+ padding: 2px 6px; border-radius: 2px; background: rgba(0,229,200,0.1); color: var(--teal);
1527
+ letter-spacing: 0.08em;
1528
+ }
1529
+ #mg-export-status { margin-top: 12px; font-family: 'Azeret Mono', monospace; font-size: 10px; color: var(--muted); min-height: 20px; }
1530
+
1531
+ /* Report tab */
1532
+ #mg-report-pane { overflow-y: auto; padding: 20px; }
1533
+ #mg-report-content { }
1534
+ .mg-report-card {
1535
+ background: rgba(255,255,255,0.02); border: 1px solid var(--border);
1536
+ border-radius: 5px; padding: 14px 16px; margin-bottom: 10px;
1537
+ }
1538
+ .mg-report-card-title {
1539
+ font-family: 'Syne', sans-serif; font-size: 10px; font-weight: 700;
1540
+ letter-spacing: 0.12em; text-transform: uppercase; color: rgba(123,97,255,0.8);
1541
+ margin-bottom: 8px;
1542
+ }
1543
+ .mg-report-card-body {
1544
+ font-family: 'Azeret Mono', monospace; font-size: 10px; color: var(--dim);
1545
+ line-height: 1.7; white-space: pre-wrap; word-break: break-word;
1546
+ }
1547
+
1138
1548
  #po-header {
1139
1549
  display: flex; align-items: center; gap: 14px;
1140
1550
  padding: 10px 16px; border-bottom: 1px solid var(--border);
@@ -1161,6 +1571,27 @@
1161
1571
  .po-tab:hover { color: var(--text); border-color: var(--border); }
1162
1572
  .po-tab.active { color: var(--teal); border-color: rgba(0,229,200,0.35); background: rgba(0,229,200,0.06); }
1163
1573
  #po-stats { margin-left: auto; font-size: 10px; color: var(--muted); display: flex; gap: 16px; }
1574
+
1575
+ /* Usage pane */
1576
+ #po-usage-tab { flex-direction: column; overflow: hidden; }
1577
+ .po-usage-period-tabs { display: flex; gap: 4px; padding: 10px 20px; border-bottom: 1px solid var(--border); flex-shrink: 0; }
1578
+ .po-usage-ptab { font-family: 'Azeret Mono', monospace; font-size: 9px; letter-spacing: 0.08em; padding: 3px 10px; background: none; border: 1px solid transparent; color: var(--muted); cursor: pointer; border-radius: 3px; text-transform: uppercase; transition: all 0.15s; }
1579
+ .po-usage-ptab:hover { color: var(--text); border-color: var(--border); }
1580
+ .po-usage-ptab.active { color: var(--teal); border-color: rgba(0,229,200,0.35); background: rgba(0,229,200,0.06); }
1581
+ .po-usage-section { padding: 16px 24px; border-bottom: 1px solid var(--border); }
1582
+ .po-usage-section:last-child { border-bottom: none; }
1583
+ .po-usage-section-title { font-size: 9px; font-weight: 700; letter-spacing: 0.12em; color: var(--muted); text-transform: uppercase; margin-bottom: 12px; font-family: 'Syne', sans-serif; }
1584
+ .po-usage-stat-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(140px, 1fr)); gap: 8px; }
1585
+ .po-usage-stat { background: rgba(0,0,0,0.3); border: 1px solid var(--border); border-radius: 4px; padding: 10px 12px; }
1586
+ .po-usage-stat-label { font-size: 8px; color: var(--dim); letter-spacing: 0.12em; text-transform: uppercase; margin-bottom: 4px; }
1587
+ .po-usage-stat-value { font-size: 20px; font-weight: 700; color: #ffd700; font-variant-numeric: tabular-nums; line-height: 1.1; }
1588
+ .po-usage-stat-sub { font-size: 9px; color: var(--muted); margin-top: 3px; }
1589
+ .po-usage-bar-row { display: flex; align-items: center; gap: 10px; margin-bottom: 7px; }
1590
+ .po-usage-bar-row:last-child { margin-bottom: 0; }
1591
+ .po-usage-bar-label { font-size: 10px; width: 130px; flex-shrink: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1592
+ .po-usage-bar-track { flex: 1; height: 5px; background: rgba(255,255,255,0.06); border-radius: 3px; overflow: hidden; }
1593
+ .po-usage-bar-fill { height: 100%; border-radius: 3px; transition: width 0.5s ease; }
1594
+ .po-usage-bar-count { font-size: 9px; color: var(--muted); width: 90px; text-align: right; flex-shrink: 0; font-variant-numeric: tabular-nums; font-family: 'Azeret Mono', monospace; }
1164
1595
  #po-body { flex: 1; overflow: hidden; position: relative; }
1165
1596
  .po-tab-pane { display: none; position: absolute; inset: 0; }
1166
1597
  .po-tab-pane.active { display: flex; }
@@ -1451,18 +1882,6 @@
1451
1882
  .po-ag-sess-title { font-size: 11px; font-weight: 700; color: var(--text); font-family: 'Azeret Mono', monospace; margin-bottom: 4px; }
1452
1883
  .po-ag-sess-subtitle { font-size: 9px; color: var(--muted); }
1453
1884
 
1454
- /* Knowledge graph pane */
1455
- #po-knowledge-tab { flex-direction: row; }
1456
- #po-knowledge-sidebar {
1457
- width: 200px; flex-shrink: 0; border-right: 1px solid var(--border);
1458
- padding: 16px 14px; overflow-y: auto; background: rgba(0,0,0,0.12);
1459
- }
1460
-
1461
- .gf-tool-tab { background: none; border: 1px solid transparent; color: var(--muted); font-family: 'Azeret Mono', monospace; font-size: 8px; letter-spacing: 0.1em; padding: 3px 10px; cursor: pointer; border-radius: 2px; transition: all 0.15s; }
1462
- .gf-tool-tab:hover { color: var(--text); background: rgba(255,255,255,0.04); }
1463
- .gf-tool-tab.active { color: var(--teal); border-color: rgba(0,229,200,0.3); background: rgba(0,229,200,0.06); }
1464
- .gf-view { display: none; }
1465
- .gf-view.active { display: flex; }
1466
1885
 
1467
1886
  /* Sidebar stat blocks */
1468
1887
  .kg-stat-group { margin-bottom: 18px; }
@@ -1496,6 +1915,14 @@
1496
1915
  .kg-node-name { font-size: 10px; color: var(--teal); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1497
1916
  .kg-node-meta { font-size: 8px; color: var(--muted); flex-shrink: 0; }
1498
1917
 
1918
+
1919
+ @media (prefers-reduced-motion: reduce) {
1920
+ *, *::before, *::after {
1921
+ animation-duration: 0.01ms !important;
1922
+ animation-iteration-count: 1 !important;
1923
+ transition-duration: 0.01ms !important;
1924
+ }
1925
+ }
1499
1926
  </style>
1500
1927
  </head>
1501
1928
  <body>
@@ -1544,7 +1971,7 @@
1544
1971
  <button class="po-tab" onclick="switchPalaceTab('routing')">ROUTING</button>
1545
1972
  <button class="po-tab" onclick="switchPalaceTab('swarm')">SWARM</button>
1546
1973
  <button class="po-tab" onclick="switchPalaceTab('graph')">AGENT GRAPH</button>
1547
- <button class="po-tab" onclick="switchPalaceTab('knowledge')">GRAPHIFY</button>
1974
+ <button class="po-tab" onclick="switchPalaceTab('usage')">USAGE</button>
1548
1975
  </div>
1549
1976
  <div id="po-stats"></div>
1550
1977
  </div>
@@ -1676,76 +2103,14 @@
1676
2103
  <div style="color:var(--muted);font-size:10px;">Loading…</div>
1677
2104
  </div>
1678
2105
  </div>
1679
- <div id="po-knowledge-tab" class="po-tab-pane">
1680
- <div id="po-knowledge-sidebar">
1681
- <div id="po-knowledge-stats"><div style="color:var(--muted);font-size:10px;">Loading…</div></div>
1682
- </div>
1683
- <div id="po-knowledge-main-area" style="flex:1;display:flex;flex-direction:column;min-width:0;overflow:hidden;">
1684
- <div id="po-graphify-toolbar" style="display:flex;align-items:center;gap:8px;padding:6px 14px;border-bottom:1px solid var(--border);flex-shrink:0;">
1685
- <button class="gf-tool-tab active" data-gftab="graph" onclick="switchGraphifyView('graph')">GRAPH</button>
1686
- <button class="gf-tool-tab" data-gftab="query" onclick="switchGraphifyView('query')">QUERY</button>
1687
- <button class="gf-tool-tab" data-gftab="explain" onclick="switchGraphifyView('explain')">EXPLAIN</button>
1688
- <button class="gf-tool-tab" data-gftab="path" onclick="switchGraphifyView('path')">PATH</button>
1689
- <button class="gf-tool-tab" data-gftab="benchmark" onclick="switchGraphifyView('benchmark')">BENCHMARK</button>
1690
- <button class="gf-tool-tab" data-gftab="report" onclick="switchGraphifyView('report')">REPORT</button>
1691
- <span style="flex:1;"></span>
1692
- <span id="gf-watch-indicator" style="font-size:8px;letter-spacing:0.06em;"></span>
1693
- <button id="gf-watch-btn" class="kg-action-btn" style="width:auto;padding:3px 10px;margin:0;" onclick="toggleGraphifyWatch()">WATCH</button>
1694
- </div>
1695
-
1696
- <!-- GRAPH sub-view (embedded graphify interactive HTML) -->
1697
- <div id="gf-view-graph" class="gf-view active" style="flex:1;display:flex;flex-direction:column;">
1698
- <iframe id="gf-graph-iframe" style="flex:1;width:100%;border:none;background:#0f0f1a;"></iframe>
1699
- </div>
1700
-
1701
- <!-- QUERY sub-view -->
1702
- <div id="gf-view-query" class="gf-view" style="flex:1;overflow-y:auto;padding:16px 20px;">
1703
- <div style="display:flex;gap:8px;align-items:center;margin-bottom:12px;">
1704
- <input id="gf-query-input" type="text" placeholder="Ask about the codebase…" style="flex:1;background:rgba(255,255,255,0.04);border:1px solid var(--border);border-radius:3px;padding:7px 10px;color:var(--text);font-family:'Azeret Mono',monospace;font-size:11px;outline:none;" onkeydown="if(event.key==='Enter')runGraphifyQuery()">
1705
- <select id="gf-query-mode" style="background:rgba(255,255,255,0.04);border:1px solid var(--border);border-radius:3px;padding:5px 8px;color:var(--text);font-family:'Azeret Mono',monospace;font-size:9px;">
1706
- <option value="dfs">DFS</option>
1707
- <option value="bfs">BFS</option>
1708
- </select>
1709
- <input id="gf-query-budget" type="number" value="2000" min="100" max="50000" step="500" style="width:70px;background:rgba(255,255,255,0.04);border:1px solid var(--border);border-radius:3px;padding:5px 6px;color:var(--text);font-family:'Azeret Mono',monospace;font-size:9px;" title="Token budget">
1710
- <button class="kg-action-btn" style="width:auto;padding:5px 14px;margin:0;" onclick="runGraphifyQuery()">ASK</button>
1711
- </div>
1712
- <div id="gf-query-result" style="font-size:10px;color:var(--dim);line-height:1.7;white-space:pre-wrap;word-break:break-word;"></div>
1713
- </div>
1714
-
1715
- <!-- EXPLAIN sub-view -->
1716
- <div id="gf-view-explain" class="gf-view" style="flex:1;overflow-y:auto;padding:16px 20px;">
1717
- <div style="display:flex;gap:8px;align-items:center;margin-bottom:12px;">
1718
- <input id="gf-explain-input" type="text" placeholder="Node name (e.g. UserService, auth.ts)…" style="flex:1;background:rgba(255,255,255,0.04);border:1px solid var(--border);border-radius:3px;padding:7px 10px;color:var(--text);font-family:'Azeret Mono',monospace;font-size:11px;outline:none;" onkeydown="if(event.key==='Enter')runGraphifyExplain()">
1719
- <button class="kg-action-btn" style="width:auto;padding:5px 14px;margin:0;" onclick="runGraphifyExplain()">EXPLAIN</button>
1720
- </div>
1721
- <div id="gf-explain-result" style="font-size:10px;color:var(--dim);line-height:1.7;white-space:pre-wrap;word-break:break-word;"></div>
1722
- </div>
1723
-
1724
- <!-- PATH sub-view -->
1725
- <div id="gf-view-path" class="gf-view" style="flex:1;overflow-y:auto;padding:16px 20px;">
1726
- <div style="display:flex;gap:8px;align-items:center;margin-bottom:12px;">
1727
- <input id="gf-path-from" type="text" placeholder="From node…" style="flex:1;background:rgba(255,255,255,0.04);border:1px solid var(--border);border-radius:3px;padding:7px 10px;color:var(--text);font-family:'Azeret Mono',monospace;font-size:11px;outline:none;">
1728
- <span style="color:var(--muted);font-size:10px;">→</span>
1729
- <input id="gf-path-to" type="text" placeholder="To node…" style="flex:1;background:rgba(255,255,255,0.04);border:1px solid var(--border);border-radius:3px;padding:7px 10px;color:var(--text);font-family:'Azeret Mono',monospace;font-size:11px;outline:none;" onkeydown="if(event.key==='Enter')runGraphifyPath()">
1730
- <button class="kg-action-btn" style="width:auto;padding:5px 14px;margin:0;" onclick="runGraphifyPath()">FIND PATH</button>
1731
- </div>
1732
- <div id="gf-path-result" style="font-size:10px;color:var(--dim);line-height:1.7;white-space:pre-wrap;word-break:break-word;"></div>
1733
- </div>
1734
-
1735
- <!-- BENCHMARK sub-view -->
1736
- <div id="gf-view-benchmark" class="gf-view" style="flex:1;overflow-y:auto;padding:16px 20px;">
1737
- <div style="display:flex;gap:8px;align-items:center;margin-bottom:12px;">
1738
- <button class="kg-action-btn" style="width:auto;padding:5px 14px;margin:0;" onclick="runGraphifyBenchmark()">RUN BENCHMARK</button>
1739
- <span id="gf-bench-status" style="font-size:9px;color:var(--muted);"></span>
1740
- </div>
1741
- <div id="gf-benchmark-result" style="font-size:10px;color:var(--dim);line-height:1.7;white-space:pre-wrap;word-break:break-word;"></div>
1742
- </div>
1743
-
1744
- <!-- REPORT sub-view -->
1745
- <div id="gf-view-report" class="gf-view" style="flex:1;overflow-y:auto;padding:16px 20px;">
1746
- <div id="po-knowledge-body"><div style="color:var(--muted);font-size:10px;padding:16px;">Loading…</div></div>
1747
- </div>
2106
+ <div id="po-usage-tab" class="po-tab-pane" style="flex-direction:column;overflow:hidden;">
2107
+ <div class="po-usage-period-tabs">
2108
+ <button class="po-usage-ptab active" onclick="switchUsagePeriod('today')">TODAY</button>
2109
+ <button class="po-usage-ptab" onclick="switchUsagePeriod('week')">WEEK</button>
2110
+ <button class="po-usage-ptab" onclick="switchUsagePeriod('30days')">30 DAYS</button>
2111
+ <button class="po-usage-ptab" onclick="switchUsagePeriod('month')">MONTH</button>
1748
2112
  </div>
2113
+ <div id="po-usage-body" style="flex:1;overflow-y:auto;"></div>
1749
2114
  </div>
1750
2115
  </div>
1751
2116
  <!-- Edit memory modal -->
@@ -1791,6 +2156,468 @@
1791
2156
  </div>
1792
2157
  </div>
1793
2158
 
2159
+ <!-- ══════════════════════════ MASTERMIND OVERLAY ══════════════════ -->
2160
+ <div id="mastermind-overlay">
2161
+ <div id="mm-header">
2162
+ <button id="mm-back" onclick="closeMastermindOverlay()">← BACK</button>
2163
+ <span id="mm-header-title">🧠 &nbsp;MASTERMIND</span>
2164
+ <span id="mm-header-sub">AUTONOMOUS BUSINESS BRAIN · 10 DOMAINS · SELF-IMPROVING</span>
2165
+ </div>
2166
+ <iframe id="mm-frame" src="" title="Mastermind"></iframe>
2167
+ </div>
2168
+
2169
+ <!-- ═══════════════════════════ MONOGRAPH OVERLAY ══════════════════ -->
2170
+ <div id="monograph-overlay">
2171
+ <div id="mg-header">
2172
+ <button id="mg-back" onclick="closeMonographOverlay()">← BACK</button>
2173
+ <span id="mg-title">MONOGRAPH</span>
2174
+ <div id="mg-tabs">
2175
+ <button class="mg-tab active" onclick="switchMonographTab('overview')">OVERVIEW</button>
2176
+ <button class="mg-tab" onclick="switchMonographTab('graph')">GRAPH</button>
2177
+ <button class="mg-tab" onclick="switchMonographTab('analyze')">ANALYZE</button>
2178
+ <button class="mg-tab" onclick="switchMonographTab('query')">QUERY</button>
2179
+ <button class="mg-tab" onclick="switchMonographTab('export')">EXPORT</button>
2180
+ <button class="mg-tab" onclick="switchMonographTab('report')">REPORT</button>
2181
+ <button class="mg-tab" onclick="switchMonographTab('wiki')" style="color:rgba(0,229,135,0.85);border-bottom-color:rgba(0,229,135,0.5);">WIKI</button>
2182
+ </div>
2183
+ <div id="mg-stats"></div>
2184
+ </div>
2185
+ <div id="mg-body">
2186
+
2187
+ <!-- OVERVIEW -->
2188
+ <div id="mg-pane-overview" class="mg-pane active">
2189
+ <div id="mg-overview-live" style="padding:10px 16px;border-bottom:1px solid var(--border);background:rgba(0,0,0,0.2);flex-shrink:0;">
2190
+ <div style="font-size:8px;letter-spacing:0.12em;color:var(--muted);margin-bottom:8px;text-transform:uppercase;">Project Snapshot</div>
2191
+ <div id="mg-overview-stats" style="display:grid;grid-template-columns:repeat(4,1fr);gap:8px;margin-bottom:10px;"></div>
2192
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:16px;">
2193
+ <div><div style="font-size:8px;letter-spacing:0.1em;color:var(--muted);margin-bottom:5px;text-transform:uppercase;">Top Nodes by Degree</div><div id="mg-overview-top-nodes"></div></div>
2194
+ <div><div style="font-size:8px;letter-spacing:0.1em;color:var(--muted);margin-bottom:5px;text-transform:uppercase;">Node Type Distribution</div><div id="mg-overview-types" style="font-size:9px;color:#c0c0ff;line-height:2;"></div></div>
2195
+ </div>
2196
+ </div>
2197
+ <div id="mg-overview" style="overflow:auto;flex:1;padding:16px;">
2198
+ <div class="mg-section-title">Parse &amp; Pipeline</div>
2199
+ <div class="mg-feature-grid">
2200
+ <div class="mg-feature-card"><div class="mg-card-name">Arrow Function Extractor</div><div class="mg-card-desc">Extracts const/let arrow functions from TS/JS source with export detection.</div><span class="mg-card-badge">parse.ts</span></div>
2201
+ <div class="mg-feature-card"><div class="mg-card-name">C# Namespace Extractor</div><div class="mg-card-desc">Extracts namespace declarations from .cs files using regex matching.</div><span class="mg-card-badge">parse.ts</span></div>
2202
+ <div class="mg-feature-card"><div class="mg-card-name">Variable Phase</div><div class="mg-card-desc">Top-level variable declaration extraction for TS/JS files.</div><span class="mg-card-badge">variables-phase.ts</span></div>
2203
+ <div class="mg-feature-card"><div class="mg-card-name">Field Access Phase</div><div class="mg-card-desc">Tracks property/field access patterns across the codebase.</div><span class="mg-card-badge">field-access.ts</span></div>
2204
+ <div class="mg-feature-card"><div class="mg-card-name">Wildcard Synthesis</div><div class="mg-card-desc">Synthesizes wildcard import/export edges into real symbol edges.</div><span class="mg-card-badge">wildcard-phase.ts</span></div>
2205
+ <div class="mg-feature-card"><div class="mg-card-name">Framework Detector</div><div class="mg-card-desc">Detects React, Vue, Angular, Next.js, Express and other frameworks.</div><span class="mg-card-badge">framework-detect.ts</span></div>
2206
+ <div class="mg-feature-card"><div class="mg-card-name">Import Resolver</div><div class="mg-card-desc">Resolves TypeScript path aliases and tsconfig paths to real files.</div><span class="mg-card-badge">import-resolver.ts</span></div>
2207
+ <div class="mg-feature-card"><div class="mg-card-name">Evidence Phase</div><div class="mg-card-desc">Collects confidence evidence for edges based on multiple signals.</div><span class="mg-card-badge">evidence.ts</span></div>
2208
+ </div>
2209
+
2210
+ <div class="mg-section-title">Graph Algorithms</div>
2211
+ <div class="mg-feature-grid">
2212
+ <div class="mg-feature-card"><div class="mg-card-name">PageRank</div><div class="mg-card-desc">Power iteration PageRank (d=0.85) to find influential nodes in the graph.</div><span class="mg-card-badge">pagerank.ts</span></div>
2213
+ <div class="mg-feature-card"><div class="mg-card-name">Topological Sort</div><div class="mg-card-desc">Kahn's algorithm on reversed graph for dependency ordering.</div><span class="mg-card-badge">topo-sort.ts</span></div>
2214
+ <div class="mg-feature-card"><div class="mg-card-name">Weakly Connected Components</div><div class="mg-card-desc">Union-find WCC detection to find isolated subgraphs.</div><span class="mg-card-badge">wcc.ts</span></div>
2215
+ <div class="mg-feature-card"><div class="mg-card-name">Cycle Detection (SCC)</div><div class="mg-card-desc">Kosaraju's SCC algorithm to find circular dependencies.</div><span class="mg-card-badge">cycles.ts</span></div>
2216
+ <div class="mg-feature-card"><div class="mg-card-name">Dead Code Detection</div><div class="mg-card-desc">Finds unexported nodes with in-degree 0 — unreachable symbols.</div><span class="mg-card-badge">dead-code.ts</span></div>
2217
+ <div class="mg-feature-card"><div class="mg-card-name">Import Chain Tracer</div><div class="mg-card-desc">DFS traversal to find all import paths between any two nodes.</div><span class="mg-card-badge">import-chain.ts</span></div>
2218
+ <div class="mg-feature-card"><div class="mg-card-name">Ripple Impact</div><div class="mg-card-desc">Multi-hop cascade analysis — which nodes are affected by a change.</div><span class="mg-card-badge">ripple-impact.ts</span></div>
2219
+ <div class="mg-feature-card"><div class="mg-card-name">Graph Statistics</div><div class="mg-card-desc">Density, clustering coefficient, average path length, diameter.</div><span class="mg-card-badge">statistics.ts</span></div>
2220
+ </div>
2221
+
2222
+ <div class="mg-section-title">Analysis &amp; Scoring</div>
2223
+ <div class="mg-feature-grid">
2224
+ <div class="mg-feature-card"><div class="mg-card-name">Surprise Scorer</div><div class="mg-card-desc">5-factor scoring: cross-type, cross-community, cross-filetype, MRO, god-node bonuses.</div><span class="mg-card-badge">surprise-scorer.ts</span></div>
2225
+ <div class="mg-feature-card"><div class="mg-card-name">File Classifier</div><div class="mg-card-desc">Classifies files as CODE, TEST, CONFIG, DOCUMENT or PAPER using 12 content signals.</div><span class="mg-card-badge">file-classifier.ts</span></div>
2226
+ <div class="mg-feature-card"><div class="mg-card-name">Corpus Health</div><div class="mg-card-desc">Checks corpus for too-small / too-large / too-many-files thresholds.</div><span class="mg-card-badge">corpus-health.ts</span></div>
2227
+ <div class="mg-feature-card"><div class="mg-card-name">Cluster Quality</div><div class="mg-card-desc">Silhouette score and modularity score for community quality assessment.</div><span class="mg-card-badge">cluster-quality.ts</span></div>
2228
+ <div class="mg-feature-card"><div class="mg-card-name">Node Similarity</div><div class="mg-card-desc">Jaccard similarity on neighbor sets to find structurally similar nodes.</div><span class="mg-card-badge">node-similarity.ts</span></div>
2229
+ <div class="mg-feature-card"><div class="mg-card-name">Dependency Health</div><div class="mg-card-desc">Detects circular deps, fan-in/fan-out violations, instability metrics.</div><span class="mg-card-badge">dependency-health.ts</span></div>
2230
+ <div class="mg-feature-card"><div class="mg-card-name">Betweenness Centrality</div><div class="mg-card-desc">Identifies bridge nodes whose removal would disconnect the graph.</div><span class="mg-card-badge">analyzer.ts</span></div>
2231
+ <div class="mg-feature-card"><div class="mg-card-name">Graph Benchmark</div><div class="mg-card-desc">Tracks pipeline performance metrics across builds over time.</div><span class="mg-card-badge">benchmark.ts</span></div>
2232
+ <div class="mg-feature-card" style="border-color:rgba(123,97,255,0.4)"><div class="mg-card-name">Blast Radius (Impact)</div><div class="mg-card-desc">BFS upstream/downstream traversal — find all files that depend on a node.</div><span class="mg-card-badge">monograph_impact</span></div>
2233
+ <div class="mg-feature-card" style="border-color:rgba(123,97,255,0.4)"><div class="mg-card-name">360° Context View</div><div class="mg-card-desc">Importers, imports, parent, and community siblings for any node.</div><span class="mg-card-badge">monograph_context</span></div>
2234
+ <div class="mg-feature-card" style="border-color:rgba(123,97,255,0.4)"><div class="mg-card-name">Community Cohesion</div><div class="mg-card-desc">Ratio of intra-community edges to max possible — identifies loose clusters.</div><span class="mg-card-badge">monograph_cohesion</span></div>
2235
+ <div class="mg-feature-card" style="border-color:rgba(123,97,255,0.4)"><div class="mg-card-name">Bridge Nodes</div><div class="mg-card-desc">Cross-community connectors — architectural coupling points with cross-edge count.</div><span class="mg-card-badge">monograph_bridge</span></div>
2236
+ <div class="mg-feature-card" style="border-color:rgba(123,97,255,0.4)"><div class="mg-card-name">Rename (Dry-run)</div><div class="mg-card-desc">Graph-assisted multi-file rename — finds all graph + text occurrences with confidence tags.</div><span class="mg-card-badge">monograph_rename</span></div>
2237
+ <div class="mg-feature-card" style="border-color:rgba(123,97,255,0.4)"><div class="mg-card-name">Cypher Query</div><div class="mg-card-desc">MATCH (n:Class)-[:IMPORTS]->(b) style graph queries translated to SQL.</div><span class="mg-card-badge">monograph_cypher</span></div>
2238
+ <div class="mg-feature-card" style="border-color:rgba(123,97,255,0.4)"><div class="mg-card-name">Detect Changes</div><div class="mg-card-desc">Maps git diff changed files → graph nodes + 1-level dependents.</div><span class="mg-card-badge">monograph_detect_changes</span></div>
2239
+ </div>
2240
+
2241
+ <div class="mg-section-title">Dead Code Analysis</div>
2242
+ <div class="mg-feature-grid">
2243
+ <div class="mg-feature-card"><div class="mg-card-name">Unused Exports</div><div class="mg-card-desc">Reference-set inversion over the module graph — finds every export not consumed by any reachable file. Handles re-exports, type-only guards, and CJS modules.</div><span class="mg-card-badge">monograph_unlinked_refs</span></div>
2244
+ <div class="mg-feature-card"><div class="mg-card-name">Unused Files</div><div class="mg-card-desc">Graph-reachability scan from all entry points — files with no reachable importer are flagged. Integrates with suppress rules and plugin-provided entry patterns.</div><span class="mg-card-badge">analyze/unused-files.ts</span></div>
2245
+ <div class="mg-feature-card"><div class="mg-card-name">Unused Dependencies</div><div class="mg-card-desc">Correlates npm package names against actual imports across the module graph. Finds unused, unresolved, and type-only dependencies.</div><span class="mg-card-badge">analyze/unused-deps.ts</span></div>
2246
+ <div class="mg-feature-card"><div class="mg-card-name">Boundary Violations</div><div class="mg-card-desc">Zone classification with caching + isImportAllowed edge checking. Supports 4 presets: layered, hexagonal, feature-sliced, bulletproof.</div><span class="mg-card-badge">monograph_boundary_check</span></div>
2247
+ <div class="mg-feature-card"><div class="mg-card-name">Graph Reachability</div><div class="mg-card-desc">Marks reachable/runtime-reachable/test-reachable flags on all module nodes from entry points using BFS. collectUnreachable enumerates dead files.</div><span class="mg-card-badge">monograph_reachability</span></div>
2248
+ <div class="mg-feature-card"><div class="mg-card-name">Cycle Detection (Tarjan)</div><div class="mg-card-desc">Iterative Tarjan SCC for circular dependency detection — no recursion stack overflow on large graphs. Returns cycle groups sorted by size.</div><span class="mg-card-badge">monograph_find_cycles</span></div>
2249
+ <div class="mg-feature-card"><div class="mg-card-name">Namespace Import Narrowing</div><div class="mg-card-desc">Tracks member accesses on namespace imports, marks all-exports-referenced, and prunes unused import bindings from the symbol set.</div><span class="mg-card-badge">graph/narrowing.ts</span></div>
2250
+ <div class="mg-feature-card"><div class="mg-card-name">Suppression System</div><div class="mg-card-desc">IssueKind (20 variants) with discriminant round-trip encoding for compact storage. Detects file-wide vs line-level suppression comments.</div><span class="mg-card-badge">monograph_suppress</span></div>
2251
+ </div>
2252
+
2253
+ <div class="mg-section-title">Duplication Detection</div>
2254
+ <div class="mg-feature-grid">
2255
+ <div class="mg-feature-card"><div class="mg-card-name">Suffix Array (O(N log N))</div><div class="mg-card-desc">Prefix-doubling suffix array construction for exact clone detection. Paired with Kasai's O(N) LCP array algorithm for longest common prefix.</div><span class="mg-card-badge">monograph_clone_detect</span></div>
2256
+ <div class="mg-feature-card"><div class="mg-card-name">Shingle Filter</div><div class="mg-card-desc">MinHash-inspired k-shingle pre-filter that narrows candidate pairs before the expensive suffix-array pass. Configurable shingle size.</div><span class="mg-card-badge">duplicates/detect/filtering.ts</span></div>
2257
+ <div class="mg-feature-card"><div class="mg-card-name">Clone Group Extraction</div><div class="mg-card-desc">Extracts raw instance/group pairs from LCP array using the suffix + threshold approach. Handles overlapping spans with interval deduplication.</div><span class="mg-card-badge">duplicates/detect/extraction.ts</span></div>
2258
+ <div class="mg-feature-card"><div class="mg-card-name">Clone Families</div><div class="mg-card-desc">Groups clone instances by file-set key into families, generates refactoring suggestions: ExtractFunction / ExtractModule / MergeDirectories.</div><span class="mg-card-badge">monograph_similar_files</span></div>
2259
+ <div class="mg-feature-card"><div class="mg-card-name">Mirrored Directories</div><div class="mg-card-desc">Pairs directories whose file sets overlap (Jaccard ≥ 0.5) — identifies copy-paste directory structures that should be merged.</div><span class="mg-card-badge">monograph_mirrored_dirs</span></div>
2260
+ <div class="mg-feature-card"><div class="mg-card-name">Duplication Stats</div><div class="mg-card-desc">Per-file deduplicated line sets (no double-counting), duplicated token counting, and duplication % across the full pipeline.</div><span class="mg-card-badge">graph/duplication-stats.ts</span></div>
2261
+ </div>
2262
+
2263
+ <div class="mg-section-title">Health Scoring</div>
2264
+ <div class="mg-feature-grid">
2265
+ <div class="mg-feature-card"><div class="mg-card-name">Vital Signs</div><div class="mg-card-desc">0–100 health score from complexity, maintainability, duplication, dead-code penalties. JSONL snapshot persistence with trend detection (improving/degrading/stable).</div><span class="mg-card-badge">monograph_vital_signs_snapshot</span></div>
2266
+ <div class="mg-feature-card"><div class="mg-card-name">Hotspot Scoring</div><div class="mg-card-desc">Composite hotspot score from churn × complexity. Normalization maxima computation + normalizeHotspotScore for cross-project comparison.</div><span class="mg-card-badge">monograph_hotspots</span></div>
2267
+ <div class="mg-feature-card"><div class="mg-card-name">Ownership Risk</div><div class="mg-card-desc">Bus factor computation with bot-pattern filtering. Identifies single-owner files and computes ownership risk from git-blame contributor records.</div><span class="mg-card-badge">monograph_codeowners</span></div>
2268
+ <div class="mg-feature-card"><div class="mg-card-name">Effort Estimation</div><div class="mg-card-desc">Converts duplicated line count to effort (hours/days) and confidence level using log-scale calibrated thresholds.</div><span class="mg-card-badge">health/target-types.ts</span></div>
2269
+ <div class="mg-feature-card"><div class="mg-card-name">Health Grouping</div><div class="mg-card-desc">Groups files by owner, computes per-group aggregate scores, and builds a health grouping for team-level health dashboards.</div><span class="mg-card-badge">health/health-grouping.ts</span></div>
2270
+ <div class="mg-feature-card"><div class="mg-card-name">Scoring Types</div><div class="mg-card-desc">CRAP score density, dead-code ratio, and Maintainability Index (171 − 5.2·ln(V) − 0.23·G − 16.2·ln(L)) per file.</div><span class="mg-card-badge">monograph_maintainability</span></div>
2271
+ </div>
2272
+
2273
+ <div class="mg-section-title">Report Formatters</div>
2274
+ <div class="mg-feature-grid">
2275
+ <div class="mg-feature-card"><div class="mg-card-name">Check Report Formatter</div><div class="mg-card-desc">buildCheckHumanLines: grouped/truncated sections for unused files, exports, deps, members, unresolved imports. Category headers with item counts.</div><span class="mg-card-badge">report/human-check.ts</span></div>
2276
+ <div class="mg-feature-card"><div class="mg-card-name">Duplication Formatter</div><div class="mg-card-desc">buildDuplicationFamilyLines: clone groups sorted by line count, instance listings with file:start-end format, stats summary, suggestions.</div><span class="mg-card-badge">report/human-dupes.ts</span></div>
2277
+ <div class="mg-feature-card"><div class="mg-card-name">Health Report Formatter</div><div class="mg-card-desc">buildHealthHumanLines: vital signs with OK/WARNING/CRITICAL indicators, top N hotspot table, coverage gap listings, configurable limits.</div><span class="mg-card-badge">report/human-health.ts</span></div>
2278
+ </div>
2279
+
2280
+ <div class="mg-section-title">Config, Plugins &amp; Discovery</div>
2281
+ <div class="mg-feature-grid">
2282
+ <div class="mg-feature-card"><div class="mg-card-name">Built-in Plugin Registry</div><div class="mg-card-desc">30 built-in plugins (eslint, jest, vitest, prettier, vite, webpack, storybook, nx, turborepo, playwright, cypress, husky, docker, vercel, netlify, …). Prevents false positives on tool config files. 60 always-dev-tooling prefixes.</div><span class="mg-card-badge">config/plugins/builtin.ts</span></div>
2283
+ <div class="mg-feature-card"><div class="mg-card-name">Boundary Config</div><div class="mg-card-desc">4 architecture presets (layered, hexagonal, feature-sliced, bulletproof) with full zone/rule resolution. isImportAllowed checks any cross-file import against the active preset.</div><span class="mg-card-badge">monograph_boundary_check</span></div>
2284
+ <div class="mg-feature-card"><div class="mg-card-name">Config File Parser</div><div class="mg-card-desc">Walk-up VCS-stop config file discovery, JSON/JSONC/TOML format detection, JSONC comment stripping, sourceRoot auto-detection from src/lib/app.</div><span class="mg-card-badge">config/config-parsing.ts</span></div>
2285
+ <div class="mg-feature-card"><div class="mg-card-name">Infrastructure Entry Points</div><div class="mg-card-desc">Scans Dockerfile, Procfile, fly.toml, render.yaml, railway.toml for CMD/ENTRYPOINT/command references to .js/.ts files. Prevents false unreachability reports.</div><span class="mg-card-badge">discover/infrastructure-entries.ts</span></div>
2286
+ <div class="mg-feature-card"><div class="mg-card-name">Entry Point Categorization</div><div class="mg-card-desc">Classifies entry points as app/lib/bin/test/infra with formatSkippedEntryWarning and deduplication by canonical path.</div><span class="mg-card-badge">discover/entry-points.ts</span></div>
2287
+ <div class="mg-feature-card"><div class="mg-card-name">Output Format Parser</div><div class="mg-card-desc">Parses output format strings with CLI alias normalisation (codeclimate → code-climate). Validates against all supported output format variants.</div><span class="mg-card-badge">config/output-format.ts</span></div>
2288
+ </div>
2289
+
2290
+ <div class="mg-section-title">Fix, Actions &amp; Baseline</div>
2291
+ <div class="mg-feature-grid">
2292
+ <div class="mg-feature-card"><div class="mg-card-name">Export Surgery</div><div class="mg-card-desc">Surgical removal of a specific name from export { a, b, unusedName } lists — without deleting the entire statement. Handles aliases, type exports, multi-line spans, and batched multi-surgery.</div><span class="mg-card-badge">fix/export-surgery.ts</span></div>
2293
+ <div class="mg-feature-card"><div class="mg-card-name">JSON Action Specs</div><div class="mg-card-desc">Machine-readable action objects: delete-file, remove-export, export-type, remove-dependency, add-suppression. buildActionsFor* helpers produce the full action list per issue type.</div><span class="mg-card-badge">report/json-actions.ts</span></div>
2294
+ <div class="mg-feature-card"><div class="mg-card-name">Baseline Deltas</div><div class="mg-card-desc">Unified cross-category issue diffing: CategoryDelta per issue type, filterNewIssues removes anything already in baseline, formatBaselineDeltas produces human-readable summary lines.</div><span class="mg-card-badge">monograph_baseline_compare</span></div>
2295
+ <div class="mg-feature-card"><div class="mg-card-name">Analysis Result Set</div><div class="mg-card-desc">25-type complete result system with 17 issue arrays. makeEmptyResults, totalIssues, hasIssues, sortResults helpers for composing multi-pass analysis output.</div><span class="mg-card-badge">results/analysis-results.ts</span></div>
2296
+ </div>
2297
+
2298
+ <div class="mg-section-title">Export Formats</div>
2299
+ <div class="mg-feature-grid">
2300
+ <div class="mg-feature-card"><div class="mg-card-name">JSON</div><div class="mg-card-desc">Native graph format with all node/edge metadata.</div><span class="mg-card-badge">.json</span></div>
2301
+ <div class="mg-feature-card"><div class="mg-card-name">GraphML</div><div class="mg-card-desc">XML-based format with community attributes for Gephi/yEd.</div><span class="mg-card-badge">.graphml</span></div>
2302
+ <div class="mg-feature-card"><div class="mg-card-name">Mermaid</div><div class="mg-card-desc">Flowchart diagram with subgraphs per community, dashed INFERRED edges.</div><span class="mg-card-badge">.mmd</span></div>
2303
+ <div class="mg-feature-card"><div class="mg-card-name">DOT (Graphviz)</div><div class="mg-card-desc">Graphviz DOT format for layout with neato/dot/fdp engines.</div><span class="mg-card-badge">.dot</span></div>
2304
+ <div class="mg-feature-card"><div class="mg-card-name">GEXF</div><div class="mg-card-desc">GEXF 1.3 XML for Gephi with full metadata support.</div><span class="mg-card-badge">.gexf</span></div>
2305
+ <div class="mg-feature-card"><div class="mg-card-name">SVG</div><div class="mg-card-desc">Static SVG diagram with force-directed layout.</div><span class="mg-card-badge">.svg</span></div>
2306
+ <div class="mg-feature-card"><div class="mg-card-name">Cypher</div><div class="mg-card-desc">Neo4j Cypher CREATE statements for graph database import.</div><span class="mg-card-badge">.cypher</span></div>
2307
+ <div class="mg-feature-card"><div class="mg-card-name">CSV</div><div class="mg-card-desc">Node and edge CSV files for spreadsheet analysis.</div><span class="mg-card-badge">.csv</span></div>
2308
+ <div class="mg-feature-card"><div class="mg-card-name">Adjacency Matrix</div><div class="mg-card-desc">Square matrix representation with node ID headers.</div><span class="mg-card-badge">.csv</span></div>
2309
+ <div class="mg-feature-card"><div class="mg-card-name">HTML Report</div><div class="mg-card-desc">Self-contained interactive HTML with full graph visualization.</div><span class="mg-card-badge">.html</span></div>
2310
+ </div>
2311
+
2312
+ <div class="mg-section-title">Search &amp; Retrieval</div>
2313
+ <div class="mg-feature-grid">
2314
+ <div class="mg-feature-card"><div class="mg-card-name">Hybrid RRF Search</div><div class="mg-card-desc">Reciprocal Rank Fusion combining BM25 FTS + vector similarity.</div><span class="mg-card-badge">rrf.ts</span></div>
2315
+ <div class="mg-feature-card"><div class="mg-card-name">Exact Vector Search</div><div class="mg-card-desc">Brute-force cosine similarity when ANN index isn't available.</div><span class="mg-card-badge">exact-search.ts</span></div>
2316
+ <div class="mg-feature-card"><div class="mg-card-name">Diacritic Search</div><div class="mg-card-desc">Normalizes accented characters for language-aware search.</div><span class="mg-card-badge">diacritic.ts</span></div>
2317
+ <div class="mg-feature-card"><div class="mg-card-name">AST Chunker</div><div class="mg-card-desc">Splits code into semantic chunks at function/class boundaries.</div><span class="mg-card-badge">ast-chunker.ts</span></div>
2318
+ <div class="mg-feature-card"><div class="mg-card-name">Embedding Staleness</div><div class="mg-card-desc">Content-hash tracking to detect and re-embed stale nodes.</div><span class="mg-card-badge">embedding-store.ts</span></div>
2319
+ </div>
2320
+
2321
+ <div class="mg-section-title">Groups &amp; Contracts</div>
2322
+ <div class="mg-feature-grid">
2323
+ <div class="mg-feature-card"><div class="mg-card-name">gRPC Contract Extractor</div><div class="mg-card-desc">Parses .proto files and Go/Node/Python/Java gRPC clients into contracts.</div><span class="mg-card-badge">grpc-extractor.ts</span></div>
2324
+ <div class="mg-feature-card"><div class="mg-card-name">Topic Contract Extractor</div><div class="mg-card-desc">Detects Kafka, RabbitMQ, SQS and generic pub/sub topic contracts.</div><span class="mg-card-badge">topic-extractor.ts</span></div>
2325
+ <div class="mg-feature-card"><div class="mg-card-name">Service Boundary Detector</div><div class="mg-card-desc">Identifies service boundaries from HTTP clients, gRPC stubs, and DB access.</div><span class="mg-card-badge">service-boundary.ts</span></div>
2326
+ <div class="mg-feature-card"><div class="mg-card-name">Contract Bridge</div><div class="mg-card-desc">Links producers and consumers across service boundaries.</div><span class="mg-card-badge">contract-bridge.ts</span></div>
2327
+ </div>
2328
+
2329
+ <div class="mg-section-title">Wiki &amp; Knowledge</div>
2330
+ <div class="mg-feature-grid">
2331
+ <div class="mg-feature-card"><div class="mg-card-name">Wiki Generator</div><div class="mg-card-desc">Auto-generates wiki pages from graph analysis. Supports reviewOnly mode for pre-flight checks.</div><span class="mg-card-badge">wiki-generator.ts</span></div>
2332
+ <div class="mg-feature-card"><div class="mg-card-name">Provider-Aware LLM</div><div class="mg-card-desc">Anthropic / OpenAI / Ollama support with special handling for o1/o3/o4-mini reasoning models.</div><span class="mg-card-badge">providers.ts</span></div>
2333
+ <div class="mg-feature-card"><div class="mg-card-name">Gist Publisher</div><div class="mg-card-desc">Publishes wiki pages to GitHub Gist with upsert support.</div><span class="mg-card-badge">gist-publisher.ts</span></div>
2334
+ <div class="mg-feature-card"><div class="mg-card-name">Graph Report</div><div class="mg-card-desc">Generates Markdown report with knowledge gap section for isolated nodes.</div><span class="mg-card-badge">graph-report.ts</span></div>
2335
+ <div class="mg-feature-card" style="border-color:rgba(0,229,135,0.4)"><div class="mg-card-name">Docs KG Parser</div><div class="mg-card-desc">Parses .md/.mdx/.txt/.rst into Section nodes. Extracts headings, YAML frontmatter, wiki links, and #hashtags into the graph.</div><span class="mg-card-badge" style="color:rgba(0,229,135,0.7);">docs-parse.ts</span></div>
2336
+ <div class="mg-feature-card" style="border-color:rgba(0,229,135,0.4)"><div class="mg-card-name">Wiki Links</div><div class="mg-card-desc">[[WikiLink]] and [text](./file.md) resolve to REFERENCES edges between Section and File nodes with deferred resolution.</div><span class="mg-card-badge" style="color:rgba(0,229,135,0.7);">REFERENCES edge</span></div>
2337
+ <div class="mg-feature-card" style="border-color:rgba(0,229,135,0.4)"><div class="mg-card-name">Section Hierarchy</div><div class="mg-card-desc">H1 → H2 → H3 nesting expressed as PARENT_SECTION edges. Navigate like a tree with monograph_neighbors.</div><span class="mg-card-badge" style="color:rgba(0,229,135,0.7);">PARENT_SECTION edge</span></div>
2338
+ <div class="mg-feature-card" style="border-color:rgba(0,229,135,0.4)"><div class="mg-card-name">Tag Graph</div><div class="mg-card-desc">Frontmatter tags/categories and inline #hashtags become Concept nodes connected via TAGGED_AS edges — enables topic-based graph traversal.</div><span class="mg-card-badge" style="color:rgba(0,229,135,0.7);">TAGGED_AS edge</span></div>
2339
+ <div class="mg-feature-card" style="border-color:rgba(0,229,135,0.4)"><div class="mg-card-name">Hybrid Doc Search</div><div class="mg-card-desc">Section content is embedded at build time. Query with mode=hybrid for BM25+cosine RRF merge — semantic search across all docs.</div><span class="mg-card-badge" style="color:rgba(0,229,135,0.7);">monograph_query</span></div>
2340
+ </div>
2341
+
2342
+ <div class="mg-section-title">Ingest &amp; Input</div>
2343
+ <div class="mg-feature-grid">
2344
+ <div class="mg-feature-card"><div class="mg-card-name">URL Ingest</div><div class="mg-card-desc">Fetches and ingests web content into the knowledge graph.</div><span class="mg-card-badge">url-ingest.ts</span></div>
2345
+ <div class="mg-feature-card"><div class="mg-card-name">Git Clone / Pull</div><div class="mg-card-desc">SSRF-protected git clone and pull for remote repository ingestion.</div><span class="mg-card-badge">git-clone.ts</span></div>
2346
+ <div class="mg-feature-card"><div class="mg-card-name">Transcription</div><div class="mg-card-desc">Audio/video transcription via whisper/yt-dlp pipeline.</div><span class="mg-card-badge">transcribe.ts</span></div>
2347
+ <div class="mg-feature-card"><div class="mg-card-name">Query Memory</div><div class="mg-card-desc">Saves Q&amp;A pairs as Markdown+YAML for persistent knowledge.</div><span class="mg-card-badge">query-memory.ts</span></div>
2348
+ </div>
2349
+
2350
+ <div class="mg-section-title">CLI &amp; Platform</div>
2351
+ <div class="mg-feature-grid">
2352
+ <div class="mg-feature-card"><div class="mg-card-name">Platform Installs</div><div class="mg-card-desc">14-platform hook installer (Claude, GitHub Actions, Cursor, VSCode, etc.) with marker blocks.</div><span class="mg-card-badge">platforms.ts</span></div>
2353
+ <div class="mg-feature-card"><div class="mg-card-name">Embedding Device</div><div class="mg-card-desc">--embedding-device flag: auto/cpu/dml/cuda/wasm selection.</div><span class="mg-card-badge">analyze.ts</span></div>
2354
+ <div class="mg-feature-card"><div class="mg-card-name">Marker-Based Hooks</div><div class="mg-card-desc">Idempotent hook installs using start/end marker blocks with Husky detection.</div><span class="mg-card-badge">hooks-install.ts</span></div>
2355
+ <div class="mg-feature-card"><div class="mg-card-name">Language Parsers</div><div class="mg-card-desc">Symbol extractors for Scala, Lua, Zig, PowerShell, Elixir.</div><span class="mg-card-badge">language-parsers.ts</span></div>
2356
+ </div>
2357
+ </div>
2358
+ </div>
2359
+
2360
+ <!-- GRAPH -->
2361
+ <div id="mg-pane-graph" class="mg-pane" style="flex-direction:column;">
2362
+ <div style="display:flex;align-items:center;gap:8px;padding:5px 12px;border-bottom:1px solid var(--border);flex-shrink:0;background:rgba(0,0,0,0.15);">
2363
+ <span id="mg-watch-indicator" style="font-size:8px;letter-spacing:0.06em;color:var(--muted);">○ IDLE</span>
2364
+ <button id="mg-watch-btn" onclick="toggleMonographWatch()" style="background:none;border:1px solid var(--border);color:var(--muted);font-family:'Azeret Mono',monospace;font-size:8px;letter-spacing:0.08em;padding:3px 8px;cursor:pointer;border-radius:2px;">WATCH</button>
2365
+ <button onclick="triggerPalaceGraphBuild()" style="background:none;border:1px solid rgba(123,97,255,0.4);color:rgba(123,97,255,0.8);font-family:'Azeret Mono',monospace;font-size:8px;letter-spacing:0.08em;padding:3px 8px;cursor:pointer;border-radius:2px;">REBUILD</button>
2366
+ <button onclick="window.open('/api/monograph-html'+(selectedProjectDir?'?dir='+encodeURIComponent(selectedProjectDir):''),'_blank')" style="background:none;border:1px solid var(--border);color:var(--muted);font-family:'Azeret Mono',monospace;font-size:8px;letter-spacing:0.08em;padding:3px 8px;cursor:pointer;border-radius:2px;">OPEN ↗</button>
2367
+ <span style="flex:1;"></span>
2368
+ <span id="mg-graph-node-count" style="font-size:9px;color:var(--muted);font-family:'Azeret Mono',monospace;"></span>
2369
+ </div>
2370
+ <iframe id="mg-graph-iframe" style="flex:1;width:100%;border:none;background:#0f0f1a;"></iframe>
2371
+ </div>
2372
+
2373
+ <!-- ANALYZE -->
2374
+ <div id="mg-pane-analyze" class="mg-pane">
2375
+ <div id="mg-analyze">
2376
+ <div id="mg-analyze-sidebar">
2377
+ <div style="font-family:'Azeret Mono',monospace;font-size:8px;letter-spacing:0.1em;color:var(--muted);padding:4px 6px 8px;text-transform:uppercase;">Algorithms</div>
2378
+ <button class="mg-analyze-btn active" onclick="runMonographAnalysis('stats')">Graph Statistics</button>
2379
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('pagerank')">PageRank Top 20</button>
2380
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('deadcode')">Dead Code</button>
2381
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('cycles')">Cycles / SCCs</button>
2382
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('wcc')">Connected Components</button>
2383
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('topo')">Topological Order</button>
2384
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('hubs')">Top Hubs (Degree)</button>
2385
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('surprises')">Surprising Edges</button>
2386
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('communities')">Communities</button>
2387
+ <div style="font-family:'Azeret Mono',monospace;font-size:8px;letter-spacing:0.1em;color:var(--muted);padding:12px 6px 8px;text-transform:uppercase;border-top:1px solid var(--border);margin-top:8px;">Impact</div>
2388
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('detect_changes')">Changed Files</button>
2389
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('diff')">Graph Diff</button>
2390
+ <div style="font-family:'Azeret Mono',monospace;font-size:8px;letter-spacing:0.1em;color:var(--muted);padding:12px 6px 8px;text-transform:uppercase;border-top:1px solid var(--border);margin-top:8px;">Quality</div>
2391
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('cohesion')">Community Cohesion</button>
2392
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('bridges')">Bridge Nodes</button>
2393
+ <div style="font-family:'Azeret Mono',monospace;font-size:8px;letter-spacing:0.1em;color:var(--muted);padding:12px 6px 8px;text-transform:uppercase;border-top:1px solid var(--border);margin-top:8px;">Dead Code</div>
2394
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('unlinked_refs')">Unlinked Refs</button>
2395
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('boundary_check')">Boundary Violations</button>
2396
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('reachability')">Reachability</button>
2397
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('regression_check')">Regression Check</button>
2398
+ <div style="font-family:'Azeret Mono',monospace;font-size:8px;letter-spacing:0.1em;color:var(--muted);padding:12px 6px 8px;text-transform:uppercase;border-top:1px solid var(--border);margin-top:8px;">Duplication</div>
2399
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('clone_detect')">Clone Detection</button>
2400
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('similar_files')">Similar Files</button>
2401
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('mirrored_dirs')">Mirrored Dirs</button>
2402
+ <div style="font-family:'Azeret Mono',monospace;font-size:8px;letter-spacing:0.1em;color:var(--muted);padding:12px 6px 8px;text-transform:uppercase;border-top:1px solid var(--border);margin-top:8px;">Health &amp; Quality</div>
2403
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('health_score')">Health Score</button>
2404
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('vital_signs')">Vital Signs</button>
2405
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('health_trend')">Health Trend</button>
2406
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('hotspots')">Hotspots</button>
2407
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('maintainability')">Maintainability Index</button>
2408
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('complexity')">Complexity</button>
2409
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('crap_score')">CRAP Score</button>
2410
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('risk_profile')">Risk Profile</button>
2411
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('baseline_compare')">Baseline Compare</button>
2412
+ <button class="mg-analyze-btn" onclick="runMonographAnalysis('author_analytics')">Author Analytics</button>
2413
+ </div>
2414
+ <div id="mg-analyze-result"><span style="color:var(--border);">SELECT AN ANALYSIS →</span></div>
2415
+ </div>
2416
+ </div>
2417
+
2418
+ <!-- QUERY -->
2419
+ <div id="mg-pane-query" class="mg-pane">
2420
+ <div id="mg-query-pane">
2421
+ <div class="mg-query-section">
2422
+ <div class="mg-query-label">Search nodes</div>
2423
+ <div class="mg-input-row">
2424
+ <input class="mg-input" id="mg-search-input" placeholder="Type to search nodes by name…" oninput="runMonographSearch(this.value)">
2425
+ </div>
2426
+ <div class="mg-result-box" id="mg-search-result" style="min-height:80px;">–</div>
2427
+ </div>
2428
+ <div class="mg-query-section">
2429
+ <div class="mg-query-label">Impact / Blast Radius</div>
2430
+ <div class="mg-input-row">
2431
+ <input class="mg-input" id="mg-impact-input" placeholder="File path or node name…" onkeydown="if(event.key==='Enter')runMonographImpact()">
2432
+ <select id="mg-impact-dir" style="background:rgba(255,255,255,0.04);border:1px solid var(--border);border-radius:3px;padding:5px 8px;color:var(--text);font-family:'Azeret Mono',monospace;font-size:9px;">
2433
+ <option value="both">Both</option>
2434
+ <option value="upstream">Upstream</option>
2435
+ <option value="downstream">Downstream</option>
2436
+ </select>
2437
+ <button class="mg-action-btn" onclick="runMonographImpact()">ANALYZE</button>
2438
+ </div>
2439
+ <div class="mg-result-box" id="mg-impact-result">–</div>
2440
+ </div>
2441
+ <div class="mg-query-section">
2442
+ <div class="mg-query-label">Context (360° view)</div>
2443
+ <div class="mg-input-row">
2444
+ <input class="mg-input" id="mg-context-input" placeholder="File path or node name…" onkeydown="if(event.key==='Enter')runMonographContext()">
2445
+ <button class="mg-action-btn" onclick="runMonographContext()">CONTEXT</button>
2446
+ </div>
2447
+ <div class="mg-result-box" id="mg-context-result">–</div>
2448
+ </div>
2449
+ <div class="mg-query-section">
2450
+ <div class="mg-query-label">Explain node</div>
2451
+ <div class="mg-input-row">
2452
+ <input class="mg-input" id="mg-explain-input" placeholder="Node name or ID…" onkeydown="if(event.key==='Enter')runMonographExplain()">
2453
+ <button class="mg-action-btn" onclick="runMonographExplain()">EXPLAIN</button>
2454
+ </div>
2455
+ <div class="mg-result-box" id="mg-explain-result">–</div>
2456
+ </div>
2457
+ <div class="mg-query-section">
2458
+ <div class="mg-query-label">Shortest path</div>
2459
+ <div class="mg-input-row">
2460
+ <input class="mg-input" id="mg-path-from" placeholder="From node…">
2461
+ <span style="color:var(--muted);font-size:11px;">→</span>
2462
+ <input class="mg-input" id="mg-path-to" placeholder="To node…">
2463
+ <button class="mg-action-btn" onclick="runMonographPath()">FIND PATH</button>
2464
+ </div>
2465
+ <div class="mg-result-box" id="mg-path-result">–</div>
2466
+ </div>
2467
+ <div class="mg-query-section">
2468
+ <div class="mg-query-label">Rename (dry-run)</div>
2469
+ <div class="mg-input-row">
2470
+ <input class="mg-input" id="mg-rename-from" placeholder="Current symbol name…">
2471
+ <span style="color:var(--muted);font-size:11px;">→</span>
2472
+ <input class="mg-input" id="mg-rename-to" placeholder="New name…">
2473
+ <button class="mg-action-btn" onclick="runMonographRename()">PREVIEW</button>
2474
+ </div>
2475
+ <div class="mg-result-box" id="mg-rename-result">–</div>
2476
+ </div>
2477
+ <div class="mg-query-section">
2478
+ <div class="mg-query-label">Cypher Query</div>
2479
+ <div class="mg-input-row">
2480
+ <input class="mg-input" id="mg-cypher-input" placeholder="MATCH (n:Class) RETURN n.name LIMIT 10" onkeydown="if(event.key==='Enter')runMonographCypher()" style="flex:3;">
2481
+ <button class="mg-action-btn" onclick="runMonographCypher()">QUERY</button>
2482
+ </div>
2483
+ <div class="mg-result-box" id="mg-cypher-result">–</div>
2484
+ </div>
2485
+ <div class="mg-query-section">
2486
+ <div class="mg-query-label">Ask the graph</div>
2487
+ <div class="mg-input-row">
2488
+ <input class="mg-input" id="mg-ask-input" placeholder="Ask a question about the codebase…" onkeydown="if(event.key==='Enter')runMonographAsk()">
2489
+ <select id="mg-ask-mode" style="background:rgba(255,255,255,0.04);border:1px solid var(--border);border-radius:3px;padding:5px 8px;color:var(--text);font-family:'Azeret Mono',monospace;font-size:9px;">
2490
+ <option value="bfs">BFS</option>
2491
+ <option value="dfs">DFS</option>
2492
+ </select>
2493
+ <button class="mg-action-btn" onclick="runMonographAsk()">ASK</button>
2494
+ </div>
2495
+ <div class="mg-result-box" id="mg-ask-result">–</div>
2496
+ </div>
2497
+ </div>
2498
+ </div>
2499
+
2500
+ <!-- EXPORT -->
2501
+ <div id="mg-pane-export" class="mg-pane">
2502
+ <div id="mg-export-pane">
2503
+ <div style="font-family:'Syne',sans-serif;font-size:11px;font-weight:700;letter-spacing:0.1em;color:var(--text);margin-bottom:4px;">EXPORT GRAPH</div>
2504
+ <div style="font-family:'Azeret Mono',monospace;font-size:9px;color:var(--muted);margin-bottom:4px;">Download the knowledge graph in various formats</div>
2505
+ <div style="font-family:'Azeret Mono',monospace;font-size:9px;color:rgba(245,158,11,0.7);margin-bottom:10px;padding:6px 10px;background:rgba(245,158,11,0.06);border:1px solid rgba(245,158,11,0.2);border-radius:3px;">
2506
+ ⚠ Dashboard exports use the visible 500-node subset. For full <span id="mg-export-total-hint">54K</span>-node exports, use: <code>monomind monograph export --format json</code>
2507
+ </div>
2508
+ <div id="mg-export-status"></div>
2509
+ <div class="mg-export-grid">
2510
+ <div class="mg-export-card" onclick="downloadMonographExport('json')">
2511
+ <div class="mg-export-name">JSON</div>
2512
+ <div class="mg-export-desc">Full graph with all node and edge metadata</div>
2513
+ <span class="mg-export-ext">.json</span>
2514
+ </div>
2515
+ <div class="mg-export-card" onclick="downloadMonographExport('graphml')">
2516
+ <div class="mg-export-name">GraphML</div>
2517
+ <div class="mg-export-desc">XML with community attributes for Gephi / yEd</div>
2518
+ <span class="mg-export-ext">.graphml</span>
2519
+ </div>
2520
+ <div class="mg-export-card" onclick="downloadMonographExport('mermaid')">
2521
+ <div class="mg-export-name">Mermaid</div>
2522
+ <div class="mg-export-desc">Flowchart diagram with community subgraphs</div>
2523
+ <span class="mg-export-ext">.mmd</span>
2524
+ </div>
2525
+ <div class="mg-export-card" onclick="downloadMonographExport('dot')">
2526
+ <div class="mg-export-name">DOT (Graphviz)</div>
2527
+ <div class="mg-export-desc">Graphviz DOT format for layout engines</div>
2528
+ <span class="mg-export-ext">.dot</span>
2529
+ </div>
2530
+ <div class="mg-export-card" onclick="downloadMonographExport('gexf')">
2531
+ <div class="mg-export-name">GEXF</div>
2532
+ <div class="mg-export-desc">GEXF 1.3 XML for Gephi with full metadata</div>
2533
+ <span class="mg-export-ext">.gexf</span>
2534
+ </div>
2535
+ <div class="mg-export-card" onclick="downloadMonographExport('cypher')">
2536
+ <div class="mg-export-name">Cypher</div>
2537
+ <div class="mg-export-desc">Neo4j CREATE statements for graph database</div>
2538
+ <span class="mg-export-ext">.cypher</span>
2539
+ </div>
2540
+ <div class="mg-export-card" onclick="downloadMonographExport('csv-nodes')">
2541
+ <div class="mg-export-name">CSV (Nodes)</div>
2542
+ <div class="mg-export-desc">Node list with id, label, type, degree</div>
2543
+ <span class="mg-export-ext">nodes.csv</span>
2544
+ </div>
2545
+ <div class="mg-export-card" onclick="downloadMonographExport('csv-edges')">
2546
+ <div class="mg-export-name">CSV (Edges)</div>
2547
+ <div class="mg-export-desc">Edge list with source, target, relation</div>
2548
+ <span class="mg-export-ext">edges.csv</span>
2549
+ </div>
2550
+ <div class="mg-export-card" onclick="downloadMonographExport('matrix')">
2551
+ <div class="mg-export-name">Adjacency Matrix</div>
2552
+ <div class="mg-export-desc">Square matrix CSV representation</div>
2553
+ <span class="mg-export-ext">matrix.csv</span>
2554
+ </div>
2555
+ <div class="mg-export-card" onclick="downloadMonographExport('html')">
2556
+ <div class="mg-export-name">HTML Report</div>
2557
+ <div class="mg-export-desc">Open interactive graph visualization</div>
2558
+ <span class="mg-export-ext">.html</span>
2559
+ </div>
2560
+ </div>
2561
+ </div>
2562
+ </div>
2563
+
2564
+ <!-- REPORT -->
2565
+ <div id="mg-pane-report" class="mg-pane">
2566
+ <div id="mg-report-pane">
2567
+ <div id="mg-report-content"><div style="color:var(--muted);font-family:'Azeret Mono',monospace;font-size:10px;">Loading report…</div></div>
2568
+ </div>
2569
+ </div>
2570
+
2571
+ <!-- WIKI / DOCS KG -->
2572
+ <div id="mg-pane-wiki" class="mg-pane">
2573
+ <div id="mg-wiki-layout">
2574
+
2575
+ <!-- Left sidebar: stats + search + results list -->
2576
+ <div id="mg-wiki-sidebar">
2577
+ <div id="mg-wiki-statsbar">
2578
+ <div class="wiki-stat-cell"><div class="wiki-stat-cell-label">Nodes</div><div class="wiki-stat-cell-val" id="wstat-nodes">—</div></div>
2579
+ <div class="wiki-stat-cell"><div class="wiki-stat-cell-label">Sections</div><div class="wiki-stat-cell-val" id="wstat-sections">—</div></div>
2580
+ <div class="wiki-stat-cell"><div class="wiki-stat-cell-label">Functions</div><div class="wiki-stat-cell-val" id="wstat-fns">—</div></div>
2581
+ <div class="wiki-stat-cell" style="cursor:pointer;flex:0;padding:7px 10px;" onclick="wikiRefresh()" title="Refresh graph data"><div class="wiki-stat-cell-label">↺</div><div class="wiki-stat-cell-val" style="font-size:9px;">SYNC</div></div>
2582
+ <div class="wiki-stat-cell" style="cursor:pointer;flex:0;padding:7px 10px;" id="wstat-build-docs" onclick="wikiBuildDocs()" title="Parse .md/.mdx/.txt/.rst files into Section nodes"><div class="wiki-stat-cell-label">📄</div><div class="wiki-stat-cell-val" style="font-size:9px;">BUILD DOCS</div></div>
2583
+ </div>
2584
+ <div id="mg-wiki-search-bar">
2585
+ <input id="mg-wiki-q" placeholder="Search nodes… (name, type, content)" oninput="wikiOnSearch(this.value)" autocomplete="off">
2586
+ <div class="wiki-search-mode" id="wiki-search-mode"></div>
2587
+ </div>
2588
+ <div id="mg-wiki-mode-bar" style="display:flex;gap:3px;padding:6px 10px 0;flex-shrink:0;">
2589
+ <button class="wiki-pill active" data-mode="all" onclick="wikiSetMode(this,'all')" style="flex:1;text-align:center;">ALL</button>
2590
+ <button class="wiki-pill" data-mode="docs" onclick="wikiSetMode(this,'docs')" style="flex:1;text-align:center;color:rgba(0,229,135,0.7);">DOCS</button>
2591
+ <button class="wiki-pill" data-mode="code" onclick="wikiSetMode(this,'code')" style="flex:1;text-align:center;color:rgba(0,229,200,0.7);">CODE</button>
2592
+ </div>
2593
+ <div id="mg-wiki-pills">
2594
+ <button class="wiki-pill active" data-type="" onclick="wikiSetType(this,'')">ALL</button>
2595
+ <button class="wiki-pill" data-type="File" onclick="wikiSetType(this,'File')">FILE</button>
2596
+ <button class="wiki-pill" data-type="Function" onclick="wikiSetType(this,'Function')">FN</button>
2597
+ <button class="wiki-pill" data-type="Class" onclick="wikiSetType(this,'Class')">CLASS</button>
2598
+ <button class="wiki-pill" data-type="Interface" onclick="wikiSetType(this,'Interface')">IFACE</button>
2599
+ <button class="wiki-pill" data-type="Method" onclick="wikiSetType(this,'Method')">METHOD</button>
2600
+ <button class="wiki-pill" data-type="Section" onclick="wikiSetType(this,'Section')" style="color:rgba(0,229,135,0.8);">§ SECTION</button>
2601
+ <button class="wiki-pill" data-type="Concept" onclick="wikiSetType(this,'Concept')" style="color:rgba(0,229,135,0.8);">💡 CONCEPT</button>
2602
+ </div>
2603
+ <div id="mg-wiki-list"><div class="wiki-empty" style="height:120px;font-size:9px;">Loading…</div></div>
2604
+ </div>
2605
+
2606
+ <!-- Right detail panel -->
2607
+ <div id="mg-wiki-detail">
2608
+ <div class="wiki-empty">
2609
+ <span style="font-size:22px;opacity:0.3;">◈</span>
2610
+ <span>Select a node to explore</span>
2611
+ <span style="font-size:9px;color:var(--dim);">Search or browse the sidebar</span>
2612
+ </div>
2613
+ </div>
2614
+
2615
+ </div>
2616
+ </div>
2617
+
2618
+ </div>
2619
+ </div>
2620
+
1794
2621
  <!-- ═══════════════════════════ HEADER ═══════════════════════════ -->
1795
2622
  <div id="header">
1796
2623
  <div id="header-left">
@@ -1798,6 +2625,8 @@
1798
2625
  <div id="header-meta">
1799
2626
  <span><span id="conn-dot"></span><span id="conn-label">CONNECTING</span></span>
1800
2627
  <button onclick="openPalaceOverlay()" style="background:none;border:1px solid rgba(0,229,200,0.3);color:var(--teal);font-family:'Azeret Mono',monospace;font-size:9px;letter-spacing:0.1em;padding:3px 9px;cursor:pointer;border-radius:3px;transition:background 0.15s;" onmouseover="this.style.background='rgba(0,229,200,0.1)'" onmouseout="this.style.background='none'">⬡ MEMORY PALACE</button>
2628
+ <button onclick="openMonographOverlay()" style="background:none;border:1px solid rgba(123,97,255,0.4);color:rgba(123,97,255,0.9);font-family:'Azeret Mono',monospace;font-size:9px;letter-spacing:0.1em;padding:3px 9px;cursor:pointer;border-radius:3px;transition:background 0.15s;margin-left:4px;" onmouseover="this.style.background='rgba(123,97,255,0.12)'" onmouseout="this.style.background='none'">◈ MONOGRAPH</button>
2629
+ <button onclick="openMastermindOverlay()" style="background:none;border:1px solid rgba(200,120,255,0.45);color:rgba(210,140,255,0.9);font-family:'Azeret Mono',monospace;font-size:9px;letter-spacing:0.1em;padding:3px 9px;cursor:pointer;border-radius:3px;transition:background 0.15s;margin-left:4px;" onmouseover="this.style.background='rgba(180,100,255,0.14)'" onmouseout="this.style.background='none'">🧠 MASTERMIND</button>
1801
2630
  </div>
1802
2631
  </div>
1803
2632
  <div id="header-right">
@@ -1860,6 +2689,21 @@
1860
2689
  </div>
1861
2690
  </div>
1862
2691
 
2692
+ <!-- ─── MASTERMIND ORGS ──────────────────────────────────────── -->
2693
+ <div class="panel open" id="panel-orgs">
2694
+ <div class="panel-header" onclick="togglePanel('panel-orgs')">
2695
+ <div class="panel-title"><span class="live-dot" id="orgs-live-dot"></span>MASTERMIND ORGS</div>
2696
+ <div style="display:flex;align-items:center;gap:6px;">
2697
+ <span style="font-size:9px;color:var(--dim);letter-spacing:0.08em;" id="orgs-running-label"></span>
2698
+ <span class="panel-badge" id="orgs-badge">0</span>
2699
+ <span class="panel-chevron">›</span>
2700
+ </div>
2701
+ </div>
2702
+ <div class="panel-body" id="orgs-body">
2703
+ <div class="orgs-empty" id="orgs-empty">Loading orgs…</div>
2704
+ </div>
2705
+ </div>
2706
+
1863
2707
  <!-- ─── SESSION JOURNAL ────────────────────────────────────────── -->
1864
2708
  <div class="panel open" id="panel-activity">
1865
2709
  <div class="panel-header" onclick="togglePanel('panel-activity')">
@@ -1892,7 +2736,7 @@ const MAX_ACTIVITY = 80;
1892
2736
  // Track which panels are currently open (match initial HTML .open classes)
1893
2737
  const expandedPanels = new Set([
1894
2738
  'panel-projects',
1895
- 'panel-tokens', 'panel-activity'
2739
+ 'panel-tokens', 'panel-activity', 'panel-orgs'
1896
2740
  ]);
1897
2741
 
1898
2742
  // Section name → panel id mapping
@@ -1923,9 +2767,10 @@ window.togglePanel = function(panelId) {
1923
2767
  };
1924
2768
 
1925
2769
  function renderPanelById(panelId) {
1926
- if (!appData) return;
1927
2770
  switch (panelId) {
1928
- case 'panel-tokens': renderTokens(appData); break;
2771
+ case 'panel-tokens': if (appData) renderTokens(appData); break;
2772
+ case 'panel-orgs': renderOrgs(); break;
2773
+ case 'panel-loops': renderLoops(); break;
1929
2774
  }
1930
2775
  }
1931
2776
 
@@ -2088,11 +2933,14 @@ function timeAgo(ms) {
2088
2933
 
2089
2934
  // Track which project dir is currently selected (null = default/server project)
2090
2935
  let selectedProjectDir = null;
2936
+ let _codeGraphLoaded = false;
2091
2937
 
2092
2938
  async function selectProject(dir) {
2093
2939
  if (selectedProjectDir === dir) return;
2094
2940
  selectedProjectDir = dir;
2095
2941
  _codeGraphLoaded = false; kgCodeGraph.reset();
2942
+ // Reset monograph caches so next open reloads data for the new project
2943
+ mgGraphData = null; mgReportData = null; _wikiData = null; _monographIframeLoaded = false;
2096
2944
  renderJournal();
2097
2945
  try {
2098
2946
  const res = await fetch(`/api/data?dir=${encodeURIComponent(dir)}`);
@@ -2190,6 +3038,14 @@ function renderSessions(data) {
2190
3038
  <span style="font-size:9px;color:var(--teal);letter-spacing:0.06em;">EXPLORE →</span>
2191
3039
  </span>
2192
3040
  </div>`;
3041
+ html += `<div class="palace-row" style="cursor:pointer;margin-top:4px;background:rgba(123,97,255,0.05);border-color:rgba(123,97,255,0.2);" onclick="openMonographOverlay()">
3042
+ <span class="palace-label" style="color:rgba(123,97,255,0.8);">MONOGRAPH</span>
3043
+ <span style="display:flex;align-items:center;gap:6px;">
3044
+ <span style="font-size:11px;color:var(--text);font-weight:500;" id="mg-node-count-anim">–</span>
3045
+ <span style="font-size:9px;color:var(--muted);">NODES</span>
3046
+ <span style="font-size:9px;color:rgba(123,97,255,0.9);letter-spacing:0.06em;">EXPLORE →</span>
3047
+ </span>
3048
+ </div>`;
2193
3049
 
2194
3050
  body.innerHTML = html;
2195
3051
  }
@@ -2942,50 +3798,198 @@ function renderMemory(data) {
2942
3798
  }
2943
3799
 
2944
3800
  // ═══════════════════════════════════════════════════════════════════
2945
- // SCHEDULED LOOPS PANEL
3801
+ // MASTERMIND ORGS PANEL
2946
3802
  // ═══════════════════════════════════════════════════════════════════
2947
- let _loopCountdownTimers = [];
2948
-
2949
- function fmtCountdown(nextRunAt) {
2950
- if (!nextRunAt) return '';
2951
- const diff = nextRunAt - Date.now();
2952
- if (diff <= 0) return 'running now';
2953
- const s = Math.floor(diff / 1000);
2954
- if (s < 60) return `next in ${s}s`;
2955
- const m = Math.floor(s / 60), rs = s % 60;
2956
- return `next in ${m}m ${rs}s`;
2957
- }
3803
+ const orgEventLog = {}; // org name → array of recent events
2958
3804
 
2959
- function fmtRelTime(ts) {
2960
- if (!ts) return '—';
2961
- const diff = Date.now() - ts;
2962
- if (diff < 5000) return 'just now';
2963
- if (diff < 60000) return `${Math.floor(diff/1000)}s ago`;
2964
- if (diff < 3600000) return `${Math.floor(diff/60000)}m ago`;
2965
- return new Date(ts).toLocaleTimeString();
3805
+ function fmtOrgEventType(type) {
3806
+ const map = {
3807
+ 'org:start': 'STARTED', 'org:stop': 'STOPPED', 'org:complete': 'COMPLETE',
3808
+ 'org:agent:online': 'AGENT ON', 'org:comms': 'COMMS', 'org:checkpoint': 'CHECK',
3809
+ 'org:create': 'CREATED',
3810
+ };
3811
+ return map[type] || (typeof type === 'string' ? type.toUpperCase() : 'UNKNOWN');
2966
3812
  }
2967
3813
 
2968
- async function renderLoops() {
3814
+ let _orgRenderInFlight = false;
3815
+ async function renderOrgs() {
3816
+ if (_orgRenderInFlight) return;
3817
+ _orgRenderInFlight = true;
2969
3818
  try {
2970
- const r = await fetch('/api/loops');
2971
- const { loops = [] } = await r.json();
2972
- const body = document.getElementById('loops-body');
2973
- const badge = document.getElementById('loops-badge');
2974
- const dot = document.getElementById('loops-live-dot');
3819
+ const _orgCtrl = new AbortController();
3820
+ const _orgTimeout = setTimeout(() => _orgCtrl.abort(), 10000);
3821
+ let orgs;
3822
+ try {
3823
+ const r = await fetch('/api/orgs', { signal: _orgCtrl.signal });
3824
+ orgs = await r.json();
3825
+ } finally {
3826
+ clearTimeout(_orgTimeout);
3827
+ }
3828
+ if (!Array.isArray(orgs)) return;
3829
+ const body = document.getElementById('orgs-body');
3830
+ const badge = document.getElementById('orgs-badge');
3831
+ const dot = document.getElementById('orgs-live-dot');
3832
+ const runLabel = document.getElementById('orgs-running-label');
2975
3833
  if (!body) return;
2976
3834
 
2977
- // Clear old countdown timers
2978
- _loopCountdownTimers.forEach(clearInterval);
2979
- _loopCountdownTimers = [];
2980
-
2981
- const active = loops.filter(l => l.status !== 'complete');
2982
- badge.textContent = active.length || '0';
2983
-
2984
- if (dot) {
2985
- dot.className = 'live-dot' + (active.length > 0 ? ' green' : '');
3835
+ badge.textContent = orgs.length;
3836
+ const runningCount = orgs.filter(o => o.running).length;
3837
+ if (runningCount > 0) {
3838
+ dot.classList.add('live');
3839
+ runLabel.textContent = `${runningCount} RUNNING`;
3840
+ runLabel.style.color = 'var(--green)';
3841
+ } else {
3842
+ dot.classList.remove('live');
3843
+ runLabel.textContent = '';
2986
3844
  }
2987
3845
 
2988
- if (!loops.length) {
3846
+ if (!orgs.length) {
3847
+ body.innerHTML = `<div class="orgs-empty">
3848
+ <div class="orgs-empty-icon">⬡</div>
3849
+ <div class="orgs-empty-label">NO PERSISTENT ORGS</div>
3850
+ <div class="orgs-empty-sub">Orgs are named agent teams that coordinate across sessions — boss assigns work, specialists execute in parallel.</div>
3851
+ <div class="orgs-empty-cmd">/mastermind:createorg</div>
3852
+ </div>`;
3853
+ return;
3854
+ }
3855
+
3856
+ let html = `<div class="org-row" style="background:rgba(0,0,0,0.2);">
3857
+ <div class="org-header" style="grid-column:1/3">ORG</div>
3858
+ <div class="org-header">ROLES</div>
3859
+ <div class="org-header">TOPOLOGY</div>
3860
+ <div class="org-header">STATUS</div>
3861
+ <div class="org-header">ACTION</div>
3862
+ </div>`;
3863
+
3864
+ for (const org of orgs) {
3865
+ const events = (orgEventLog[org.name] || []).slice(-3);
3866
+ const statusHtml = org.running
3867
+ ? `<span class="org-live-badge">LIVE</span>`
3868
+ : `<span style="font-size:9px;color:var(--dim);">IDLE</span>`;
3869
+ // Use data- attributes for org name to avoid JS string injection via onclick
3870
+ const actionHtml = org.running
3871
+ ? `<button class="org-btn stop" data-org-stop="${escHtml(org.name)}">STOP</button>`
3872
+ : `<button class="org-btn" data-org-view="${escHtml(org.name)}">VIEW</button>`;
3873
+
3874
+ html += `<div class="org-row" data-org="${escHtml(org.name)}">
3875
+ <div class="org-cell" style="padding-left:10px;">
3876
+ <div class="org-status-dot ${org.running ? 'running' : ''}"></div>
3877
+ </div>
3878
+ <div class="org-cell">
3879
+ <div class="org-name">${escHtml(org.name)}</div>
3880
+ <div class="org-goal">${escHtml((org.goal || '').slice(0, 70))}${(org.goal || '').length > 70 ? '…' : ''}</div>
3881
+ </div>
3882
+ <div class="org-cell" style="color:var(--teal)">${org.roles || 0}</div>
3883
+ <div class="org-cell" style="color:var(--muted)">${escHtml(org.topology || 'hierarchical')}</div>
3884
+ <div class="org-cell">${statusHtml}</div>
3885
+ <div class="org-cell">${actionHtml}</div>
3886
+ </div>`;
3887
+
3888
+ if (events.length) {
3889
+ for (const ev of events) {
3890
+ const t = new Date(ev.ts).toLocaleTimeString('en', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
3891
+ const rawDetail = ev.role ? ` (${ev.role})` : ev.msg ? ` — ${ev.msg}` : ev.agent ? ` → ${ev.agent}` : '';
3892
+ html += `<div class="org-event-log">
3893
+ <span class="org-event-time">${t}</span>
3894
+ <span class="org-event-type">${escHtml(fmtOrgEventType(ev.type))}</span>
3895
+ <span>${escHtml(rawDetail)}</span>
3896
+ </div>`;
3897
+ }
3898
+ }
3899
+ }
3900
+
3901
+ body.innerHTML = html;
3902
+
3903
+ // Attach event handlers via addEventListener (avoids onclick JS injection)
3904
+ body.querySelectorAll('[data-org-stop]').forEach(btn => {
3905
+ btn.addEventListener('click', () => stopOrg(btn.dataset.orgStop));
3906
+ });
3907
+ body.querySelectorAll('[data-org-view]').forEach(btn => {
3908
+ btn.addEventListener('click', () => openMastermindForOrg(btn.dataset.orgView));
3909
+ });
3910
+ } catch { /* server may not be ready yet */ } finally { _orgRenderInFlight = false; }
3911
+ }
3912
+
3913
+ async function stopOrg(name) {
3914
+ try {
3915
+ await fetch(`/api/orgs/${encodeURIComponent(name)}/stop`, { method: 'POST' });
3916
+ setTimeout(renderOrgs, 500);
3917
+ } catch {}
3918
+ }
3919
+
3920
+ function openMastermindForOrg(name) {
3921
+ // Open the mastermind overlay; the org name is stored as a custom event
3922
+ // for any future mastermind panel that wants to filter by org
3923
+ document.dispatchEvent(new CustomEvent('mastermind:open-org', { detail: { org: name } }));
3924
+ const btn = document.getElementById('mastermind-btn');
3925
+ if (btn) btn.click();
3926
+ }
3927
+
3928
+ // Debounce timer for SSE-triggered renders (avoid one HTTP fetch per event)
3929
+ let _orgRenderDebounce = null;
3930
+
3931
+ // Listen for org events from the SSE stream and update the event log
3932
+ function handleOrgEvent(ev) {
3933
+ const orgName = ev.org;
3934
+ if (!orgName || typeof orgName !== 'string') return;
3935
+ // Guard against prototype-polluting keys from attacker-controlled SSE payloads
3936
+ if (orgName === '__proto__' || orgName === 'constructor' || orgName === 'prototype') return;
3937
+ if (!Object.prototype.hasOwnProperty.call(orgEventLog, orgName)) orgEventLog[orgName] = [];
3938
+ orgEventLog[orgName].push(ev);
3939
+ if (orgEventLog[orgName].length > 20) orgEventLog[orgName].shift();
3940
+ // Debounce: re-render at most once per 500ms on SSE bursts
3941
+ const panel = document.getElementById('panel-orgs');
3942
+ if (panel && panel.classList.contains('open')) {
3943
+ clearTimeout(_orgRenderDebounce);
3944
+ _orgRenderDebounce = setTimeout(renderOrgs, 500);
3945
+ }
3946
+ }
3947
+
3948
+ // ═══════════════════════════════════════════════════════════════════
3949
+ // SCHEDULED LOOPS PANEL
3950
+ // ═══════════════════════════════════════════════════════════════════
3951
+ let _loopCountdownTimers = [];
3952
+
3953
+ function fmtCountdown(nextRunAt) {
3954
+ if (!nextRunAt) return '';
3955
+ const diff = nextRunAt - Date.now();
3956
+ if (diff <= 0) return 'running now';
3957
+ const s = Math.floor(diff / 1000);
3958
+ if (s < 60) return `next in ${s}s`;
3959
+ const m = Math.floor(s / 60), rs = s % 60;
3960
+ return `next in ${m}m ${rs}s`;
3961
+ }
3962
+
3963
+ function fmtRelTime(ts) {
3964
+ if (!ts) return '—';
3965
+ const diff = Date.now() - ts;
3966
+ if (diff < 5000) return 'just now';
3967
+ if (diff < 60000) return `${Math.floor(diff/1000)}s ago`;
3968
+ if (diff < 3600000) return `${Math.floor(diff/60000)}m ago`;
3969
+ return new Date(ts).toLocaleTimeString();
3970
+ }
3971
+
3972
+ async function renderLoops() {
3973
+ try {
3974
+ const r = await fetch('/api/loops');
3975
+ const { loops = [] } = await r.json();
3976
+ const body = document.getElementById('loops-body');
3977
+ const badge = document.getElementById('loops-badge');
3978
+ const dot = document.getElementById('loops-live-dot');
3979
+ if (!body) return;
3980
+
3981
+ // Clear old countdown timers
3982
+ _loopCountdownTimers.forEach(clearInterval);
3983
+ _loopCountdownTimers = [];
3984
+
3985
+ const active = loops.filter(l => l.status !== 'complete');
3986
+ badge.textContent = active.length || '0';
3987
+
3988
+ if (dot) {
3989
+ dot.className = 'live-dot' + (active.length > 0 ? ' green' : '');
3990
+ }
3991
+
3992
+ if (!loops.length) {
2989
3993
  body.innerHTML = '<div class="loops-empty">NO ACTIVE LOOPS</div>';
2990
3994
  return;
2991
3995
  }
@@ -3509,6 +4513,34 @@ async function fetchInitial() {
3509
4513
  renderLoops();
3510
4514
  setInterval(renderLoops, 10000);
3511
4515
 
4516
+ // ═══════════════════════════════════════════════════════════════════
4517
+ // ORGS POLLING + MASTERMIND SSE FOR LIVE ORG EVENTS
4518
+ // ═══════════════════════════════════════════════════════════════════
4519
+ renderOrgs();
4520
+ setInterval(renderOrgs, 15000);
4521
+
4522
+ (function connectMastermindStream() {
4523
+ let mmSse = null;
4524
+ function connect() {
4525
+ // Guard against duplicate open connections
4526
+ if (mmSse && mmSse.readyState !== EventSource.CLOSED) return;
4527
+ try {
4528
+ mmSse = new EventSource('/api/mastermind-stream');
4529
+ mmSse.onmessage = function(e) {
4530
+ try {
4531
+ const ev = JSON.parse(e.data);
4532
+ if (ev.type && ev.type.startsWith('org:')) handleOrgEvent(ev);
4533
+ } catch(_) {}
4534
+ };
4535
+ mmSse.onerror = function() {
4536
+ try { mmSse.close(); } catch(_) {}
4537
+ setTimeout(connect, 10000);
4538
+ };
4539
+ } catch(_) { setTimeout(connect, 10000); }
4540
+ }
4541
+ connect();
4542
+ })();
4543
+
3512
4544
  // ═══════════════════════════════════════════════════════════════════
3513
4545
  // LIVE BORDER REFRESH TIMER
3514
4546
  // ═══════════════════════════════════════════════════════════════════
@@ -3577,10 +4609,10 @@ window.switchPalaceTab = function(tab) {
3577
4609
  b.classList.toggle('active', !!(btnTab && btnTab[1] === tab));
3578
4610
  });
3579
4611
  document.querySelectorAll('.po-tab-pane').forEach(p => p.classList.remove('active'));
3580
- const paneIds = { drawers: 'po-drawers-tab', sessions: 'po-sessions-tab', chunks: 'po-chunks-tab', routing: 'po-routing-tab', swarm: 'po-swarm-tab', graph: 'po-graph-tab', knowledge: 'po-knowledge-tab' };
4612
+ const paneIds = { drawers: 'po-drawers-tab', sessions: 'po-sessions-tab', chunks: 'po-chunks-tab', routing: 'po-routing-tab', swarm: 'po-swarm-tab', graph: 'po-graph-tab', usage: 'po-usage-tab' };
3581
4613
  const pane = document.getElementById(paneIds[tab]);
3582
4614
  if (pane) pane.classList.add('active');
3583
- if (tab !== 'knowledge' && typeof kgCodeGraph !== 'undefined' && kgCodeGraph.stop) kgCodeGraph.stop();
4615
+ if (typeof kgCodeGraph !== 'undefined' && kgCodeGraph.stop) kgCodeGraph.stop();
3584
4616
  if (palaceData) renderPalaceTab(tab);
3585
4617
  };
3586
4618
 
@@ -3626,9 +4658,9 @@ async function loadPalaceData() {
3626
4658
  }
3627
4659
 
3628
4660
  // ═══════════════════════════════════════════════════════════════════
3629
- // GRAPHIFY REPORT PARSER (used by Memory Palace knowledge tab)
4661
+ // MONOGRAPH REPORT PARSER (used by Memory Palace knowledge tab)
3630
4662
  // ═══════════════════════════════════════════════════════════════════
3631
- function parseGraphifyReport(md) {
4663
+ function parseMonographReport(md) {
3632
4664
  const SECTION_ICONS = {
3633
4665
  'corpus check': '📂', 'summary': '📊', 'god nodes': '⭐',
3634
4666
  'surprising connections': '🔗', 'communities': '🏘', 'community': '🏘',
@@ -3678,7 +4710,97 @@ function renderPalaceTab(tab) {
3678
4710
  else if (tab === 'routing') renderHooks(appData || {});
3679
4711
  else if (tab === 'swarm') renderPalaceSwarm();
3680
4712
  else if (tab === 'graph') { renderAgentGraph(palaceData.graph || { nodes: [], edges: [] }); }
3681
- else if (tab === 'knowledge') { renderPalaceKnowledge(); loadGraphifyIframe(); refreshWatchStatus(); }
4713
+ else if (tab === 'usage') renderPalaceUsage();
4714
+ }
4715
+
4716
+ // ── USAGE TAB ────────────────────────────────────────────────────────────────
4717
+ let _usagePeriod = 'today';
4718
+
4719
+ window.switchUsagePeriod = function(period) {
4720
+ _usagePeriod = period;
4721
+ document.querySelectorAll('.po-usage-ptab').forEach(b => {
4722
+ const m = (b.getAttribute('onclick') || '').match(/'([\w]+)'/);
4723
+ b.classList.toggle('active', !!(m && m[1] === period));
4724
+ });
4725
+ renderPalaceUsage();
4726
+ };
4727
+
4728
+ async function renderPalaceUsage() {
4729
+ const body = document.getElementById('po-usage-body');
4730
+ if (!body) return;
4731
+ body.innerHTML = '<div style="color:var(--muted);font-size:10px;padding:20px 24px;">Loading…</div>';
4732
+
4733
+ const dir = selectedProjectDir || '';
4734
+ const apiUrl = `/api/token-usage?period=${_usagePeriod}${dir ? '&dir=' + encodeURIComponent(dir) : ''}`;
4735
+ let data;
4736
+ try {
4737
+ const res = await fetch(apiUrl);
4738
+ data = res.ok ? await res.json() : null;
4739
+ } catch { data = null; }
4740
+
4741
+ if (!data) {
4742
+ body.innerHTML = '<div style="color:var(--muted);font-size:10px;padding:20px 24px;">No usage data available.</div>';
4743
+ return;
4744
+ }
4745
+
4746
+ const { totalCost, totalCalls, totalIn, totalOut, totalCR, projects, modelBreakdown, categoryBreakdown, toolBreakdown, mcpBreakdown } = data;
4747
+ const totalCached = totalCR || 0;
4748
+ const cacheEff = (totalIn + totalCached) > 0 ? Math.round(totalCached / (totalIn + totalCached) * 100) : 0;
4749
+
4750
+ function _fmtC(n) { if (!n) return '$0.00'; if (n >= 100) return '$'+n.toFixed(2); if (n >= 1) return '$'+n.toFixed(3); return '$'+n.toFixed(4); }
4751
+ function _fmtN(n) { if (!n) return '0'; if (n >= 1e9) return (n/1e9).toFixed(1)+'B'; if (n >= 1e6) return (n/1e6).toFixed(1)+'M'; if (n >= 1e3) return (n/1e3).toFixed(1)+'K'; return String(n); }
4752
+ function _bar(pct, color) { return `<div class="po-usage-bar-track"><div class="po-usage-bar-fill" style="width:${pct}%;background:${color}"></div></div>`; }
4753
+
4754
+ const modelRows = Object.entries(modelBreakdown||{}).sort((a,b) => b[1].cost-a[1].cost);
4755
+ const maxMC = modelRows.length ? modelRows[0][1].cost : 1;
4756
+ const modelHTML = modelRows.map(([name, m]) => {
4757
+ const short = name.replace(/^claude-/,'').replace(/-\d{8}$/,'');
4758
+ return `<div class="po-usage-bar-row"><div class="po-usage-bar-label" style="color:rgba(224,91,245,0.9)">${short}</div>${_bar(Math.round(m.cost/maxMC*100),'rgba(224,91,245,0.55)')}<div class="po-usage-bar-count">${_fmtC(m.cost)} · ${m.calls}x</div></div>`;
4759
+ }).join('');
4760
+
4761
+ const ACT_COLORS = {Coding:'rgba(91,158,245,0.7)',Exploration:'rgba(91,245,224,0.7)',Debugging:'rgba(245,91,91,0.7)',Feature:'rgba(91,245,160,0.7)',Brainstorming:'rgba(245,91,224,0.7)',Testing:'rgba(224,91,245,0.7)',Conversation:'rgba(136,136,136,0.7)',Refactoring:'rgba(245,200,91,0.7)'};
4762
+ const catRows = Object.entries(categoryBreakdown||{}).sort((a,b) => b[1].turns-a[1].turns);
4763
+ const maxCC = catRows.length ? catRows[0][1].turns : 1;
4764
+ const catHTML = catRows.map(([cat, c]) =>
4765
+ `<div class="po-usage-bar-row"><div class="po-usage-bar-label" style="color:var(--text)">${cat}</div>${_bar(Math.round(c.turns/maxCC*100), ACT_COLORS[cat]||'rgba(91,158,245,0.6)')}<div class="po-usage-bar-count">${c.turns} turns</div></div>`
4766
+ ).join('');
4767
+
4768
+ const toolRows = Object.entries(toolBreakdown||{}).sort((a,b) => b[1].calls-a[1].calls).slice(0,8);
4769
+ const maxTC = toolRows.length ? toolRows[0][1].calls : 1;
4770
+ const toolHTML = toolRows.map(([tool, t]) =>
4771
+ `<div class="po-usage-bar-row"><div class="po-usage-bar-label" style="color:var(--teal)">${tool}</div>${_bar(Math.round(t.calls/maxTC*100),'rgba(0,229,200,0.5)')}<div class="po-usage-bar-count">${t.calls}x</div></div>`
4772
+ ).join('');
4773
+
4774
+ const mcpRows = Object.entries(mcpBreakdown||{}).sort((a,b) => b[1].calls-a[1].calls);
4775
+ const maxMCP = mcpRows.length ? mcpRows[0][1].calls : 1;
4776
+ const mcpHTML = mcpRows.length
4777
+ ? mcpRows.map(([srv, m]) => `<div class="po-usage-bar-row"><div class="po-usage-bar-label" style="color:var(--text)">${srv}</div>${_bar(Math.round(m.calls/maxMCP*100),'rgba(245,200,91,0.55)')}<div class="po-usage-bar-count">${m.calls}x</div></div>`).join('')
4778
+ : '<div style="font-size:10px;color:var(--dim);">No MCP calls</div>';
4779
+
4780
+ const projRows = (projects||[]).slice(0,5);
4781
+ const maxPC = projRows.length ? projRows[0].totalCost : 1;
4782
+ const projHTML = projRows.map(p => {
4783
+ const name = (p.projectPath||p.project||'').replace(/^.*[/\\]/,'') || 'unknown';
4784
+ return `<div class="po-usage-bar-row"><div class="po-usage-bar-label" style="color:rgba(91,245,160,0.9)">${name}</div>${_bar(Math.round(p.totalCost/maxPC*100),'rgba(91,245,160,0.5)')}<div class="po-usage-bar-count">${_fmtC(p.totalCost)} · ${p.totalApiCalls}x</div></div>`;
4785
+ }).join('');
4786
+
4787
+ body.innerHTML = `
4788
+ <div class="po-usage-section">
4789
+ <div class="po-usage-section-title">Overview — ${_usagePeriod}</div>
4790
+ <div class="po-usage-stat-grid">
4791
+ <div class="po-usage-stat"><div class="po-usage-stat-label">Total Cost</div><div class="po-usage-stat-value">${_fmtC(totalCost)}</div></div>
4792
+ <div class="po-usage-stat"><div class="po-usage-stat-label">API Calls</div><div class="po-usage-stat-value" style="color:rgba(0,229,200,0.9)">${_fmtN(totalCalls)}</div></div>
4793
+ <div class="po-usage-stat"><div class="po-usage-stat-label">Tokens In</div><div class="po-usage-stat-value" style="color:rgba(220,220,220,0.9)">${_fmtN(totalIn)}</div></div>
4794
+ <div class="po-usage-stat"><div class="po-usage-stat-label">Tokens Out</div><div class="po-usage-stat-value" style="color:rgba(220,220,220,0.9)">${_fmtN(totalOut)}</div></div>
4795
+ <div class="po-usage-stat"><div class="po-usage-stat-label">Cache Read</div><div class="po-usage-stat-value" style="color:rgba(91,158,245,0.9)">${_fmtN(totalCached)}</div><div class="po-usage-stat-sub">Efficiency: ${cacheEff}%</div></div>
4796
+ </div>
4797
+ </div>
4798
+ ${modelHTML ? `<div class="po-usage-section"><div class="po-usage-section-title">Models</div>${modelHTML}</div>` : ''}
4799
+ ${catHTML ? `<div class="po-usage-section"><div class="po-usage-section-title">Activity</div>${catHTML}</div>` : ''}
4800
+ ${toolHTML ? `<div class="po-usage-section"><div class="po-usage-section-title">Top Tools</div>${toolHTML}</div>` : ''}
4801
+ <div class="po-usage-section"><div class="po-usage-section-title">MCP Servers</div>${mcpHTML}</div>
4802
+ ${projHTML ? `<div class="po-usage-section"><div class="po-usage-section-title">Projects</div>${projHTML}</div>` : ''}
4803
+ `;
3682
4804
  }
3683
4805
 
3684
4806
  let _allChunks = [];
@@ -3712,7 +4834,7 @@ function renderPalaceChunks(filter) {
3712
4834
  : _allChunks;
3713
4835
 
3714
4836
  if (!chunks.length && !kd.chunks) {
3715
- body.innerHTML = '<div style="color:var(--muted);font-size:10px;padding:8px 0;">No knowledge chunks indexed yet.<br><br>Run: <code style="color:var(--teal)">npx monomind graphify build</code></div>';
4837
+ body.innerHTML = '<div style="color:var(--muted);font-size:10px;padding:8px 0;">No knowledge chunks indexed yet.<br><br>Run: <code style="color:var(--teal)">npx monomind monograph build</code></div>';
3716
4838
  return;
3717
4839
  }
3718
4840
 
@@ -4303,7 +5425,7 @@ async function renderPalaceKnowledge() {
4303
5425
  const dir = selectedProjectDir || '';
4304
5426
  let d;
4305
5427
  try {
4306
- const r = await fetch(`/api/graphify-report${dir ? '?dir=' + encodeURIComponent(dir) : ''}`);
5428
+ const r = await fetch(`/api/monograph-report${dir ? '?dir=' + encodeURIComponent(dir) : ''}`);
4307
5429
  d = r.ok ? await r.json() : { exists: false, stats: null, content: null };
4308
5430
  } catch { d = { exists: false, stats: null, content: null }; }
4309
5431
 
@@ -4328,7 +5450,7 @@ async function renderPalaceKnowledge() {
4328
5450
  statsEl.innerHTML = `<div class="kg-badge kg-badge-warn">NO GRAPH</div>
4329
5451
  <div style="font-size:9px;color:var(--muted);margin:10px 0 14px;line-height:1.6;">No graph built yet.</div>
4330
5452
  <button class="kg-action-btn" onclick="triggerPalaceGraphBuild()">BUILD GRAPH</button>
4331
- <div style="margin-top:10px;font-size:8px;color:var(--dim);line-height:1.7;">or run:<br><code style="color:var(--teal)">npx monomind graphify build</code></div>
5453
+ <div style="margin-top:10px;font-size:8px;color:var(--dim);line-height:1.7;">or run:<br><code style="color:var(--teal)">npx monomind monograph build</code></div>
4332
5454
  ${knowledgeSuffix}`;
4333
5455
  } else {
4334
5456
  const s = d.stats;
@@ -4349,28 +5471,29 @@ async function renderPalaceKnowledge() {
4349
5471
  ${e.resolvedCallEdges !== undefined ? `<div class="kg-stat-row"><span class="kg-stat-key">Call edges</span><span class="kg-stat-val" style="color:var(--sky)">${fmtNum(e.resolvedCallEdges)}</span></div>` : ''}
4350
5472
  </div>` : ''}
4351
5473
  <button class="kg-action-btn" onclick="triggerPalaceGraphBuild()">REBUILD</button>
5474
+ <button class="kg-action-btn" onclick="triggerUAEnrich()" style="margin-left:6px;border-color:rgba(0,229,135,0.4);color:rgba(0,229,135,0.9);" title="Run semantic enrichment — imports understand graph or runs structural analysis">UNDERSTAND</button>
4352
5475
  ${knowledgeSuffix}`;
4353
5476
  }
4354
5477
 
4355
5478
  if (!d.exists || !d.content) {
4356
5479
  bodyEl.innerHTML = `<div class="kg-report-grid"><div class="kg-card kg-card-wide" style="text-align:center;padding:40px 20px;">
4357
5480
  <div class="kg-card-title" style="justify-content:center;margin-bottom:12px;"><span class="kg-card-title-icon">🗂</span>NO REPORT GENERATED</div>
4358
- <div class="kg-card-body" style="text-align:center;">Run <code>npx monomind graphify report</code><br>or click REBUILD to generate a full analysis.</div>
5481
+ <div class="kg-card-body" style="text-align:center;">Run <code>npx monomind monograph report</code><br>or click REBUILD to generate a full analysis.</div>
4359
5482
  </div></div>`;
4360
5483
  } else {
4361
- bodyEl.innerHTML = parseGraphifyReport(d.content);
5484
+ bodyEl.innerHTML = parseMonographReport(d.content);
4362
5485
  }
4363
5486
  }
4364
5487
 
4365
- let _graphifyIframeLoaded = false;
4366
- function loadGraphifyIframe(force) {
4367
- const iframe = document.getElementById('gf-graph-iframe');
5488
+ let _monographIframeLoaded = false;
5489
+ function loadMonographIframe(force) {
5490
+ const iframe = document.getElementById('mg-graph-iframe');
4368
5491
  if (!iframe) return;
4369
5492
  const dir = selectedProjectDir || '';
4370
- const src = `/api/graphify-html${dir ? '?dir=' + encodeURIComponent(dir) : ''}`;
4371
- if (!force && _graphifyIframeLoaded && iframe.src && iframe.src.includes('/api/graphify-html')) return;
5493
+ const src = `/api/monograph-html${dir ? '?dir=' + encodeURIComponent(dir) : ''}`;
5494
+ if (!force && _monographIframeLoaded && iframe.src && iframe.src.includes('/api/monograph-html')) return;
4372
5495
  iframe.src = src;
4373
- _graphifyIframeLoaded = true;
5496
+ _monographIframeLoaded = true;
4374
5497
  }
4375
5498
 
4376
5499
  window.triggerPalaceGraphBuild = async function() {
@@ -4380,14 +5503,14 @@ window.triggerPalaceGraphBuild = async function() {
4380
5503
  if (bodyEl) bodyEl.innerHTML = '<div style="color:var(--dim);font-size:10px;padding:16px;">Background build started — panel updates when complete.</div>';
4381
5504
  const dir = selectedProjectDir || '';
4382
5505
  try {
4383
- await fetch(`/api/graphify-build${dir ? '?dir=' + encodeURIComponent(dir) : ''}`, { method: 'POST' });
5506
+ await fetch(`/api/monograph-build${dir ? '?dir=' + encodeURIComponent(dir) : ''}`, { method: 'POST' });
4384
5507
  let attempts = 0;
4385
5508
  const poll = setInterval(async () => {
4386
5509
  attempts++;
4387
5510
  try {
4388
- const r = await fetch(`/api/graphify-report${dir ? '?dir=' + encodeURIComponent(dir) : ''}`);
5511
+ const r = await fetch(`/api/monograph-report${dir ? '?dir=' + encodeURIComponent(dir) : ''}`);
4389
5512
  const d = await r.json();
4390
- if (d.stats || d.exists) { clearInterval(poll); renderPalaceKnowledge(); _graphifyIframeLoaded = false; loadGraphifyIframe(true); }
5513
+ if (d.stats || d.exists) { clearInterval(poll); renderPalaceKnowledge(); _monographIframeLoaded = false; loadMonographIframe(true); }
4391
5514
  else if (attempts >= 36) { clearInterval(poll); if (bodyEl) bodyEl.innerHTML = '<div style="color:var(--amber);font-size:10px;">Build timed out — check server logs.</div>'; }
4392
5515
  } catch {}
4393
5516
  }, 5000);
@@ -4397,28 +5520,63 @@ window.triggerPalaceGraphBuild = async function() {
4397
5520
  }
4398
5521
  };
4399
5522
 
5523
+ window.triggerUAEnrich = async function() {
5524
+ const dir = selectedProjectDir || '';
5525
+ const btn = event && event.target;
5526
+ if (btn) { btn.textContent = 'ENRICHING…'; btn.disabled = true; }
5527
+ try {
5528
+ const r = await fetch(`/api/ua-enrich${dir ? '?dir=' + encodeURIComponent(dir) : ''}`, { method: 'POST' });
5529
+ const d = await r.json();
5530
+ if (d.status === 'importing') {
5531
+ showToast('Understand: importing graph — graph will update shortly');
5532
+ } else if (d.status === 'enriching') {
5533
+ showToast('Understand: structural enrichment running in background');
5534
+ } else {
5535
+ showToast('Understand: ' + (d.reason || d.status || 'done'));
5536
+ }
5537
+ // Reload graph after short delay
5538
+ setTimeout(() => {
5539
+ mgGraphData = null; mgReportData = null; _wikiData = null;
5540
+ _monographIframeLoaded = false;
5541
+ if (mgCurrentTab === 'graph') loadMonographIframe(true);
5542
+ if (mgCurrentTab === 'report') renderMonographReport();
5543
+ }, 3000);
5544
+ } catch (e) {
5545
+ showToast('Understand enrichment failed: ' + e.message, 'error');
5546
+ } finally {
5547
+ if (btn) { btn.textContent = 'UNDERSTAND'; btn.disabled = false; }
5548
+ }
5549
+ };
5550
+
5551
+ function showToast(msg, type) {
5552
+ var el = document.getElementById('mg-toast');
5553
+ if (!el) {
5554
+ el = document.createElement('div');
5555
+ el.id = 'mg-toast';
5556
+ el.style.cssText = 'position:fixed;bottom:24px;right:24px;background:var(--panel-bg);border:1px solid var(--border);color:var(--text);font-family:\'Azeret Mono\',monospace;font-size:10px;padding:8px 14px;border-radius:4px;z-index:9999;transition:opacity 0.3s;pointer-events:none;';
5557
+ document.body.appendChild(el);
5558
+ }
5559
+ el.textContent = msg;
5560
+ el.style.borderColor = type === 'error' ? 'rgba(239,83,80,0.5)' : 'rgba(0,229,135,0.4)';
5561
+ el.style.opacity = '1';
5562
+ clearTimeout(el._t);
5563
+ el._t = setTimeout(() => { el.style.opacity = '0'; }, 4000);
5564
+ }
5565
+
4400
5566
  // ═══════════════════════════════════════════════════════════════════
4401
- // GRAPHIFY INTERACTIVE VIEWS
5567
+ // MONOGRAPH INTERACTIVE VIEWS
4402
5568
  // ═══════════════════════════════════════════════════════════════════
4403
- let _currentGraphifyView = 'graph';
5569
+ let _currentMonographView = 'graph';
4404
5570
 
4405
- window.switchGraphifyView = function(view) {
4406
- _currentGraphifyView = view;
4407
- document.querySelectorAll('.gf-tool-tab').forEach(b => {
4408
- b.classList.toggle('active', b.getAttribute('data-gftab') === view);
4409
- });
4410
- document.querySelectorAll('.gf-view').forEach(v => v.classList.remove('active'));
4411
- const pane = document.getElementById('gf-view-' + view);
4412
- if (pane) pane.classList.add('active');
4413
- if (view === 'graph') { loadGraphifyIframe(); }
4414
- if (view === 'report') { renderPalaceKnowledge(); }
5571
+ window.switchMonographView = function(view) {
5572
+ switchMonographTab(view === 'report' ? 'report' : view === 'graph' ? 'graph' : 'query');
4415
5573
  };
4416
5574
 
4417
- window.runGraphifyQuery = async function() {
4418
- const input = document.getElementById('gf-query-input');
4419
- const mode = document.getElementById('gf-query-mode');
5575
+ window.runMonographQuery = async function() {
5576
+ const input = document.getElementById('mg-ask-input') || document.getElementById('gf-query-input');
5577
+ const mode = document.getElementById('mg-ask-mode') || document.getElementById('gf-query-mode');
4420
5578
  const budget = document.getElementById('gf-query-budget');
4421
- const result = document.getElementById('gf-query-result');
5579
+ const result = document.getElementById('mg-ask-result') || document.getElementById('gf-query-result');
4422
5580
  const q = (input && input.value || '').trim();
4423
5581
  if (!q) { if (result) result.textContent = 'Type a question first.'; return; }
4424
5582
  if (result) result.innerHTML = '<span style="color:var(--teal);">Querying…</span>';
@@ -4426,7 +5584,7 @@ window.runGraphifyQuery = async function() {
4426
5584
  try {
4427
5585
  const params = new URLSearchParams({ q, mode: mode.value, budget: budget.value });
4428
5586
  if (dir) params.set('dir', dir);
4429
- const r = await fetch('/api/graphify-query?' + params);
5587
+ const r = await fetch('/api/monograph-query?' + params);
4430
5588
  const d = await r.json();
4431
5589
  if (d.error) { result.innerHTML = `<span style="color:var(--red);">${escHtml(d.error)}</span>`; return; }
4432
5590
  result.innerHTML = `<div class="kg-card kg-card-wide" style="margin-bottom:10px;">
@@ -4436,9 +5594,9 @@ window.runGraphifyQuery = async function() {
4436
5594
  } catch (e) { result.innerHTML = `<span style="color:var(--red);">${escHtml(e.message)}</span>`; }
4437
5595
  };
4438
5596
 
4439
- window.runGraphifyExplain = async function() {
4440
- const input = document.getElementById('gf-explain-input');
4441
- const result = document.getElementById('gf-explain-result');
5597
+ window.runMonographExplain = async function() {
5598
+ const input = document.getElementById('mg-explain-input') || document.getElementById('gf-explain-input');
5599
+ const result = document.getElementById('mg-explain-result') || document.getElementById('gf-explain-result');
4442
5600
  const node = (input && input.value || '').trim();
4443
5601
  if (!node) { if (result) result.textContent = 'Enter a node name first.'; return; }
4444
5602
  if (result) result.innerHTML = '<span style="color:var(--teal);">Explaining…</span>';
@@ -4446,7 +5604,7 @@ window.runGraphifyExplain = async function() {
4446
5604
  try {
4447
5605
  const params = new URLSearchParams({ node });
4448
5606
  if (dir) params.set('dir', dir);
4449
- const r = await fetch('/api/graphify-explain?' + params);
5607
+ const r = await fetch('/api/monograph-explain?' + params);
4450
5608
  const d = await r.json();
4451
5609
  if (d.error) { result.innerHTML = `<span style="color:var(--red);">${escHtml(d.error)}</span>`; return; }
4452
5610
  result.innerHTML = `<div class="kg-card kg-card-wide" style="margin-bottom:10px;">
@@ -4456,10 +5614,10 @@ window.runGraphifyExplain = async function() {
4456
5614
  } catch (e) { result.innerHTML = `<span style="color:var(--red);">${escHtml(e.message)}</span>`; }
4457
5615
  };
4458
5616
 
4459
- window.runGraphifyPath = async function() {
4460
- const fromEl = document.getElementById('gf-path-from');
4461
- const toEl = document.getElementById('gf-path-to');
4462
- const result = document.getElementById('gf-path-result');
5617
+ window.runMonographPath = async function() {
5618
+ const fromEl = document.getElementById('mg-path-from') || document.getElementById('gf-path-from');
5619
+ const toEl = document.getElementById('mg-path-to') || document.getElementById('gf-path-to');
5620
+ const result = document.getElementById('mg-path-result') || document.getElementById('gf-path-result');
4463
5621
  const from = (fromEl && fromEl.value || '').trim();
4464
5622
  const to = (toEl && toEl.value || '').trim();
4465
5623
  if (!from || !to) { if (result) result.textContent = 'Enter both source and target nodes.'; return; }
@@ -4468,7 +5626,7 @@ window.runGraphifyPath = async function() {
4468
5626
  try {
4469
5627
  const params = new URLSearchParams({ from, to });
4470
5628
  if (dir) params.set('dir', dir);
4471
- const r = await fetch('/api/graphify-path?' + params);
5629
+ const r = await fetch('/api/monograph-path?' + params);
4472
5630
  const d = await r.json();
4473
5631
  if (d.error) { result.innerHTML = `<span style="color:var(--red);">${escHtml(d.error)}</span>`; return; }
4474
5632
  result.innerHTML = `<div class="kg-card kg-card-wide" style="margin-bottom:10px;">
@@ -4478,7 +5636,7 @@ window.runGraphifyPath = async function() {
4478
5636
  } catch (e) { result.innerHTML = `<span style="color:var(--red);">${escHtml(e.message)}</span>`; }
4479
5637
  };
4480
5638
 
4481
- window.runGraphifyBenchmark = async function() {
5639
+ window.runMonographBenchmark = async function() {
4482
5640
  const result = document.getElementById('gf-benchmark-result');
4483
5641
  const status = document.getElementById('gf-bench-status');
4484
5642
  if (result) result.innerHTML = '<span style="color:var(--teal);">Running benchmark…</span>';
@@ -4486,7 +5644,7 @@ window.runGraphifyBenchmark = async function() {
4486
5644
  const dir = selectedProjectDir || '';
4487
5645
  try {
4488
5646
  const params = dir ? '?dir=' + encodeURIComponent(dir) : '';
4489
- const r = await fetch('/api/graphify-benchmark' + params);
5647
+ const r = await fetch('/api/monograph-benchmark' + params);
4490
5648
  const d = await r.json();
4491
5649
  if (status) status.textContent = '';
4492
5650
  if (d.error) { result.innerHTML = `<span style="color:var(--red);">${escHtml(d.error)}</span>`; return; }
@@ -4497,13 +5655,13 @@ window.runGraphifyBenchmark = async function() {
4497
5655
  } catch (e) { if (status) status.textContent = ''; result.innerHTML = `<span style="color:var(--red);">${escHtml(e.message)}</span>`; }
4498
5656
  };
4499
5657
 
4500
- window.toggleGraphifyWatch = async function() {
4501
- const btn = document.getElementById('gf-watch-btn');
4502
- const indicator = document.getElementById('gf-watch-indicator');
5658
+ window.toggleMonographWatch = async function() {
5659
+ const btn = document.getElementById('mg-watch-btn') || document.getElementById('gf-watch-btn');
5660
+ const indicator = document.getElementById('mg-watch-indicator') || document.getElementById('gf-watch-indicator');
4503
5661
  const dir = selectedProjectDir || '';
4504
5662
  const params = dir ? '?dir=' + encodeURIComponent(dir) : '';
4505
5663
  try {
4506
- const r = await fetch('/api/graphify-watch-toggle' + params, { method: 'POST' });
5664
+ const r = await fetch('/api/monograph-watch-toggle' + params, { method: 'POST' });
4507
5665
  const d = await r.json();
4508
5666
  updateWatchIndicator(d);
4509
5667
  } catch (e) {
@@ -4515,15 +5673,15 @@ async function refreshWatchStatus() {
4515
5673
  const dir = selectedProjectDir || '';
4516
5674
  const params = dir ? '?dir=' + encodeURIComponent(dir) : '';
4517
5675
  try {
4518
- const r = await fetch('/api/graphify-watch-status' + params);
5676
+ const r = await fetch('/api/monograph-watch-status' + params);
4519
5677
  const d = await r.json();
4520
5678
  updateWatchIndicator(d);
4521
5679
  } catch {}
4522
5680
  }
4523
5681
 
4524
5682
  function updateWatchIndicator(d) {
4525
- const indicator = document.getElementById('gf-watch-indicator');
4526
- const btn = document.getElementById('gf-watch-btn');
5683
+ const indicator = document.getElementById('mg-watch-indicator') || document.getElementById('gf-watch-indicator');
5684
+ const btn = document.getElementById('mg-watch-btn') || document.getElementById('gf-watch-btn');
4527
5685
  if (!indicator) return;
4528
5686
  if (d.running) {
4529
5687
  indicator.innerHTML = '<span style="color:var(--green);">● WATCHING</span>';
@@ -5229,9 +6387,1159 @@ window.closePalaceOverlay = function() {
5229
6387
  _origClose();
5230
6388
  };
5231
6389
 
6390
+ // ═══════════════════════════════════════════════════════════════════
6391
+ // MONOGRAPH OVERLAY
6392
+ // ═══════════════════════════════════════════════════════════════════
6393
+ let mgGraphData = null;
6394
+ let mgReportData = null;
6395
+ let _wikiData = null;
6396
+ let mgCurrentTab = 'overview';
6397
+
6398
+ window.openMonographOverlay = async function() {
6399
+ document.getElementById('monograph-overlay').classList.add('open');
6400
+ if (!mgGraphData) await loadMonographData();
6401
+ renderMonographTab(mgCurrentTab);
6402
+ };
6403
+
6404
+ window.closeMonographOverlay = function() {
6405
+ document.getElementById('monograph-overlay').classList.remove('open');
6406
+ };
6407
+
6408
+ window.switchMonographTab = function(tab) {
6409
+ mgCurrentTab = tab;
6410
+ document.querySelectorAll('#mg-tabs .mg-tab').forEach(b => {
6411
+ const m = (b.getAttribute('onclick') || '').match(/'(\w+)'/);
6412
+ b.classList.toggle('active', !!(m && m[1] === tab));
6413
+ });
6414
+ document.querySelectorAll('.mg-pane').forEach(p => p.classList.remove('active'));
6415
+ const pane = document.getElementById('mg-pane-' + tab);
6416
+ if (pane) pane.classList.add('active');
6417
+ if (mgGraphData) renderMonographTab(tab);
6418
+ };
6419
+
6420
+ async function loadMonographData() {
6421
+ const dir = selectedProjectDir || '';
6422
+ const qs = dir ? '?dir=' + encodeURIComponent(dir) : '';
6423
+ try {
6424
+ const [graphRes, reportRes] = await Promise.all([
6425
+ fetch('/api/monograph-graph' + qs).catch(() => ({ ok: false })),
6426
+ fetch('/api/monograph-report' + qs).catch(() => ({ ok: false })),
6427
+ ]);
6428
+ mgGraphData = graphRes.ok ? await graphRes.json() : { nodes: [], edges: [] };
6429
+ const reportJson = reportRes.ok ? await reportRes.json() : {};
6430
+ mgReportData = reportJson.report || '';
6431
+ // Use real totals from DB stats when available, fall back to visible subset
6432
+ const totalNodes = reportJson.stats?.nodes ?? (mgGraphData.nodes || []).length;
6433
+ const totalEdges = reportJson.stats?.edges ?? (mgGraphData.edges || []).length;
6434
+ // Update stats bar
6435
+ const stats = document.getElementById('mg-stats');
6436
+ if (stats) stats.innerHTML = `<span>${totalNodes.toLocaleString()} nodes</span><span>${totalEdges.toLocaleString()} edges</span>`;
6437
+ // Update node count in button
6438
+ const btn = document.getElementById('mg-node-count-anim');
6439
+ if (btn) btn.textContent = totalNodes || '–';
6440
+ const countEl = document.getElementById('mg-graph-node-count');
6441
+ if (countEl) countEl.textContent = `${totalNodes.toLocaleString()} nodes · ${totalEdges.toLocaleString()} edges`;
6442
+ } catch (e) {
6443
+ mgGraphData = { nodes: [], edges: [] };
6444
+ }
6445
+ }
6446
+
6447
+ function renderMonographTab(tab) {
6448
+ if (tab === 'overview') {
6449
+ renderMonographOverview();
6450
+ } else if (tab === 'graph') {
6451
+ loadMonographIframe(false);
6452
+ } else if (tab === 'analyze') {
6453
+ runMonographAnalysis('stats');
6454
+ } else if (tab === 'report') {
6455
+ renderMonographReport();
6456
+ } else if (tab === 'wiki') {
6457
+ loadWikiTab();
6458
+ }
6459
+ }
6460
+
6461
+ window.runMonographAnalysis = function(type) {
6462
+ document.querySelectorAll('.mg-analyze-btn').forEach(b => {
6463
+ const m = (b.getAttribute('onclick') || '').match(/'([\w]+)'/);
6464
+ b.classList.toggle('active', !!(m && m[1] === type));
6465
+ });
6466
+ const result = document.getElementById('mg-analyze-result');
6467
+ if (!mgGraphData || !mgGraphData.nodes) {
6468
+ result.innerHTML = '<span style="color:var(--muted);">No graph data loaded.</span>';
6469
+ return;
6470
+ }
6471
+ const nodes = mgGraphData.nodes || [];
6472
+ const edges = mgGraphData.edges || [];
6473
+ const n = nodes.length, e = edges.length;
6474
+
6475
+ if (type === 'stats') {
6476
+ const density = n > 1 ? (2 * e / (n * (n - 1))).toFixed(6) : '0';
6477
+ const avgDegree = n ? (2 * e / n).toFixed(2) : '0';
6478
+ const maxDeg = nodes.reduce((m, x) => Math.max(m, x.degree || 0), 0);
6479
+ result.innerHTML = `<div style="font-family:'Syne',sans-serif;font-size:11px;font-weight:700;color:rgba(123,97,255,0.8);margin-bottom:12px;">GRAPH STATISTICS</div>
6480
+ <table style="border-collapse:collapse;width:100%;max-width:400px;">
6481
+ <tr><td style="padding:4px 12px 4px 0;color:var(--muted);">Nodes</td><td style="color:var(--text);font-weight:700;">${n.toLocaleString()}</td></tr>
6482
+ <tr><td style="padding:4px 12px 4px 0;color:var(--muted);">Edges</td><td style="color:var(--text);font-weight:700;">${e.toLocaleString()}</td></tr>
6483
+ <tr><td style="padding:4px 12px 4px 0;color:var(--muted);">Density</td><td style="color:var(--text);font-weight:700;">${density}</td></tr>
6484
+ <tr><td style="padding:4px 12px 4px 0;color:var(--muted);">Avg Degree</td><td style="color:var(--text);font-weight:700;">${avgDegree}</td></tr>
6485
+ <tr><td style="padding:4px 12px 4px 0;color:var(--muted);">Max Degree</td><td style="color:var(--text);font-weight:700;">${maxDeg}</td></tr>
6486
+ <tr><td style="padding:4px 12px 4px 0;color:var(--muted);">Edge/Node Ratio</td><td style="color:var(--text);font-weight:700;">${n ? (e/n).toFixed(2) : 0}</td></tr>
6487
+ </table>`;
6488
+ } else if (type === 'pagerank') {
6489
+ // Approximate PageRank by degree (normalized)
6490
+ const sorted = [...nodes].sort((a, b) => (b.degree||0) - (a.degree||0)).slice(0, 20);
6491
+ const maxD = sorted[0]?.degree || 1;
6492
+ result.innerHTML = `<div style="font-family:'Syne',sans-serif;font-size:11px;font-weight:700;color:rgba(123,97,255,0.8);margin-bottom:12px;">PAGERANK TOP 20 (by degree)</div>` +
6493
+ sorted.map((nd, i) => {
6494
+ const pct = Math.round(((nd.degree||0) / maxD) * 100);
6495
+ return `<div style="display:flex;align-items:center;gap:8px;margin-bottom:5px;">
6496
+ <span style="color:var(--muted);width:20px;text-align:right;">${i+1}</span>
6497
+ <span style="flex:1;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${nd.id}">${nd.label || nd.id}</span>
6498
+ <div style="width:100px;height:6px;background:rgba(255,255,255,0.06);border-radius:3px;overflow:hidden;">
6499
+ <div style="width:${pct}%;height:100%;background:rgba(123,97,255,0.6);border-radius:3px;"></div>
6500
+ </div>
6501
+ <span style="color:var(--muted);width:28px;text-align:right;">${nd.degree||0}</span>
6502
+ </div>`;
6503
+ }).join('');
6504
+ } else if (type === 'deadcode') {
6505
+ const dead = nodes.filter(nd => (nd.degree || 0) === 0);
6506
+ if (!dead.length) {
6507
+ result.innerHTML = '<span style="color:var(--teal);">✓ No dead code nodes detected in the visible graph.</span>';
6508
+ } else {
6509
+ result.innerHTML = `<div style="font-family:'Syne',sans-serif;font-size:11px;font-weight:700;color:rgba(123,97,255,0.8);margin-bottom:12px;">DEAD CODE — ${dead.length} nodes (degree = 0)</div>` +
6510
+ dead.slice(0, 50).map(nd => `<div style="color:var(--dim);margin-bottom:3px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;" title="${nd.id}">${nd.label || nd.id} <span style="color:var(--border);">${nd.type || ''}</span></div>`).join('') +
6511
+ (dead.length > 50 ? `<div style="color:var(--muted);margin-top:6px;">…and ${dead.length - 50} more</div>` : '');
6512
+ }
6513
+ } else if (type === 'wcc') {
6514
+ // Union-find on undirected graph
6515
+ const parent = {};
6516
+ nodes.forEach(nd => { parent[nd.id] = nd.id; });
6517
+ function find(x) { return parent[x] === x ? x : (parent[x] = find(parent[x])); }
6518
+ function union(a, b) { parent[find(a)] = find(b); }
6519
+ const idSet = new Set(nodes.map(nd => nd.id));
6520
+ edges.forEach(e => { if (idSet.has(e.source) && idSet.has(e.target)) union(e.source, e.target); });
6521
+ const compMap = {};
6522
+ nodes.forEach(nd => { const root = find(nd.id); (compMap[root] = compMap[root] || []).push(nd); });
6523
+ const comps = Object.values(compMap).sort((a, b) => b.length - a.length);
6524
+ result.innerHTML = `<div style="font-family:'Syne',sans-serif;font-size:11px;font-weight:700;color:rgba(123,97,255,0.8);margin-bottom:12px;">WEAKLY CONNECTED COMPONENTS — ${comps.length}</div>` +
6525
+ comps.slice(0, 20).map((c, i) => `<div style="margin-bottom:6px;">
6526
+ <span style="color:var(--muted);">Component ${i+1}</span>
6527
+ <span style="color:var(--text);margin-left:8px;font-weight:700;">${c.length} nodes</span>
6528
+ <div style="color:var(--border);font-size:9px;margin-top:2px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${c.slice(0,4).map(nd=>nd.label||nd.id).join(', ')}${c.length>4?' …':''}</div>
6529
+ </div>`).join('') +
6530
+ (comps.length > 20 ? `<div style="color:var(--muted);">…and ${comps.length - 20} more</div>` : '');
6531
+ } else if (type === 'topo') {
6532
+ // Kahn's topological sort
6533
+ const inDeg = {};
6534
+ const idSet = new Set(nodes.map(nd => nd.id));
6535
+ nodes.forEach(nd => { inDeg[nd.id] = 0; });
6536
+ edges.forEach(e => { if (idSet.has(e.target)) inDeg[e.target] = (inDeg[e.target]||0) + 1; });
6537
+ const queue = nodes.filter(nd => (inDeg[nd.id]||0) === 0).map(nd => nd.id);
6538
+ const order = [];
6539
+ const adj = {};
6540
+ edges.forEach(e => { (adj[e.source] = adj[e.source]||[]).push(e.target); });
6541
+ while (queue.length) {
6542
+ const cur = queue.shift(); order.push(cur);
6543
+ (adj[cur]||[]).forEach(t => { if (--inDeg[t] === 0) queue.push(t); });
6544
+ }
6545
+ const hasCycle = order.length < nodes.length;
6546
+ const labelMap = {};
6547
+ nodes.forEach(nd => { labelMap[nd.id] = nd.label || nd.id; });
6548
+ result.innerHTML = `<div style="font-family:'Syne',sans-serif;font-size:11px;font-weight:700;color:rgba(123,97,255,0.8);margin-bottom:12px;">TOPOLOGICAL ORDER — ${order.length}/${nodes.length} nodes sorted${hasCycle?' (cycles detected, partial)':''}</div>` +
6549
+ order.slice(0, 40).map((id, i) => `<div style="color:var(--dim);margin-bottom:2px;"><span style="color:var(--border);margin-right:8px;">${String(i+1).padStart(3,' ')}</span>${labelMap[id]||id}</div>`).join('') +
6550
+ (order.length > 40 ? `<div style="color:var(--muted);margin-top:6px;">…and ${order.length - 40} more</div>` : '');
6551
+ } else if (type === 'hubs') {
6552
+ const sorted = [...nodes].sort((a, b) => (b.degree||0) - (a.degree||0)).slice(0, 30);
6553
+ result.innerHTML = `<div style="font-family:'Syne',sans-serif;font-size:11px;font-weight:700;color:rgba(123,97,255,0.8);margin-bottom:12px;">TOP HUBS BY DEGREE — Top 30</div>` +
6554
+ sorted.map(nd => `<div style="display:flex;justify-content:space-between;margin-bottom:4px;border-bottom:1px solid rgba(255,255,255,0.03);padding-bottom:3px;">
6555
+ <span style="color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;" title="${nd.id}">${nd.label||nd.id}</span>
6556
+ <span style="color:var(--muted);margin-left:8px;font-size:9px;">${nd.type||''}</span>
6557
+ <span style="color:rgba(123,97,255,0.7);font-weight:700;margin-left:12px;flex-shrink:0;">${nd.degree||0}</span>
6558
+ </div>`).join('');
6559
+ } else if (type === 'cycles') {
6560
+ // Simple cycle detection using DFS
6561
+ const idSet = new Set(nodes.map(nd => nd.id));
6562
+ const adj = {};
6563
+ edges.forEach(e => { if (idSet.has(e.source)&&idSet.has(e.target)) (adj[e.source]=adj[e.source]||[]).push(e.target); });
6564
+ const visited = new Set(), inStack = new Set();
6565
+ const cycles = [];
6566
+ function dfs(node, path) {
6567
+ if (cycles.length >= 10) return;
6568
+ visited.add(node); inStack.add(node);
6569
+ for (const nb of (adj[node]||[])) {
6570
+ if (!visited.has(nb)) { dfs(nb, [...path, nb]); }
6571
+ else if (inStack.has(nb)) {
6572
+ const cycleStart = path.indexOf(nb);
6573
+ if (cycleStart >= 0) cycles.push(path.slice(cycleStart));
6574
+ }
6575
+ }
6576
+ inStack.delete(node);
6577
+ }
6578
+ nodes.forEach(nd => { if (!visited.has(nd.id)) dfs(nd.id, [nd.id]); });
6579
+ const labelMap = {};
6580
+ nodes.forEach(nd => { labelMap[nd.id] = nd.label || nd.id; });
6581
+ if (!cycles.length) {
6582
+ result.innerHTML = '<span style="color:var(--teal);">✓ No cycles detected in the visible graph.</span>';
6583
+ } else {
6584
+ result.innerHTML = `<div style="font-family:'Syne',sans-serif;font-size:11px;font-weight:700;color:rgba(123,97,255,0.8);margin-bottom:12px;">CYCLES DETECTED — ${cycles.length} (showing up to 10)</div>` +
6585
+ cycles.map((c, i) => `<div style="margin-bottom:10px;background:rgba(239,83,80,0.05);border:1px solid rgba(239,83,80,0.15);border-radius:3px;padding:8px 10px;">
6586
+ <div style="color:rgba(239,83,80,0.7);font-size:9px;margin-bottom:4px;">CYCLE ${i+1} — ${c.length} nodes</div>
6587
+ <div style="color:var(--dim);font-size:9px;">${c.map(id=>labelMap[id]||id).join(' → ')}</div>
6588
+ </div>`).join('');
6589
+ }
6590
+ } else if (type === 'surprises') {
6591
+ // Show edges that cross file type boundaries
6592
+ const getExt = id => { const m = id.match(/\.[^.]+$/); return m ? m[0].toLowerCase() : ''; };
6593
+ const labelMap = {};
6594
+ nodes.forEach(nd => { labelMap[nd.id] = nd.label || nd.id; });
6595
+ const crossed = edges.filter(e => getExt(e.source) !== getExt(e.target) && getExt(e.source) && getExt(e.target)).slice(0, 30);
6596
+ if (!crossed.length) {
6597
+ result.innerHTML = '<span style="color:var(--muted);">No cross-filetype edges in visible graph.</span>';
6598
+ } else {
6599
+ result.innerHTML = `<div style="font-family:'Syne',sans-serif;font-size:11px;font-weight:700;color:rgba(123,97,255,0.8);margin-bottom:12px;">CROSS-FILETYPE EDGES — ${crossed.length} shown</div>` +
6600
+ crossed.map(e => `<div style="margin-bottom:6px;border-bottom:1px solid rgba(255,255,255,0.03);padding-bottom:5px;">
6601
+ <div style="display:flex;align-items:center;gap:6px;">
6602
+ <span style="color:var(--teal);font-size:8px;">${getExt(e.source)}</span>
6603
+ <span style="color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:200px;" title="${e.source}">${labelMap[e.source]||e.source}</span>
6604
+ <span style="color:var(--muted);font-size:9px;">─${e.relation||'REF'}→</span>
6605
+ <span style="color:var(--teal);font-size:8px;">${getExt(e.target)}</span>
6606
+ <span style="color:var(--dim);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:200px;" title="${e.target}">${labelMap[e.target]||e.target}</span>
6607
+ </div>
6608
+ </div>`).join('');
6609
+ }
6610
+ } else if (type === 'communities') {
6611
+ const groups = {};
6612
+ nodes.forEach(nd => { const g = nd.type || 'unknown'; (groups[g]=groups[g]||[]).push(nd); });
6613
+ const sorted = Object.entries(groups).sort((a,b) => b[1].length - a[1].length);
6614
+ result.innerHTML = `<div style="font-family:'Syne',sans-serif;font-size:11px;font-weight:700;color:rgba(123,97,255,0.8);margin-bottom:12px;">NODE TYPE DISTRIBUTION — ${sorted.length} types</div>` +
6615
+ sorted.map(([type, nds]) => `<div style="display:flex;align-items:center;gap:8px;margin-bottom:5px;">
6616
+ <span style="flex:1;color:var(--text);overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${type}</span>
6617
+ <div style="width:120px;height:6px;background:rgba(255,255,255,0.05);border-radius:3px;overflow:hidden;">
6618
+ <div style="width:${Math.round(nds.length/nodes.length*100)}%;height:100%;background:rgba(123,97,255,0.5);border-radius:3px;"></div>
6619
+ </div>
6620
+ <span style="color:var(--muted);width:32px;text-align:right;">${nds.length}</span>
6621
+ </div>`).join('');
6622
+ } else if (type === 'cohesion') {
6623
+ result.innerHTML = '<span style="color:var(--muted);">Computing cohesion scores…</span>';
6624
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_cohesion', input:{ limit: 30 } }) })
6625
+ .then(r => r.json())
6626
+ .then(data => {
6627
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6628
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre;line-height:1.7;color:var(--text);">${txt}</div>`;
6629
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6630
+ } else if (type === 'bridges') {
6631
+ result.innerHTML = '<span style="color:var(--muted);">Finding bridge nodes…</span>';
6632
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_bridge', input:{ limit: 20 } }) })
6633
+ .then(r => r.json())
6634
+ .then(data => {
6635
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6636
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);">${txt}</div>`;
6637
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6638
+ } else if (type === 'detect_changes') {
6639
+ result.innerHTML = '<span style="color:var(--muted);">Loading changed files…</span>';
6640
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_detect_changes', input:{ scope: 'all' } }) })
6641
+ .then(r => r.json())
6642
+ .then(data => {
6643
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6644
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);">${txt}</div>`;
6645
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6646
+ } else if (type === 'diff') {
6647
+ result.innerHTML = '<span style="color:var(--muted);">Computing graph diff…</span>';
6648
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_diff', input:{} }) })
6649
+ .then(r => r.json())
6650
+ .then(data => {
6651
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6652
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);">${txt}</div>`;
6653
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6654
+
6655
+ // ── Dead Code Analysis ───────────────────────────────────────────────────
6656
+ } else if (type === 'unlinked_refs') {
6657
+ result.innerHTML = '<span style="color:rgba(255,160,0,0.6);">Scanning for unlinked references…</span>';
6658
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_unlinked_refs', input:{ limit: 50 } }) })
6659
+ .then(r => r.json())
6660
+ .then(data => {
6661
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6662
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);border-left:2px solid rgba(255,160,0,0.3);padding-left:10px;">${txt}</div>`;
6663
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6664
+ } else if (type === 'boundary_check') {
6665
+ result.innerHTML = '<span style="color:rgba(255,160,0,0.6);">Checking boundary violations…</span>';
6666
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_boundary_check', input:{} }) })
6667
+ .then(r => r.json())
6668
+ .then(data => {
6669
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6670
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);border-left:2px solid rgba(255,160,0,0.3);padding-left:10px;">${txt}</div>`;
6671
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6672
+ } else if (type === 'reachability') {
6673
+ result.innerHTML = '<span style="color:rgba(255,160,0,0.6);">Analysing reachability from entry points…</span>';
6674
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_reachability', input:{} }) })
6675
+ .then(r => r.json())
6676
+ .then(data => {
6677
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6678
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);border-left:2px solid rgba(255,160,0,0.3);padding-left:10px;">${txt}</div>`;
6679
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6680
+ } else if (type === 'regression_check') {
6681
+ result.innerHTML = '<span style="color:rgba(255,160,0,0.6);">Comparing against saved baseline…</span>';
6682
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_regression_check', input:{} }) })
6683
+ .then(r => r.json())
6684
+ .then(data => {
6685
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6686
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);border-left:2px solid rgba(255,160,0,0.3);padding-left:10px;">${txt}</div>`;
6687
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6688
+
6689
+ // ── Duplication Detection ────────────────────────────────────────────────
6690
+ } else if (type === 'clone_detect') {
6691
+ result.innerHTML = '<span style="color:rgba(255,160,0,0.6);">Running suffix-array clone detection…</span>';
6692
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_clone_detect', input:{ minLines: 6, limit: 30 } }) })
6693
+ .then(r => r.json())
6694
+ .then(data => {
6695
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6696
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);border-left:2px solid rgba(255,160,0,0.3);padding-left:10px;">${txt}</div>`;
6697
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6698
+ } else if (type === 'similar_files') {
6699
+ result.innerHTML = '<span style="color:rgba(255,160,0,0.6);">Finding structurally similar files…</span>';
6700
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_similar_files', input:{ limit: 20 } }) })
6701
+ .then(r => r.json())
6702
+ .then(data => {
6703
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6704
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);border-left:2px solid rgba(255,160,0,0.3);padding-left:10px;">${txt}</div>`;
6705
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6706
+ } else if (type === 'mirrored_dirs') {
6707
+ result.innerHTML = '<span style="color:rgba(255,160,0,0.6);">Detecting mirrored directory families…</span>';
6708
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_mirrored_dirs', input:{} }) })
6709
+ .then(r => r.json())
6710
+ .then(data => {
6711
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6712
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);border-left:2px solid rgba(255,160,0,0.3);padding-left:10px;">${txt}</div>`;
6713
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6714
+
6715
+ // ── Health & Quality ─────────────────────────────────────────────────────
6716
+ } else if (type === 'health_score') {
6717
+ result.innerHTML = '<span style="color:rgba(255,160,0,0.6);">Computing health score…</span>';
6718
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_health_score', input:{} }) })
6719
+ .then(r => r.json())
6720
+ .then(data => {
6721
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6722
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);border-left:2px solid rgba(255,160,0,0.3);padding-left:10px;">${txt}</div>`;
6723
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6724
+ } else if (type === 'vital_signs') {
6725
+ result.innerHTML = '<span style="color:rgba(255,160,0,0.6);">Snapshotting vital signs…</span>';
6726
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_vital_signs_snapshot', input:{} }) })
6727
+ .then(r => r.json())
6728
+ .then(data => {
6729
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6730
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);border-left:2px solid rgba(255,160,0,0.3);padding-left:10px;">${txt}</div>`;
6731
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6732
+ } else if (type === 'health_trend') {
6733
+ result.innerHTML = '<span style="color:rgba(255,160,0,0.6);">Loading health trend…</span>';
6734
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_health_trend', input:{ days: 30 } }) })
6735
+ .then(r => r.json())
6736
+ .then(data => {
6737
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6738
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);border-left:2px solid rgba(255,160,0,0.3);padding-left:10px;">${txt}</div>`;
6739
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6740
+ } else if (type === 'hotspots') {
6741
+ result.innerHTML = '<span style="color:rgba(255,160,0,0.6);">Identifying churn hotspots…</span>';
6742
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_hotspots', input:{ limit: 20 } }) })
6743
+ .then(r => r.json())
6744
+ .then(data => {
6745
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6746
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);border-left:2px solid rgba(255,160,0,0.3);padding-left:10px;">${txt}</div>`;
6747
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6748
+ } else if (type === 'maintainability') {
6749
+ result.innerHTML = '<span style="color:rgba(255,160,0,0.6);">Computing Maintainability Index (171 − 5.2·ln(V) − 0.23·G − 16.2·ln(L))…</span>';
6750
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_maintainability', input:{ limit: 25 } }) })
6751
+ .then(r => r.json())
6752
+ .then(data => {
6753
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6754
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);border-left:2px solid rgba(255,160,0,0.3);padding-left:10px;">${txt}</div>`;
6755
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6756
+ } else if (type === 'complexity') {
6757
+ result.innerHTML = '<span style="color:rgba(255,160,0,0.6);">Computing cyclomatic complexity…</span>';
6758
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_complexity', input:{ limit: 25 } }) })
6759
+ .then(r => r.json())
6760
+ .then(data => {
6761
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6762
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);border-left:2px solid rgba(255,160,0,0.3);padding-left:10px;">${txt}</div>`;
6763
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6764
+ } else if (type === 'crap_score') {
6765
+ result.innerHTML = '<span style="color:rgba(255,160,0,0.6);">Computing CRAP scores (Change Risk Anti-Patterns)…</span>';
6766
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_crap_score', input:{ limit: 25 } }) })
6767
+ .then(r => r.json())
6768
+ .then(data => {
6769
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6770
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);border-left:2px solid rgba(255,160,0,0.3);padding-left:10px;">${txt}</div>`;
6771
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6772
+ } else if (type === 'risk_profile') {
6773
+ result.innerHTML = '<span style="color:rgba(255,160,0,0.6);">Building risk profile…</span>';
6774
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_risk_profile', input:{} }) })
6775
+ .then(r => r.json())
6776
+ .then(data => {
6777
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6778
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);border-left:2px solid rgba(255,160,0,0.3);padding-left:10px;">${txt}</div>`;
6779
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6780
+ } else if (type === 'baseline_compare') {
6781
+ result.innerHTML = '<span style="color:rgba(255,160,0,0.6);">Comparing to saved baseline…</span>';
6782
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_baseline_compare', input:{} }) })
6783
+ .then(r => r.json())
6784
+ .then(data => {
6785
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6786
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);border-left:2px solid rgba(255,160,0,0.3);padding-left:10px;">${txt}</div>`;
6787
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6788
+ } else if (type === 'author_analytics') {
6789
+ result.innerHTML = '<span style="color:rgba(255,160,0,0.6);">Loading author analytics…</span>';
6790
+ fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_author_analytics', input:{ limit: 20 } }) })
6791
+ .then(r => r.json())
6792
+ .then(data => {
6793
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6794
+ result.innerHTML = `<div style="font-family:'Azeret Mono',monospace;font-size:10px;white-space:pre-wrap;line-height:1.6;color:var(--text);border-left:2px solid rgba(255,160,0,0.3);padding-left:10px;">${txt}</div>`;
6795
+ }).catch(e => { result.innerHTML = '<span style="color:var(--dim);">Error: ' + e.message + '</span>'; });
6796
+ }
6797
+ };
6798
+
6799
+ window.runMonographSearch = function(val) {
6800
+ const box = document.getElementById('mg-search-result');
6801
+ if (!val || !mgGraphData) { box.textContent = '–'; return; }
6802
+ const q = val.toLowerCase();
6803
+ const matches = (mgGraphData.nodes||[]).filter(nd => (nd.label||nd.id||'').toLowerCase().includes(q)).slice(0, 20);
6804
+ if (!matches.length) { box.textContent = 'No matches.'; return; }
6805
+ box.innerHTML = matches.map(nd => `<div style="display:flex;gap:8px;margin-bottom:4px;">
6806
+ <span style="color:var(--text);flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;">${nd.label||nd.id}</span>
6807
+ <span style="color:var(--muted);font-size:9px;">${nd.type||''}</span>
6808
+ <span style="color:var(--border);font-size:9px;">${nd.degree||0} edges</span>
6809
+ </div>`).join('');
6810
+ };
6811
+
6812
+ window.runMonographExplain = async function() {
6813
+ const input = document.getElementById('mg-explain-input').value.trim();
6814
+ const box = document.getElementById('mg-explain-result');
6815
+ if (!input) return;
6816
+ box.textContent = 'Loading…';
6817
+ const dir = selectedProjectDir || '';
6818
+ const qs = `?node=${encodeURIComponent(input)}${dir ? '&dir='+encodeURIComponent(dir) : ''}`;
6819
+ try {
6820
+ const res = await fetch('/api/monograph-explain' + qs);
6821
+ const data = await res.json();
6822
+ box.textContent = data.explanation || data.error || 'No explanation available.';
6823
+ } catch(e) { box.textContent = 'Error: ' + e.message; }
6824
+ };
6825
+
6826
+ window.runMonographPath = async function() {
6827
+ const from = document.getElementById('mg-path-from').value.trim();
6828
+ const to = document.getElementById('mg-path-to').value.trim();
6829
+ const box = document.getElementById('mg-path-result');
6830
+ if (!from || !to) return;
6831
+ box.textContent = 'Loading…';
6832
+ const dir = selectedProjectDir || '';
6833
+ const qs = `?from=${encodeURIComponent(from)}&to=${encodeURIComponent(to)}${dir ? '&dir='+encodeURIComponent(dir) : ''}`;
6834
+ try {
6835
+ const res = await fetch('/api/monograph-path' + qs);
6836
+ const data = await res.json();
6837
+ box.textContent = data.path || data.error || 'No path found.';
6838
+ } catch(e) { box.textContent = 'Error: ' + e.message; }
6839
+ };
6840
+
6841
+ window.runMonographAsk = async function() {
6842
+ const input = document.getElementById('mg-ask-input').value.trim();
6843
+ const mode = document.getElementById('mg-ask-mode').value;
6844
+ const box = document.getElementById('mg-ask-result');
6845
+ if (!input) return;
6846
+ box.textContent = 'Loading…';
6847
+ const dir = selectedProjectDir || '';
6848
+ const qs = `?q=${encodeURIComponent(input)}&mode=${mode}&budget=2000${dir ? '&dir='+encodeURIComponent(dir) : ''}`;
6849
+ try {
6850
+ const res = await fetch('/api/monograph-query' + qs);
6851
+ const data = await res.json();
6852
+ box.textContent = data.result || data.error || '–';
6853
+ } catch(e) { box.textContent = 'Error: ' + e.message; }
6854
+ };
6855
+
6856
+ window.runMonographRename = async function() {
6857
+ const from = document.getElementById('mg-rename-from').value.trim();
6858
+ const to = document.getElementById('mg-rename-to').value.trim();
6859
+ const box = document.getElementById('mg-rename-result');
6860
+ if (!from || !to) { box.textContent = 'Enter both current and new name.'; return; }
6861
+ box.textContent = 'Scanning…';
6862
+ try {
6863
+ const res = await fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_rename', input:{ symbolName: from, newName: to, dryRun: true, projectDir: selectedProjectDir || undefined } }) });
6864
+ const data = await res.json();
6865
+ box.textContent = (data.content && data.content[0] && data.content[0].text) || data.error || JSON.stringify(data);
6866
+ } catch(e) { box.textContent = 'Error: ' + e.message; }
6867
+ };
6868
+
6869
+ window.runMonographCypher = async function() {
6870
+ const input = document.getElementById('mg-cypher-input').value.trim();
6871
+ const box = document.getElementById('mg-cypher-result');
6872
+ if (!input) return;
6873
+ box.textContent = 'Running…';
6874
+ try {
6875
+ const res = await fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_cypher', input:{ query: input, projectDir: selectedProjectDir || undefined } }) });
6876
+ const data = await res.json();
6877
+ const txt = (data.content && data.content[0] && data.content[0].text) || data.error || 'No response';
6878
+ box.innerHTML = `<pre style="margin:0;font-size:9px;line-height:1.5;">${txt.replace(/</g,'&lt;')}</pre>`;
6879
+ } catch(e) { box.textContent = 'Error: ' + e.message; }
6880
+ };
6881
+
6882
+ window.runMonographImpact = async function() {
6883
+ const input = document.getElementById('mg-impact-input').value.trim();
6884
+ const dir = document.getElementById('mg-impact-dir').value;
6885
+ const box = document.getElementById('mg-impact-result');
6886
+ if (!input) return;
6887
+ box.textContent = 'Analyzing…';
6888
+ try {
6889
+ const res = await fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_impact', input:{ target: input, direction: dir, maxDepth: 4, projectDir: selectedProjectDir || undefined } }) });
6890
+ const data = await res.json();
6891
+ box.textContent = (data.content && data.content[0] && data.content[0].text) || data.error || JSON.stringify(data);
6892
+ } catch(e) { box.textContent = 'Error: ' + e.message; }
6893
+ };
6894
+
6895
+ window.runMonographContext = async function() {
6896
+ const input = document.getElementById('mg-context-input').value.trim();
6897
+ const box = document.getElementById('mg-context-result');
6898
+ if (!input) return;
6899
+ box.textContent = 'Loading…';
6900
+ try {
6901
+ const res = await fetch('/api/mcp/call', { method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ tool:'monograph_context', input:{ id: input, projectDir: selectedProjectDir || undefined } }) });
6902
+ const data = await res.json();
6903
+ box.textContent = (data.content && data.content[0] && data.content[0].text) || data.error || JSON.stringify(data);
6904
+ } catch(e) { box.textContent = 'Error: ' + e.message; }
6905
+ };
6906
+
6907
+ window.downloadMonographExport = function(format) {
6908
+ const status = document.getElementById('mg-export-status');
6909
+ const data = mgGraphData;
6910
+ if (!data || !data.nodes) { status.textContent = 'No graph data. Build the graph first.'; return; }
6911
+ const nodes = data.nodes || [], edges = data.edges || [];
6912
+
6913
+ if (format === 'html') {
6914
+ const dir = selectedProjectDir || '';
6915
+ window.open('/api/monograph-html' + (dir ? '?dir='+encodeURIComponent(dir) : ''), '_blank');
6916
+ return;
6917
+ }
6918
+
6919
+ let content = '', filename = '', mime = 'text/plain';
6920
+
6921
+ if (format === 'json') {
6922
+ content = JSON.stringify({ nodes, edges }, null, 2);
6923
+ filename = 'monograph-graph.json'; mime = 'application/json';
6924
+ } else if (format === 'csv-nodes') {
6925
+ const csvQn = s => '"' + String(s || '').replace(/"/g, '""') + '"';
6926
+ content = 'id,label,type,degree\n' + nodes.map(n => `${csvQn(n.id)},${csvQn(n.label||n.id)},${csvQn(n.type||'')},${n.degree||0}`).join('\n');
6927
+ filename = 'monograph-nodes.csv'; mime = 'text/csv';
6928
+ } else if (format === 'csv-edges') {
6929
+ const csvQ = s => '"' + String(s || '').replace(/"/g, '""') + '"';
6930
+ content = 'source,target,relation\n' + edges.map(e => `${csvQ(e.source)},${csvQ(e.target)},${csvQ(e.relation)}`).join('\n');
6931
+ filename = 'monograph-edges.csv'; mime = 'text/csv';
6932
+ } else if (format === 'matrix') {
6933
+ const ids = nodes.map(n => n.id);
6934
+ const adj = new Set(edges.map(e => e.source+'||'+e.target));
6935
+ content = ',' + ids.join(',') + '\n' + ids.map(src => src + ',' + ids.map(tgt => adj.has(src+'||'+tgt)?'1':'0').join(',')).join('\n');
6936
+ filename = 'monograph-matrix.csv'; mime = 'text/csv';
6937
+ } else if (format === 'dot') {
6938
+ content = 'digraph monograph {\n rankdir=LR;\n node [shape=box fontname="monospace" fontsize=9];\n' +
6939
+ nodes.map(n => ` "${n.id.replace(/"/g,'\\"')}" [label="${(n.label||n.id).replace(/"/g,'\\"')}"];`).join('\n') + '\n' +
6940
+ edges.map(e => ` "${e.source.replace(/"/g,'\\"')}" -> "${e.target.replace(/"/g,'\\"')}" [label="${(e.relation||'').replace(/"/g,'\\"')}"];`).join('\n') + '\n}';
6941
+ filename = 'monograph.dot';
6942
+ } else if (format === 'mermaid') {
6943
+ content = 'graph LR\n' + edges.slice(0,200).map(e => {
6944
+ const sl = (e.source.split('/').pop()||e.source).replace(/[^a-zA-Z0-9_]/g,'_');
6945
+ const tl = (e.target.split('/').pop()||e.target).replace(/[^a-zA-Z0-9_]/g,'_');
6946
+ return ` ${sl} --> ${tl}`;
6947
+ }).join('\n');
6948
+ filename = 'monograph.mmd';
6949
+ } else if (format === 'cypher') {
6950
+ const nodeIds = new Set(nodes.map(n=>n.id));
6951
+ content = nodes.map(n => `CREATE (n:Node {id: "${n.id.replace(/"/g,'\\"')}", label: "${(n.label||n.id).replace(/"/g,'\\"')}", type: "${(n.type||'').replace(/"/g,'\\"')}"});`).join('\n') + '\n' +
6952
+ edges.filter(e=>nodeIds.has(e.source)&&nodeIds.has(e.target)).map(e => `MATCH (a:Node {id: "${e.source.replace(/"/g,'\\"')}"}), (b:Node {id: "${e.target.replace(/"/g,'\\"')}"}) CREATE (a)-[:${(e.relation||'REF').replace(/[^A-Z0-9_]/g,'_')}]->(b);`).join('\n');
6953
+ filename = 'monograph.cypher';
6954
+ } else if (format === 'graphml') {
6955
+ content = `<?xml version="1.0" encoding="UTF-8"?>\n<graphml xmlns="http://graphml.graphdrawing.org/graphml">\n<graph id="G" edgedefault="directed">\n` +
6956
+ nodes.map(n=>`<node id="${n.id.replace(/&/g,'&amp;').replace(/"/g,'&quot;')}"><data key="label">${(n.label||n.id).replace(/&/g,'&amp;')}</data></node>`).join('\n') + '\n' +
6957
+ edges.map((e,i)=>`<edge id="e${i}" source="${e.source.replace(/&/g,'&amp;').replace(/"/g,'&quot;')}" target="${e.target.replace(/&/g,'&amp;').replace(/"/g,'&quot;')}"></edge>`).join('\n') + '\n</graph>\n</graphml>';
6958
+ filename = 'monograph.graphml'; mime = 'application/xml';
6959
+ } else if (format === 'gexf') {
6960
+ content = `<?xml version="1.0" encoding="UTF-8"?>\n<gexf xmlns="http://gexf.net/1.3" version="1.3">\n<graph defaultedgetype="directed">\n<nodes>\n` +
6961
+ nodes.map(n=>`<node id="${n.id.replace(/&/g,'&amp;').replace(/"/g,'&quot;')}" label="${(n.label||n.id).replace(/&/g,'&amp;').replace(/"/g,'&quot;')}"/>`).join('\n') + '\n</nodes>\n<edges>\n' +
6962
+ edges.map((e,i)=>`<edge id="${i}" source="${e.source.replace(/&/g,'&amp;').replace(/"/g,'&quot;')}" target="${e.target.replace(/&/g,'&amp;').replace(/"/g,'&quot;')}"/>`).join('\n') + '\n</edges>\n</graph>\n</gexf>';
6963
+ filename = 'monograph.gexf'; mime = 'application/xml';
6964
+ }
6965
+
6966
+ const blob = new Blob([content], { type: mime });
6967
+ const url = URL.createObjectURL(blob);
6968
+ const a = document.createElement('a');
6969
+ a.href = url; a.download = filename; a.click();
6970
+ URL.revokeObjectURL(url);
6971
+ status.textContent = `Downloaded ${filename}`;
6972
+ setTimeout(() => { status.textContent = ''; }, 3000);
6973
+ };
6974
+
6975
+ function renderMonographReport() {
6976
+ const box = document.getElementById('mg-report-content');
6977
+ if (!mgReportData) {
6978
+ box.innerHTML = '<div style="color:var(--muted);font-family:\'Azeret Mono\',monospace;font-size:10px;">No report available — build the graph first.</div>';
6979
+ return;
6980
+ }
6981
+ box.innerHTML = parseMonographReport(mgReportData);
6982
+ }
6983
+
6984
+ function renderMonographOverview() {
6985
+ const statsEl = document.getElementById('mg-overview-stats');
6986
+ const topEl = document.getElementById('mg-overview-top-nodes');
6987
+ const typesEl = document.getElementById('mg-overview-types');
6988
+ if (!statsEl) return;
6989
+
6990
+ const nodes = mgGraphData?.nodes || [];
6991
+ const edges = mgGraphData?.edges || [];
6992
+
6993
+ if (!nodes.length && !edges.length) {
6994
+ statsEl.innerHTML = '<div style="color:var(--muted);font-size:9px;grid-column:1/-1;">No graph data — build the graph first.</div>';
6995
+ if (topEl) topEl.innerHTML = '';
6996
+ if (typesEl) typesEl.innerHTML = '';
6997
+ return;
6998
+ }
6999
+
7000
+ const statCard = (label, val) =>
7001
+ `<div style="background:rgba(255,255,255,0.03);border:1px solid var(--border);padding:8px 10px;border-radius:3px;">` +
7002
+ `<div style="font-size:15px;font-weight:600;color:#e2e2e2;font-family:'Azeret Mono',monospace;">${val}</div>` +
7003
+ `<div style="font-size:8px;letter-spacing:0.1em;color:var(--muted);margin-top:2px;text-transform:uppercase;">${label}</div></div>`;
7004
+
7005
+ const typeCounts = {};
7006
+ nodes.forEach(n => { const t = n.type || 'unknown'; typeCounts[t] = (typeCounts[t] || 0) + 1; });
7007
+ const avgDeg = nodes.length ? (edges.length * 2 / nodes.length).toFixed(1) : '–';
7008
+
7009
+ statsEl.innerHTML =
7010
+ statCard('Nodes', nodes.length.toLocaleString()) +
7011
+ statCard('Edges', edges.length.toLocaleString()) +
7012
+ statCard('Node Types', Object.keys(typeCounts).length) +
7013
+ statCard('Avg Degree', avgDeg);
7014
+
7015
+ if (topEl) {
7016
+ const top = [...nodes].sort((a, b) => (b.degree || 0) - (a.degree || 0)).slice(0, 6);
7017
+ topEl.innerHTML = top.length
7018
+ ? top.map(n =>
7019
+ `<div style="display:flex;justify-content:space-between;font-size:9px;padding:2px 0;border-bottom:1px solid rgba(255,255,255,0.04);">` +
7020
+ `<span style="color:#c0c0ff;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;max-width:180px;" title="${(n.label||n.id).replace(/"/g,'&quot;')}">${n.label||n.id}</span>` +
7021
+ `<span style="color:var(--muted);flex-shrink:0;margin-left:8px;">${n.degree||0}</span></div>`).join('')
7022
+ : '<div style="color:var(--muted);font-size:9px;">–</div>';
7023
+ }
7024
+
7025
+ if (typesEl) {
7026
+ typesEl.innerHTML = Object.entries(typeCounts)
7027
+ .sort((a, b) => b[1] - a[1])
7028
+ .map(([t, c]) =>
7029
+ `<div style="display:flex;justify-content:space-between;font-size:9px;padding:1px 0;">` +
7030
+ `<span style="color:#c0c0ff;">${t}</span><span style="color:var(--muted);">${c}</span></div>`)
7031
+ .join('') || '<div style="color:var(--muted);font-size:9px;">–</div>';
7032
+ }
7033
+ }
7034
+
7035
+ // ═══════════════════════════════════════════════════════════════════
7036
+ // WIKI — Code Knowledge Explorer
7037
+ // ═══════════════════════════════════════════════════════════════════
7038
+
7039
+ const TYPE_COLORS = {
7040
+ File: '#a78bfa', Function: '#00E5C8', Class: '#f59e0b', Interface: '#06b6d4',
7041
+ Method: '#00E5C8', Variable: '#94a3b8', Module: '#ec4899', Concept: '#22c55e',
7042
+ Section: '#22c55e', Default: '#64748b',
7043
+ };
7044
+ const TYPE_ICONS = {
7045
+ File: '📄', Function: '⚡', Class: '🏛', Interface: '◇', Method: '⚡',
7046
+ Variable: '▪', Module: '📦', Concept: '💡', Section: '§', Default: '○',
7047
+ };
7048
+
7049
+ const CODE_LABELS = new Set(['File','Function','Class','Interface','Method','Variable','Module']);
7050
+ const DOC_LABELS = new Set(['Section','Concept']);
7051
+
7052
+ let _wikiCodeData = null; // top nodes by degree (code-heavy)
7053
+ let _wikiDocData = null; // Section + Concept nodes (fetched separately)
7054
+ let _wikiTypeFilter = '';
7055
+ let _wikiModeFilter = 'all'; // 'all' | 'docs' | 'code'
7056
+ let _wikiSearchTimer = null;
7057
+ let _wikiSelectedId = null;
7058
+ let _wikiFtsMode = false; // true when search results come from FTS endpoint
7059
+ let _wikiFtsNodes = null; // FTS result nodes (override normal list)
7060
+ let _wikiRelatedIds = null; // Map of id→distance from "Find Related" BFS
7061
+ let _wikiRelatedSeq = 0; // Monotonic counter — guards against stale "Find Related" responses
7062
+ let _wikiFtsSeq = 0; // Monotonic counter — guards against stale FTS responses
7063
+ let _wikiModalContent = ''; // current modal content for copy
7064
+ let _wikiNodeContent = new Map(); // nodeId → raw content string (avoids onclick injection)
7065
+
7066
+ function _wikiDirQs(extra) {
7067
+ const base = selectedProjectDir ? 'dir=' + encodeURIComponent(selectedProjectDir) : '';
7068
+ return (base || extra) ? '?' + [base, extra].filter(Boolean).join('&') : '';
7069
+ }
7070
+
7071
+ async function loadWikiTab(force) {
7072
+ const needCode = !_wikiCodeData || force;
7073
+ const needDoc = !_wikiDocData || force || (_wikiDocData.nodes || []).length === 0;
7074
+ // Fetch both datasets in parallel when both are needed
7075
+ if (needCode && needDoc) {
7076
+ const [codeRes, docRes] = await Promise.allSettled([
7077
+ fetch('/api/monograph-graph' + _wikiDirQs('limit=500')),
7078
+ fetch('/api/monograph-graph' + _wikiDirQs('labels=Section%2CConcept')),
7079
+ ]);
7080
+ if (codeRes.status === 'fulfilled' && codeRes.value.ok) { try { _wikiCodeData = await codeRes.value.json(); } catch {} }
7081
+ if (docRes.status === 'fulfilled' && docRes.value.ok) { try { _wikiDocData = await docRes.value.json(); } catch {} }
7082
+ } else {
7083
+ if (needCode) {
7084
+ try { const r = await fetch('/api/monograph-graph' + _wikiDirQs('limit=500')); if (r.ok) _wikiCodeData = await r.json(); } catch {}
7085
+ }
7086
+ if (needDoc) {
7087
+ try { const r = await fetch('/api/monograph-graph' + _wikiDirQs('labels=Section%2CConcept')); if (r.ok) _wikiDocData = await r.json(); } catch {}
7088
+ }
7089
+ }
7090
+
7091
+ if (!_wikiCodeData && !_wikiDocData) {
7092
+ document.getElementById('mg-wiki-list').innerHTML = '<div class="wiki-empty" style="height:80px;font-size:9px;color:var(--red);">Graph not built yet</div>';
7093
+ return;
7094
+ }
7095
+
7096
+ // Stats bar — merge both datasets for accurate counts
7097
+ const allNodes = [...(_wikiCodeData?.nodes || []), ...(_wikiDocData?.nodes || [])];
7098
+ const seenIds = new Set();
7099
+ const uniqueNodes = allNodes.filter(n => { if (seenIds.has(n.id)) return false; seenIds.add(n.id); return true; });
7100
+ const typeCounts = {};
7101
+ uniqueNodes.forEach(n => { typeCounts[n.type] = (typeCounts[n.type] || 0) + 1; });
7102
+ document.getElementById('wstat-nodes').textContent = uniqueNodes.length.toLocaleString();
7103
+ document.getElementById('wstat-sections').textContent = ((typeCounts['Section'] || 0) + (typeCounts['Concept'] || 0)).toLocaleString();
7104
+ document.getElementById('wstat-fns').textContent = ((typeCounts['Function'] || 0) + (typeCounts['Method'] || 0)).toLocaleString();
7105
+
7106
+ wikiApplyFilter();
7107
+ }
7108
+
7109
+ window.wikiRefresh = async function() {
7110
+ clearTimeout(_wikiSearchTimer);
7111
+ _wikiCodeData = null; _wikiDocData = null;
7112
+ _wikiFtsNodes = null; _wikiFtsMode = false; _wikiFtsSeq++;
7113
+ _wikiRelatedIds = null; _wikiRelatedSeq++;
7114
+ _wikiNodeContent.clear();
7115
+ const modeEl = document.getElementById('wiki-search-mode');
7116
+ if (modeEl) modeEl.textContent = '';
7117
+ document.getElementById('mg-wiki-list').innerHTML = '<div class="wiki-empty" style="height:80px;font-size:9px;">Refreshing…</div>';
7118
+ await loadWikiTab(true);
7119
+ };
7120
+
7121
+ window.wikiBuildDocs = async function() {
7122
+ const btn = document.getElementById('wstat-build-docs');
7123
+ if (btn) btn.style.opacity = '0.4';
7124
+ const list = document.getElementById('mg-wiki-list');
7125
+ list.innerHTML = '<div class="wiki-empty" style="height:80px;font-size:9px;color:rgba(0,229,135,0.7);">Scanning .md/.mdx/.txt/.rst files…</div>';
7126
+ const dir = selectedProjectDir || '';
7127
+ const qs = dir ? '?dir=' + encodeURIComponent(dir) : '';
7128
+ try {
7129
+ const res = await fetch('/api/monograph-build-docs' + qs, { method: 'POST' });
7130
+ const data = await res.json();
7131
+ if (data.error) {
7132
+ list.innerHTML = `<div class="wiki-empty" style="height:80px;font-size:9px;color:var(--red);">${String(data.error).replace(/&/g,'&amp;').replace(/</g,'&lt;')}</div>`;
7133
+ if (btn) btn.style.opacity = '1';
7134
+ return;
7135
+ }
7136
+ } catch(e) {
7137
+ list.innerHTML = `<div class="wiki-empty" style="height:80px;font-size:9px;color:var(--red);">Error: ${String(e.message).replace(/&/g,'&amp;').replace(/</g,'&lt;')}</div>`;
7138
+ if (btn) btn.style.opacity = '1';
7139
+ return;
7140
+ }
7141
+
7142
+ // Poll status endpoint until done (max 90s)
7143
+ list.innerHTML = '<div class="wiki-empty" style="height:80px;font-size:9px;color:rgba(0,229,135,0.7);">Building docs index…</div>';
7144
+ let attempts = 0;
7145
+ const maxAttempts = 45; // 45 × 2s = 90s
7146
+ const poll = async () => {
7147
+ try {
7148
+ const sr = await fetch('/api/monograph-build-docs-status' + qs);
7149
+ const state = await sr.json();
7150
+ if (state.status === 'done') {
7151
+ const newFiles = state.files || 0;
7152
+ const cached = state.cached || 0;
7153
+ const msg = newFiles > 0
7154
+ ? `Indexed ${newFiles} docs → ${state.sections || 0} sections${cached ? ' (' + cached + ' cached)' : ''}`
7155
+ : (cached > 0 ? `Up to date — ${cached} docs unchanged` : 'No doc files found');
7156
+ list.innerHTML = `<div class="wiki-empty" style="height:60px;font-size:9px;color:rgba(0,229,135,0.9);">${msg}</div>`;
7157
+ _wikiDocData = null;
7158
+ await loadWikiTab(true);
7159
+ if (btn) btn.style.opacity = '1';
7160
+ return;
7161
+ }
7162
+ if (state.status === 'error') {
7163
+ list.innerHTML = `<div class="wiki-empty" style="height:80px;font-size:9px;color:var(--red);">Error: ${String(state.error || 'unknown').replace(/&/g,'&amp;').replace(/</g,'&lt;')}</div>`;
7164
+ if (btn) btn.style.opacity = '1';
7165
+ return;
7166
+ }
7167
+ attempts++;
7168
+ if (attempts < maxAttempts) {
7169
+ const elapsed = Math.round(attempts * 2);
7170
+ list.innerHTML = `<div class="wiki-empty" style="height:80px;font-size:9px;color:rgba(0,229,135,0.7);">Building docs… ${elapsed}s</div>`;
7171
+ setTimeout(poll, 2000);
7172
+ } else {
7173
+ // Timeout — refresh anyway
7174
+ _wikiDocData = null;
7175
+ await loadWikiTab(true);
7176
+ if (btn) btn.style.opacity = '1';
7177
+ }
7178
+ } catch {
7179
+ attempts++;
7180
+ if (attempts < maxAttempts) setTimeout(poll, 2000);
7181
+ else { if (btn) btn.style.opacity = '1'; }
7182
+ }
7183
+ };
7184
+ setTimeout(poll, 2000);
7185
+ };
7186
+
7187
+ function wikiRenderList(nodes, relatedMap) {
7188
+ const list = document.getElementById('mg-wiki-list');
7189
+ if (!nodes || !nodes.length) {
7190
+ list.innerHTML = '<div class="wiki-empty" style="height:80px;font-size:9px;">No results</div>';
7191
+ return;
7192
+ }
7193
+ list.innerHTML = nodes.map(n => {
7194
+ const color = TYPE_COLORS[n.type] || TYPE_COLORS.Default;
7195
+ const icon = TYPE_ICONS[n.type] || TYPE_ICONS.Default;
7196
+ const shortPath = (n.filePath || n.id || '').split('/').slice(-2).join('/');
7197
+ const dist = relatedMap ? relatedMap.get(n.id) : undefined;
7198
+ const distBadge = dist !== undefined ? `<span class="wiki-dist-badge">d${dist}</span>` : '';
7199
+ const snippet = n.snippet ? `<div class="wiki-card-path" style="color:rgba(180,200,240,0.5);white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">${n.snippet.slice(0,80).replace(/&/g,'&amp;').replace(/</g,'&lt;')}</div>` : '';
7200
+ const safeId = n.id.replace(/&/g,'&amp;').replace(/"/g,'&quot;').replace(/'/g,"\\'");
7201
+ return `<div class="wiki-card${n.id === _wikiSelectedId ? ' selected' : ''}" onclick="wikiSelectNode('${safeId}')">
7202
+ <div class="wiki-card-header">
7203
+ <span class="wiki-type-badge" style="background:${color}22;color:${color}">${icon} ${(n.type || '?').replace(/&/g,'&amp;').replace(/</g,'&lt;')}</span>
7204
+ <span class="wiki-card-name">${(n.label || n.id).replace(/&/g,'&amp;').replace(/</g,'&lt;')}</span>
7205
+ ${distBadge || `<span class="wiki-deg">${n.degree || 0}</span>`}
7206
+ </div>
7207
+ <div class="wiki-card-path">${shortPath.replace(/&/g,'&amp;').replace(/</g,'&lt;')}</div>
7208
+ ${snippet}
7209
+ </div>`;
7210
+ }).join('');
7211
+ }
7212
+
7213
+ window.wikiSetMode = function(btn, mode) {
7214
+ clearTimeout(_wikiSearchTimer);
7215
+ document.querySelectorAll('#mg-wiki-mode-bar .wiki-pill').forEach(b => b.classList.remove('active'));
7216
+ btn.classList.add('active');
7217
+ _wikiModeFilter = mode;
7218
+ // Reset FTS and type pill when switching modes
7219
+ if (_wikiFtsMode) {
7220
+ _wikiFtsNodes = null; _wikiFtsMode = false; _wikiFtsSeq++;
7221
+ const modeEl = document.getElementById('wiki-search-mode');
7222
+ if (modeEl) modeEl.textContent = '';
7223
+ const searchEl = document.getElementById('mg-wiki-q');
7224
+ if (searchEl) searchEl.value = '';
7225
+ }
7226
+ _wikiTypeFilter = '';
7227
+ document.querySelectorAll('#mg-wiki-pills .wiki-pill').forEach(b => b.classList.remove('active'));
7228
+ document.querySelector('#mg-wiki-pills .wiki-pill[data-type=""]').classList.add('active');
7229
+ // Show/hide type pills based on mode
7230
+ document.querySelectorAll('#mg-wiki-pills .wiki-pill[data-type]').forEach(b => {
7231
+ const t = b.dataset.type;
7232
+ if (!t) { b.style.display = ''; return; }
7233
+ if (mode === 'docs') b.style.display = DOC_LABELS.has(t) ? '' : 'none';
7234
+ else if (mode === 'code') b.style.display = CODE_LABELS.has(t) ? '' : 'none';
7235
+ else b.style.display = '';
7236
+ });
7237
+ wikiApplyFilter();
7238
+ };
7239
+
7240
+ window.wikiSetType = function(btn, type) {
7241
+ clearTimeout(_wikiSearchTimer);
7242
+ document.querySelectorAll('#mg-wiki-pills .wiki-pill').forEach(b => b.classList.remove('active'));
7243
+ btn.classList.add('active');
7244
+ _wikiTypeFilter = type;
7245
+ wikiApplyFilter();
7246
+ };
7247
+
7248
+ function wikiApplyFilter() {
7249
+ // FTS mode overrides normal node list
7250
+ if (_wikiFtsMode && _wikiFtsNodes) {
7251
+ let nodes = _wikiFtsNodes;
7252
+ if (_wikiTypeFilter) nodes = nodes.filter(n => n.type === _wikiTypeFilter);
7253
+ wikiRenderList(nodes.slice(0, 300));
7254
+ return;
7255
+ }
7256
+
7257
+ // Related re-ranking mode: sort cached nodes by graph distance
7258
+ let baseNodes;
7259
+ if (_wikiModeFilter === 'docs') {
7260
+ baseNodes = (_wikiDocData?.nodes || []);
7261
+ } else if (_wikiModeFilter === 'code') {
7262
+ baseNodes = (_wikiCodeData?.nodes || []).filter(n => CODE_LABELS.has(n.type));
7263
+ } else {
7264
+ const seen = new Set();
7265
+ baseNodes = [...(_wikiCodeData?.nodes || []), ...(_wikiDocData?.nodes || [])].filter(n => {
7266
+ if (seen.has(n.id)) return false; seen.add(n.id); return true;
7267
+ });
7268
+ }
7269
+
7270
+ let nodes = baseNodes;
7271
+ if (_wikiRelatedIds) {
7272
+ // Sort by graph distance — nodes not in the map go to the end
7273
+ nodes = [...nodes].sort((a, b) => {
7274
+ const da = _wikiRelatedIds.get(a.id) ?? 999;
7275
+ const db2 = _wikiRelatedIds.get(b.id) ?? 999;
7276
+ return da - db2;
7277
+ });
7278
+ }
7279
+
7280
+ const q = (document.getElementById('mg-wiki-q').value || '').trim().toLowerCase();
7281
+ if (_wikiTypeFilter) nodes = nodes.filter(n => n.type === _wikiTypeFilter);
7282
+ if (q) nodes = nodes.filter(n => (n.label || n.id || '').toLowerCase().includes(q));
7283
+ if (!q && !_wikiTypeFilter && _wikiModeFilter === 'all') nodes = nodes.slice(0, 200);
7284
+ wikiRenderList(nodes.slice(0, 300), _wikiRelatedIds);
7285
+ }
7286
+
7287
+ window.wikiOnSearch = function(val) {
7288
+ clearTimeout(_wikiSearchTimer);
7289
+ const q = (val || '').trim();
7290
+ const modeEl = document.getElementById('wiki-search-mode');
7291
+ if (q.length >= 3) {
7292
+ // Use FTS endpoint for meaningful queries
7293
+ const ftsSeq = ++_wikiFtsSeq; // capture before setTimeout so closure sees correct value
7294
+ _wikiSearchTimer = setTimeout(async () => {
7295
+ if (modeEl) modeEl.textContent = 'searching…';
7296
+ try {
7297
+ const qs2 = _wikiDirQs('q=' + encodeURIComponent(q) + '&limit=80');
7298
+ const r = await fetch('/api/monograph-fts' + qs2);
7299
+ if (ftsSeq !== _wikiFtsSeq) return; // a newer search superseded this one
7300
+ if (r.ok) {
7301
+ const data = await r.json();
7302
+ if (ftsSeq !== _wikiFtsSeq) return; // superseded while parsing JSON
7303
+ _wikiFtsNodes = data.nodes || [];
7304
+ _wikiFtsMode = true;
7305
+ if (modeEl) modeEl.textContent = `FTS — ${_wikiFtsNodes.length} matches`;
7306
+ }
7307
+ } catch { if (ftsSeq === _wikiFtsSeq) { _wikiFtsNodes = null; _wikiFtsMode = false; if (modeEl) modeEl.textContent = ''; } }
7308
+ if (ftsSeq === _wikiFtsSeq) wikiApplyFilter();
7309
+ }, 280);
7310
+ } else {
7311
+ _wikiFtsSeq++; // immediately invalidate any in-flight FTS fetch
7312
+ _wikiSearchTimer = setTimeout(() => {
7313
+ // Always clear FTS state and indicator for sub-3-char queries
7314
+ _wikiFtsNodes = null; _wikiFtsMode = false;
7315
+ if (modeEl) modeEl.textContent = '';
7316
+ wikiApplyFilter();
7317
+ }, 150);
7318
+ }
7319
+ };
7320
+
7321
+ window.wikiSelectNode = async function(nodeId) {
7322
+ _wikiSelectedId = nodeId;
7323
+ const _escapedId = "'" + nodeId.replace(/'/g,"\\'") + "'";
7324
+ document.querySelectorAll('.wiki-card').forEach(c => {
7325
+ c.classList.toggle('selected', c.getAttribute('onclick') && c.getAttribute('onclick').includes(_escapedId));
7326
+ });
7327
+
7328
+ const detail = document.getElementById('mg-wiki-detail');
7329
+ detail.innerHTML = '<div class="wiki-empty" style="height:80px;font-size:9px;">Loading…</div>';
7330
+
7331
+ try {
7332
+ const dirParam = selectedProjectDir ? '&dir=' + encodeURIComponent(selectedProjectDir) : '';
7333
+ // Fetch both context (edges) and content in parallel
7334
+ const [ctxRes, contentRes] = await Promise.all([
7335
+ fetch('/api/mcp/call' + (selectedProjectDir ? '?dir=' + encodeURIComponent(selectedProjectDir) : ''), {
7336
+ method: 'POST', headers: { 'Content-Type': 'application/json' },
7337
+ body: JSON.stringify({ tool: 'monograph_context', input: { id: nodeId } }),
7338
+ }),
7339
+ fetch('/api/monograph-content?id=' + encodeURIComponent(nodeId) + dirParam),
7340
+ ]);
7341
+ // Bail if the user already clicked a different card while fetches were in flight
7342
+ if (_wikiSelectedId !== nodeId) return;
7343
+ const ctxData = await ctxRes.json();
7344
+ const contentData = contentRes.ok ? await contentRes.json() : {};
7345
+ const txt = (ctxData.content && ctxData.content[0] && ctxData.content[0].text) || '';
7346
+
7347
+ const nameMatch = txt.match(/^# (.+?) \((\w+)\)/m);
7348
+ const fileMatch = txt.match(/File: (.+)/);
7349
+ const outSection = txt.match(/\*\*Imports \/ depends on \((\d+)\):\*\*\n([\s\S]*?)(?=\*\*Used by|$)/);
7350
+ const inSection = txt.match(/\*\*Used by \/ depended on by \((\d+)\):\*\*\n([\s\S]*?)$/);
7351
+
7352
+ const name = contentData.name || (nameMatch ? nameMatch[1] : nodeId);
7353
+ const type = contentData.type || (nameMatch ? nameMatch[2] : 'Unknown');
7354
+ const filePath = contentData.filePath || (fileMatch ? fileMatch[1] : '');
7355
+ const startLine = contentData.startLine || 0;
7356
+ const language = contentData.language || '';
7357
+ const color = TYPE_COLORS[type] || TYPE_COLORS.Default;
7358
+ const icon = TYPE_ICONS[type] || TYPE_ICONS.Default;
7359
+ const content = contentData.content || '';
7360
+
7361
+ const parseEdges = (section) => {
7362
+ if (!section) return [];
7363
+ return section.trim().split('\n').filter(l => l.trim() && l.trim() !== '(none)').map(l => {
7364
+ const m = l.match(/→\s+(.+?)\s+\[(\w+)\]/);
7365
+ const m2 = l.match(/←\s+(.+?)\s+\[(\w+)\]/);
7366
+ const matched = m || m2;
7367
+ return matched ? { name: matched[1], rel: matched[2] } : null;
7368
+ }).filter(Boolean);
7369
+ };
7370
+
7371
+ const outEdges = parseEdges(outSection ? outSection[2] : '');
7372
+ const inEdges = parseEdges(inSection ? inSection[2] : '');
7373
+ const outCount = outSection ? outSection[1] : outEdges.length;
7374
+ const inCount = inSection ? inSection[1] : inEdges.length;
7375
+
7376
+ const edgeItem = (e, dir) => {
7377
+ const rel = e.rel || 'REF';
7378
+ const relColor = { IMPORTS:'#3b82f6',CALLS:'#8b5cf6',EXTENDS:'#ef4444',IMPLEMENTS:'#f97316',DEFINES:'#22c55e',CONTAINS:'#64748b',REFERENCES:'#a78bfa',TAGGED_AS:'#22c55e',PARENT_SECTION:'#94a3b8',REF:'#475569' }[rel] || '#475569';
7379
+ const safeName = e.name.replace(/&/g,'&amp;').replace(/"/g,'&quot;').replace(/'/g,"\\'");
7380
+ return `<div class="wiki-edge-item" onclick="wikiSelectNode('${safeName}')">
7381
+ <span class="wiki-edge-rel" style="color:${relColor}">${dir === 'out' ? '→' : '←'} ${rel}</span>
7382
+ <span class="wiki-edge-name">${e.name.replace(/&/g,'&amp;').replace(/</g,'&lt;')}</span>
7383
+ </div>`;
7384
+ };
7385
+
7386
+ const pathParts = filePath ? filePath.split('/') : [];
7387
+ const fileName = pathParts.pop() || filePath;
7388
+ const dirPath = pathParts.join('/');
7389
+
7390
+ if (content) _wikiNodeContent.set(nodeId, content);
7391
+ // Helper: escape for HTML text content and attributes
7392
+ const hEsc = s => String(s).replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;');
7393
+ // Helper: escape for single-quoted JS string inside a double-quoted HTML attribute
7394
+ const attrEsc = s => String(s).replace(/&/g,'&amp;').replace(/"/g,'&quot;').replace(/'/g,"\\'");
7395
+ const safeNodeId = attrEsc(nodeId);
7396
+ const contentHtml = content ? `
7397
+ <div class="wiki-section-title" style="display:flex;align-items:center;justify-content:space-between;">
7398
+ <span>Content${startLine ? ' · line ' + startLine : ''}${language ? ' · ' + hEsc(language) : ''}</span>
7399
+ <button class="wiki-action-btn copy" data-copy-node="${String(nodeId).replace(/&/g,'&amp;').replace(/"/g,'&quot;')}" style="font-size:7px;padding:2px 7px;margin:0;" onclick="wikiCopyNodeContent('${safeNodeId}')">COPY</button>
7400
+ </div>
7401
+ <div class="wiki-content-block collapsed" id="wiki-content-${nodeId.replace(/[^a-z0-9]/gi,'_')}" onclick="this.classList.toggle('collapsed')" title="Click to expand/collapse">${content.replace(/&/g,'&amp;').replace(/</g,'&lt;').replace(/>/g,'&gt;')}</div>
7402
+ ` : '';
7403
+
7404
+ detail.innerHTML = `
7405
+ <div class="wiki-detail-header">
7406
+ <div class="wiki-detail-type" style="color:${color}">${icon} ${hEsc(type)}</div>
7407
+ <div class="wiki-detail-name">${hEsc(name)}</div>
7408
+ ${filePath ? `<div class="wiki-detail-path">${dirPath ? hEsc(dirPath) + '/' : ''}<span>${hEsc(fileName)}</span></div>` : ''}
7409
+ </div>
7410
+
7411
+ <div class="wiki-action-row">
7412
+ <button class="wiki-action-btn copy" onclick="wikiCopyContext('${safeNodeId}')">📋 COPY CONTEXT</button>
7413
+ <button class="wiki-action-btn explain" onclick="wikiExplainThis('${safeNodeId}')">🤖 EXPLAIN THIS</button>
7414
+ <button class="wiki-action-btn related" onclick="wikiFindRelated('${safeNodeId}')">🔍 FIND RELATED</button>
7415
+ </div>
7416
+
7417
+ <div class="wiki-stat-row">
7418
+ <div class="wiki-stat"><div class="wiki-stat-label">Outgoing</div><div class="wiki-stat-val" style="color:rgba(0,229,135,0.9)">${outCount}</div></div>
7419
+ <div class="wiki-stat"><div class="wiki-stat-label">Incoming</div><div class="wiki-stat-val" style="color:rgba(123,97,255,0.9)">${inCount}</div></div>
7420
+ <div class="wiki-stat"><div class="wiki-stat-label">Degree</div><div class="wiki-stat-val">${+outCount + +inCount}</div></div>
7421
+ </div>
7422
+
7423
+ ${contentHtml}
7424
+
7425
+ ${outEdges.length ? `<div class="wiki-section-title">Depends on (${outEdges.length})</div><div class="wiki-edge-list">${outEdges.map(e => edgeItem(e,'out')).join('')}</div>` : ''}
7426
+ ${inEdges.length ? `<div class="wiki-section-title">Used by (${inEdges.length})</div><div class="wiki-edge-list">${inEdges.map(e => edgeItem(e,'in')).join('')}</div>` : ''}
7427
+ ${!outEdges.length && !inEdges.length ? '<div style="color:var(--dim);font-size:10px;margin-top:12px;">No connections found</div>' : ''}
7428
+
7429
+ <div class="wiki-section-title">Find path from here</div>
7430
+ <div style="display:flex;gap:6px;align-items:center;">
7431
+ <input class="mg-input" id="wpath-to" placeholder="Target node name…" style="flex:1;" onkeydown="if(event.key==='Enter')wikiPath('${safeNodeId}')">
7432
+ <button class="mg-action-btn" onclick="wikiPath('${safeNodeId}')">PATH →</button>
7433
+ </div>
7434
+ <div id="wpath-result" style="margin-top:8px;font-size:10px;color:var(--muted);"></div>
7435
+ `;
7436
+ } catch(e) {
7437
+ detail.innerHTML = `<div style="color:var(--red);padding:20px;font-size:10px;">Error: ${String(e.message).replace(/</g,'&lt;')}</div>`;
7438
+ }
7439
+ };
7440
+
7441
+ window.wikiPath = async function(fromId) {
7442
+ const to = document.getElementById('wpath-to')?.value.trim();
7443
+ const box = document.getElementById('wpath-result');
7444
+ if (!to || !box) return;
7445
+ box.textContent = 'Finding path…';
7446
+ try {
7447
+ const r = await fetch('/api/monograph-path?from=' + encodeURIComponent(fromId) + '&to=' + encodeURIComponent(to) + (selectedProjectDir ? '&dir='+encodeURIComponent(selectedProjectDir) : ''));
7448
+ const d = await r.json();
7449
+ box.textContent = d.path || d.error || 'No path found';
7450
+ } catch(e) { box.textContent = 'Error: ' + e.message; }
7451
+ };
7452
+
7453
+ // ── Copy Context ── Build markdown and copy to clipboard
7454
+ window.wikiCopyNodeContent = async function(nodeId) {
7455
+ const content = _wikiNodeContent.get(nodeId) || '';
7456
+ try {
7457
+ await navigator.clipboard.writeText(content);
7458
+ const btn = document.querySelector('[data-copy-node="' + CSS.escape(nodeId) + '"]');
7459
+ if (btn) { btn.textContent = 'COPIED!'; setTimeout(() => btn.textContent = 'COPY', 1200); }
7460
+ } catch(e) { console.error('wikiCopyNodeContent:', e); }
7461
+ };
7462
+
7463
+ window.wikiCopyContext = async function(nodeId) {
7464
+ const dirParam = selectedProjectDir ? '&dir=' + encodeURIComponent(selectedProjectDir) : '';
7465
+ try {
7466
+ const r = await fetch('/api/monograph-ai-context?id=' + encodeURIComponent(nodeId) + dirParam);
7467
+ if (!r.ok) { alert('Could not load context'); return; }
7468
+ const data = await r.json();
7469
+ await navigator.clipboard.writeText(data.markdown || '');
7470
+ // Flash button
7471
+ const btn = document.querySelector('.wiki-action-btn.copy');
7472
+ if (btn) { const orig = btn.textContent; btn.textContent = '✓ COPIED!'; setTimeout(() => btn.textContent = orig, 1800); }
7473
+ } catch(e) { console.error('wikiCopyContext:', e); }
7474
+ };
7475
+
7476
+ // ── Explain This ── Fetch AI context, build prompt, show in modal
7477
+ window.wikiExplainThis = async function(nodeId) {
7478
+ const modal = document.getElementById('wiki-explain-modal');
7479
+ const body = document.getElementById('wiki-modal-body');
7480
+ modal.classList.add('open');
7481
+ body.textContent = 'Loading context…';
7482
+ const dirParam = selectedProjectDir ? '&dir=' + encodeURIComponent(selectedProjectDir) : '';
7483
+ try {
7484
+ const r = await fetch('/api/monograph-ai-context?id=' + encodeURIComponent(nodeId) + dirParam);
7485
+ if (!r.ok) { body.textContent = 'Error loading context.'; return; }
7486
+ const data = await r.json();
7487
+ const prompt = `You are a senior software engineer reviewing this code/documentation node. Explain what it does, its purpose, how it fits into the larger system, and any notable patterns or concerns.\n\n---\n\n${data.markdown}`;
7488
+ _wikiModalContent = prompt;
7489
+ body.textContent = prompt;
7490
+ } catch(e) { body.textContent = 'Error: ' + e.message; }
7491
+ };
7492
+
7493
+ // ── Find Related ── BFS the graph and re-rank wiki list by distance
7494
+ window.wikiFindRelated = async function(nodeId) {
7495
+ const seq = ++_wikiRelatedSeq; // capture sequence number before await
7496
+ const dirParam = selectedProjectDir ? '&dir=' + encodeURIComponent(selectedProjectDir) : '';
7497
+ const modeEl = document.getElementById('wiki-search-mode');
7498
+ if (modeEl) modeEl.textContent = 'finding related…';
7499
+ try {
7500
+ const r = await fetch('/api/monograph-related?id=' + encodeURIComponent(nodeId) + dirParam + '&depth=3&limit=120');
7501
+ if (seq !== _wikiRelatedSeq) return; // a newer request already landed
7502
+ if (!r.ok) { if (modeEl) modeEl.textContent = ''; return; }
7503
+ const data = await r.json();
7504
+ if (seq !== _wikiRelatedSeq) return; // a newer request landed while we were parsing JSON
7505
+ _wikiRelatedIds = new Map((data.related || []).map(x => [x.id, x.distance]));
7506
+ if (modeEl) modeEl.textContent = `Related — sorted by graph distance from ${nodeId.split(':').pop().slice(0,20)}`;
7507
+ wikiApplyFilter();
7508
+ } catch(e) { if (modeEl) modeEl.textContent = ''; console.error('wikiFindRelated:', e); }
7509
+ };
7510
+
7511
+ // ── Modal helpers ──
7512
+ window.wikiCloseModal = function() {
7513
+ document.getElementById('wiki-explain-modal').classList.remove('open');
7514
+ };
7515
+ window.wikiCopyModal = async function() {
7516
+ await navigator.clipboard.writeText(_wikiModalContent);
7517
+ const btn = document.querySelector('#wiki-explain-modal .wiki-action-btn.copy');
7518
+ if (btn) { const orig = btn.textContent; btn.textContent = '✓ COPIED!'; setTimeout(() => btn.textContent = orig, 1800); }
7519
+ };
7520
+
7521
+ async function loadDocStats() { /* replaced by loadWikiTab */ }
7522
+
5232
7523
  // ═══════════════════════════════════════════════════════════════════
5233
7524
  // BOOT
5234
7525
  // ═══════════════════════════════════════════════════════════════════
7526
+ // ═══════════════════════════════════════════════════════════════════
7527
+ // MASTERMIND OVERLAY
7528
+ // ═══════════════════════════════════════════════════════════════════
7529
+ window.openMastermindOverlay = function() {
7530
+ const overlay = document.getElementById('mastermind-overlay');
7531
+ const frame = document.getElementById('mm-frame');
7532
+ overlay.classList.add('open');
7533
+ // Lazy-load the iframe — only set src on first open
7534
+ if (!frame.src || frame.src === window.location.href) {
7535
+ frame.src = '/mastermind';
7536
+ }
7537
+ };
7538
+
7539
+ window.closeMastermindOverlay = function() {
7540
+ document.getElementById('mastermind-overlay').classList.remove('open');
7541
+ };
7542
+
5235
7543
  (function boot() {
5236
7544
  // Set conn dot initial state
5237
7545
  const dot = document.getElementById('conn-dot');
@@ -5241,5 +7549,17 @@ window.closePalaceOverlay = function() {
5241
7549
  connectSSE();
5242
7550
  })();
5243
7551
  </script>
7552
+
7553
+ <!-- ═══ Wiki: Explain This modal ══════════════════════════════════════════ -->
7554
+ <div id="wiki-explain-modal" onclick="if(event.target===this)wikiCloseModal()">
7555
+ <div class="wiki-modal-box">
7556
+ <div class="wiki-modal-title">AI CONTEXT — COPY & PASTE INTO YOUR AI CHAT</div>
7557
+ <div class="wiki-modal-body" id="wiki-modal-body"></div>
7558
+ <div class="wiki-modal-actions">
7559
+ <button class="wiki-action-btn copy" onclick="wikiCopyModal()">COPY ALL</button>
7560
+ <button class="wiki-action-btn" style="color:var(--muted);border-color:var(--border);" onclick="wikiCloseModal()">CLOSE</button>
7561
+ </div>
7562
+ </div>
7563
+ </div>
5244
7564
  </body>
5245
7565
  </html>