@dotsetlabs/bellwether 0.10.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 (403) hide show
  1. package/CHANGELOG.md +291 -0
  2. package/LICENSE +21 -0
  3. package/README.md +739 -0
  4. package/dist/auth/credentials.d.ts +64 -0
  5. package/dist/auth/credentials.js +218 -0
  6. package/dist/auth/index.d.ts +6 -0
  7. package/dist/auth/index.js +6 -0
  8. package/dist/auth/keychain.d.ts +64 -0
  9. package/dist/auth/keychain.js +268 -0
  10. package/dist/baseline/ab-testing.d.ts +80 -0
  11. package/dist/baseline/ab-testing.js +236 -0
  12. package/dist/baseline/ai-compatibility-scorer.d.ts +95 -0
  13. package/dist/baseline/ai-compatibility-scorer.js +606 -0
  14. package/dist/baseline/calibration.d.ts +77 -0
  15. package/dist/baseline/calibration.js +136 -0
  16. package/dist/baseline/category-matching.d.ts +85 -0
  17. package/dist/baseline/category-matching.js +289 -0
  18. package/dist/baseline/change-impact-analyzer.d.ts +98 -0
  19. package/dist/baseline/change-impact-analyzer.js +592 -0
  20. package/dist/baseline/comparator.d.ts +64 -0
  21. package/dist/baseline/comparator.js +916 -0
  22. package/dist/baseline/confidence.d.ts +55 -0
  23. package/dist/baseline/confidence.js +122 -0
  24. package/dist/baseline/converter.d.ts +61 -0
  25. package/dist/baseline/converter.js +585 -0
  26. package/dist/baseline/dependency-analyzer.d.ts +89 -0
  27. package/dist/baseline/dependency-analyzer.js +567 -0
  28. package/dist/baseline/deprecation-tracker.d.ts +133 -0
  29. package/dist/baseline/deprecation-tracker.js +322 -0
  30. package/dist/baseline/diff.d.ts +55 -0
  31. package/dist/baseline/diff.js +1584 -0
  32. package/dist/baseline/documentation-scorer.d.ts +205 -0
  33. package/dist/baseline/documentation-scorer.js +466 -0
  34. package/dist/baseline/embeddings.d.ts +118 -0
  35. package/dist/baseline/embeddings.js +251 -0
  36. package/dist/baseline/error-analyzer.d.ts +198 -0
  37. package/dist/baseline/error-analyzer.js +721 -0
  38. package/dist/baseline/evaluation/evaluator.d.ts +42 -0
  39. package/dist/baseline/evaluation/evaluator.js +323 -0
  40. package/dist/baseline/evaluation/expanded-dataset.d.ts +45 -0
  41. package/dist/baseline/evaluation/expanded-dataset.js +1164 -0
  42. package/dist/baseline/evaluation/golden-dataset.d.ts +58 -0
  43. package/dist/baseline/evaluation/golden-dataset.js +717 -0
  44. package/dist/baseline/evaluation/index.d.ts +15 -0
  45. package/dist/baseline/evaluation/index.js +15 -0
  46. package/dist/baseline/evaluation/types.d.ts +186 -0
  47. package/dist/baseline/evaluation/types.js +8 -0
  48. package/dist/baseline/external-dependency-detector.d.ts +181 -0
  49. package/dist/baseline/external-dependency-detector.js +524 -0
  50. package/dist/baseline/golden-output.d.ts +162 -0
  51. package/dist/baseline/golden-output.js +636 -0
  52. package/dist/baseline/health-scorer.d.ts +174 -0
  53. package/dist/baseline/health-scorer.js +451 -0
  54. package/dist/baseline/incremental-checker.d.ts +97 -0
  55. package/dist/baseline/incremental-checker.js +174 -0
  56. package/dist/baseline/index.d.ts +31 -0
  57. package/dist/baseline/index.js +42 -0
  58. package/dist/baseline/migration-generator.d.ts +137 -0
  59. package/dist/baseline/migration-generator.js +554 -0
  60. package/dist/baseline/migrations.d.ts +60 -0
  61. package/dist/baseline/migrations.js +197 -0
  62. package/dist/baseline/performance-tracker.d.ts +214 -0
  63. package/dist/baseline/performance-tracker.js +577 -0
  64. package/dist/baseline/pr-comment-generator.d.ts +117 -0
  65. package/dist/baseline/pr-comment-generator.js +546 -0
  66. package/dist/baseline/response-fingerprint.d.ts +127 -0
  67. package/dist/baseline/response-fingerprint.js +728 -0
  68. package/dist/baseline/response-schema-tracker.d.ts +129 -0
  69. package/dist/baseline/response-schema-tracker.js +420 -0
  70. package/dist/baseline/risk-scorer.d.ts +54 -0
  71. package/dist/baseline/risk-scorer.js +434 -0
  72. package/dist/baseline/saver.d.ts +89 -0
  73. package/dist/baseline/saver.js +554 -0
  74. package/dist/baseline/scenario-generator.d.ts +151 -0
  75. package/dist/baseline/scenario-generator.js +905 -0
  76. package/dist/baseline/schema-compare.d.ts +86 -0
  77. package/dist/baseline/schema-compare.js +557 -0
  78. package/dist/baseline/schema-evolution.d.ts +189 -0
  79. package/dist/baseline/schema-evolution.js +467 -0
  80. package/dist/baseline/semantic.d.ts +203 -0
  81. package/dist/baseline/semantic.js +908 -0
  82. package/dist/baseline/synonyms.d.ts +60 -0
  83. package/dist/baseline/synonyms.js +386 -0
  84. package/dist/baseline/telemetry.d.ts +165 -0
  85. package/dist/baseline/telemetry.js +294 -0
  86. package/dist/baseline/test-pruner.d.ts +120 -0
  87. package/dist/baseline/test-pruner.js +387 -0
  88. package/dist/baseline/types.d.ts +449 -0
  89. package/dist/baseline/types.js +5 -0
  90. package/dist/baseline/version.d.ts +138 -0
  91. package/dist/baseline/version.js +206 -0
  92. package/dist/cache/index.d.ts +5 -0
  93. package/dist/cache/index.js +5 -0
  94. package/dist/cache/response-cache.d.ts +151 -0
  95. package/dist/cache/response-cache.js +287 -0
  96. package/dist/ci/index.d.ts +60 -0
  97. package/dist/ci/index.js +342 -0
  98. package/dist/cli/commands/auth.d.ts +12 -0
  99. package/dist/cli/commands/auth.js +352 -0
  100. package/dist/cli/commands/badge.d.ts +3 -0
  101. package/dist/cli/commands/badge.js +74 -0
  102. package/dist/cli/commands/baseline-accept.d.ts +15 -0
  103. package/dist/cli/commands/baseline-accept.js +178 -0
  104. package/dist/cli/commands/baseline-migrate.d.ts +12 -0
  105. package/dist/cli/commands/baseline-migrate.js +164 -0
  106. package/dist/cli/commands/baseline.d.ts +14 -0
  107. package/dist/cli/commands/baseline.js +449 -0
  108. package/dist/cli/commands/beta.d.ts +10 -0
  109. package/dist/cli/commands/beta.js +231 -0
  110. package/dist/cli/commands/check.d.ts +11 -0
  111. package/dist/cli/commands/check.js +820 -0
  112. package/dist/cli/commands/cloud/badge.d.ts +3 -0
  113. package/dist/cli/commands/cloud/badge.js +74 -0
  114. package/dist/cli/commands/cloud/diff.d.ts +6 -0
  115. package/dist/cli/commands/cloud/diff.js +79 -0
  116. package/dist/cli/commands/cloud/history.d.ts +6 -0
  117. package/dist/cli/commands/cloud/history.js +102 -0
  118. package/dist/cli/commands/cloud/link.d.ts +9 -0
  119. package/dist/cli/commands/cloud/link.js +119 -0
  120. package/dist/cli/commands/cloud/login.d.ts +7 -0
  121. package/dist/cli/commands/cloud/login.js +499 -0
  122. package/dist/cli/commands/cloud/projects.d.ts +6 -0
  123. package/dist/cli/commands/cloud/projects.js +44 -0
  124. package/dist/cli/commands/cloud/shared.d.ts +7 -0
  125. package/dist/cli/commands/cloud/shared.js +42 -0
  126. package/dist/cli/commands/cloud/teams.d.ts +8 -0
  127. package/dist/cli/commands/cloud/teams.js +169 -0
  128. package/dist/cli/commands/cloud/upload.d.ts +8 -0
  129. package/dist/cli/commands/cloud/upload.js +181 -0
  130. package/dist/cli/commands/contract.d.ts +11 -0
  131. package/dist/cli/commands/contract.js +280 -0
  132. package/dist/cli/commands/discover.d.ts +3 -0
  133. package/dist/cli/commands/discover.js +82 -0
  134. package/dist/cli/commands/eval.d.ts +9 -0
  135. package/dist/cli/commands/eval.js +187 -0
  136. package/dist/cli/commands/explore.d.ts +11 -0
  137. package/dist/cli/commands/explore.js +437 -0
  138. package/dist/cli/commands/feedback.d.ts +9 -0
  139. package/dist/cli/commands/feedback.js +174 -0
  140. package/dist/cli/commands/golden.d.ts +12 -0
  141. package/dist/cli/commands/golden.js +407 -0
  142. package/dist/cli/commands/history.d.ts +10 -0
  143. package/dist/cli/commands/history.js +202 -0
  144. package/dist/cli/commands/init.d.ts +9 -0
  145. package/dist/cli/commands/init.js +219 -0
  146. package/dist/cli/commands/interview.d.ts +3 -0
  147. package/dist/cli/commands/interview.js +903 -0
  148. package/dist/cli/commands/link.d.ts +10 -0
  149. package/dist/cli/commands/link.js +169 -0
  150. package/dist/cli/commands/login.d.ts +7 -0
  151. package/dist/cli/commands/login.js +499 -0
  152. package/dist/cli/commands/preset.d.ts +33 -0
  153. package/dist/cli/commands/preset.js +297 -0
  154. package/dist/cli/commands/profile.d.ts +33 -0
  155. package/dist/cli/commands/profile.js +286 -0
  156. package/dist/cli/commands/registry.d.ts +11 -0
  157. package/dist/cli/commands/registry.js +146 -0
  158. package/dist/cli/commands/shared.d.ts +79 -0
  159. package/dist/cli/commands/shared.js +196 -0
  160. package/dist/cli/commands/teams.d.ts +8 -0
  161. package/dist/cli/commands/teams.js +169 -0
  162. package/dist/cli/commands/test.d.ts +9 -0
  163. package/dist/cli/commands/test.js +500 -0
  164. package/dist/cli/commands/upload.d.ts +8 -0
  165. package/dist/cli/commands/upload.js +223 -0
  166. package/dist/cli/commands/validate-config.d.ts +6 -0
  167. package/dist/cli/commands/validate-config.js +35 -0
  168. package/dist/cli/commands/verify.d.ts +11 -0
  169. package/dist/cli/commands/verify.js +283 -0
  170. package/dist/cli/commands/watch.d.ts +12 -0
  171. package/dist/cli/commands/watch.js +253 -0
  172. package/dist/cli/index.d.ts +3 -0
  173. package/dist/cli/index.js +178 -0
  174. package/dist/cli/interactive.d.ts +47 -0
  175. package/dist/cli/interactive.js +216 -0
  176. package/dist/cli/output/terminal-reporter.d.ts +19 -0
  177. package/dist/cli/output/terminal-reporter.js +104 -0
  178. package/dist/cli/output.d.ts +226 -0
  179. package/dist/cli/output.js +438 -0
  180. package/dist/cli/utils/env.d.ts +5 -0
  181. package/dist/cli/utils/env.js +14 -0
  182. package/dist/cli/utils/progress.d.ts +59 -0
  183. package/dist/cli/utils/progress.js +206 -0
  184. package/dist/cli/utils/server-context.d.ts +10 -0
  185. package/dist/cli/utils/server-context.js +36 -0
  186. package/dist/cloud/auth.d.ts +144 -0
  187. package/dist/cloud/auth.js +374 -0
  188. package/dist/cloud/client.d.ts +24 -0
  189. package/dist/cloud/client.js +65 -0
  190. package/dist/cloud/http-client.d.ts +38 -0
  191. package/dist/cloud/http-client.js +215 -0
  192. package/dist/cloud/index.d.ts +23 -0
  193. package/dist/cloud/index.js +25 -0
  194. package/dist/cloud/mock-client.d.ts +107 -0
  195. package/dist/cloud/mock-client.js +545 -0
  196. package/dist/cloud/types.d.ts +515 -0
  197. package/dist/cloud/types.js +15 -0
  198. package/dist/config/defaults.d.ts +160 -0
  199. package/dist/config/defaults.js +169 -0
  200. package/dist/config/loader.d.ts +24 -0
  201. package/dist/config/loader.js +122 -0
  202. package/dist/config/template.d.ts +42 -0
  203. package/dist/config/template.js +647 -0
  204. package/dist/config/validator.d.ts +2112 -0
  205. package/dist/config/validator.js +658 -0
  206. package/dist/constants/cloud.d.ts +107 -0
  207. package/dist/constants/cloud.js +110 -0
  208. package/dist/constants/core.d.ts +521 -0
  209. package/dist/constants/core.js +556 -0
  210. package/dist/constants/testing.d.ts +1283 -0
  211. package/dist/constants/testing.js +1568 -0
  212. package/dist/constants.d.ts +10 -0
  213. package/dist/constants.js +10 -0
  214. package/dist/contract/index.d.ts +6 -0
  215. package/dist/contract/index.js +5 -0
  216. package/dist/contract/validator.d.ts +177 -0
  217. package/dist/contract/validator.js +574 -0
  218. package/dist/cost/index.d.ts +6 -0
  219. package/dist/cost/index.js +5 -0
  220. package/dist/cost/tracker.d.ts +134 -0
  221. package/dist/cost/tracker.js +313 -0
  222. package/dist/discovery/discovery.d.ts +16 -0
  223. package/dist/discovery/discovery.js +173 -0
  224. package/dist/discovery/types.d.ts +51 -0
  225. package/dist/discovery/types.js +2 -0
  226. package/dist/docs/agents.d.ts +3 -0
  227. package/dist/docs/agents.js +995 -0
  228. package/dist/docs/contract.d.ts +51 -0
  229. package/dist/docs/contract.js +1681 -0
  230. package/dist/docs/generator.d.ts +4 -0
  231. package/dist/docs/generator.js +4 -0
  232. package/dist/docs/html-reporter.d.ts +9 -0
  233. package/dist/docs/html-reporter.js +757 -0
  234. package/dist/docs/index.d.ts +10 -0
  235. package/dist/docs/index.js +11 -0
  236. package/dist/docs/junit-reporter.d.ts +18 -0
  237. package/dist/docs/junit-reporter.js +210 -0
  238. package/dist/docs/report.d.ts +14 -0
  239. package/dist/docs/report.js +44 -0
  240. package/dist/docs/sarif-reporter.d.ts +19 -0
  241. package/dist/docs/sarif-reporter.js +335 -0
  242. package/dist/docs/shared.d.ts +35 -0
  243. package/dist/docs/shared.js +162 -0
  244. package/dist/docs/templates.d.ts +12 -0
  245. package/dist/docs/templates.js +76 -0
  246. package/dist/errors/index.d.ts +6 -0
  247. package/dist/errors/index.js +6 -0
  248. package/dist/errors/retry.d.ts +92 -0
  249. package/dist/errors/retry.js +323 -0
  250. package/dist/errors/types.d.ts +321 -0
  251. package/dist/errors/types.js +584 -0
  252. package/dist/index.d.ts +32 -0
  253. package/dist/index.js +32 -0
  254. package/dist/interview/dependency-resolver.d.ts +11 -0
  255. package/dist/interview/dependency-resolver.js +32 -0
  256. package/dist/interview/interviewer.d.ts +232 -0
  257. package/dist/interview/interviewer.js +1939 -0
  258. package/dist/interview/mock-response-generator.d.ts +7 -0
  259. package/dist/interview/mock-response-generator.js +102 -0
  260. package/dist/interview/orchestrator.d.ts +237 -0
  261. package/dist/interview/orchestrator.js +1296 -0
  262. package/dist/interview/rate-limiter.d.ts +15 -0
  263. package/dist/interview/rate-limiter.js +55 -0
  264. package/dist/interview/response-validator.d.ts +10 -0
  265. package/dist/interview/response-validator.js +132 -0
  266. package/dist/interview/schema-inferrer.d.ts +8 -0
  267. package/dist/interview/schema-inferrer.js +71 -0
  268. package/dist/interview/schema-test-generator.d.ts +71 -0
  269. package/dist/interview/schema-test-generator.js +834 -0
  270. package/dist/interview/smart-value-generator.d.ts +155 -0
  271. package/dist/interview/smart-value-generator.js +554 -0
  272. package/dist/interview/stateful-test-runner.d.ts +19 -0
  273. package/dist/interview/stateful-test-runner.js +106 -0
  274. package/dist/interview/types.d.ts +561 -0
  275. package/dist/interview/types.js +2 -0
  276. package/dist/llm/anthropic.d.ts +41 -0
  277. package/dist/llm/anthropic.js +355 -0
  278. package/dist/llm/client.d.ts +123 -0
  279. package/dist/llm/client.js +42 -0
  280. package/dist/llm/factory.d.ts +38 -0
  281. package/dist/llm/factory.js +145 -0
  282. package/dist/llm/fallback.d.ts +140 -0
  283. package/dist/llm/fallback.js +379 -0
  284. package/dist/llm/index.d.ts +18 -0
  285. package/dist/llm/index.js +15 -0
  286. package/dist/llm/ollama.d.ts +37 -0
  287. package/dist/llm/ollama.js +330 -0
  288. package/dist/llm/openai.d.ts +25 -0
  289. package/dist/llm/openai.js +320 -0
  290. package/dist/llm/token-budget.d.ts +161 -0
  291. package/dist/llm/token-budget.js +395 -0
  292. package/dist/logging/logger.d.ts +70 -0
  293. package/dist/logging/logger.js +130 -0
  294. package/dist/metrics/collector.d.ts +106 -0
  295. package/dist/metrics/collector.js +547 -0
  296. package/dist/metrics/index.d.ts +7 -0
  297. package/dist/metrics/index.js +7 -0
  298. package/dist/metrics/prometheus.d.ts +20 -0
  299. package/dist/metrics/prometheus.js +241 -0
  300. package/dist/metrics/types.d.ts +209 -0
  301. package/dist/metrics/types.js +5 -0
  302. package/dist/persona/builtins.d.ts +54 -0
  303. package/dist/persona/builtins.js +219 -0
  304. package/dist/persona/index.d.ts +8 -0
  305. package/dist/persona/index.js +8 -0
  306. package/dist/persona/loader.d.ts +30 -0
  307. package/dist/persona/loader.js +190 -0
  308. package/dist/persona/types.d.ts +144 -0
  309. package/dist/persona/types.js +5 -0
  310. package/dist/persona/validation.d.ts +94 -0
  311. package/dist/persona/validation.js +332 -0
  312. package/dist/prompts/index.d.ts +5 -0
  313. package/dist/prompts/index.js +5 -0
  314. package/dist/prompts/templates.d.ts +180 -0
  315. package/dist/prompts/templates.js +431 -0
  316. package/dist/registry/client.d.ts +49 -0
  317. package/dist/registry/client.js +191 -0
  318. package/dist/registry/index.d.ts +7 -0
  319. package/dist/registry/index.js +6 -0
  320. package/dist/registry/types.d.ts +140 -0
  321. package/dist/registry/types.js +6 -0
  322. package/dist/scenarios/evaluator.d.ts +43 -0
  323. package/dist/scenarios/evaluator.js +206 -0
  324. package/dist/scenarios/index.d.ts +10 -0
  325. package/dist/scenarios/index.js +9 -0
  326. package/dist/scenarios/loader.d.ts +20 -0
  327. package/dist/scenarios/loader.js +285 -0
  328. package/dist/scenarios/types.d.ts +153 -0
  329. package/dist/scenarios/types.js +8 -0
  330. package/dist/security/index.d.ts +17 -0
  331. package/dist/security/index.js +18 -0
  332. package/dist/security/payloads.d.ts +61 -0
  333. package/dist/security/payloads.js +268 -0
  334. package/dist/security/security-tester.d.ts +42 -0
  335. package/dist/security/security-tester.js +582 -0
  336. package/dist/security/types.d.ts +166 -0
  337. package/dist/security/types.js +8 -0
  338. package/dist/transport/base-transport.d.ts +59 -0
  339. package/dist/transport/base-transport.js +38 -0
  340. package/dist/transport/http-transport.d.ts +67 -0
  341. package/dist/transport/http-transport.js +238 -0
  342. package/dist/transport/mcp-client.d.ts +141 -0
  343. package/dist/transport/mcp-client.js +496 -0
  344. package/dist/transport/sse-transport.d.ts +88 -0
  345. package/dist/transport/sse-transport.js +316 -0
  346. package/dist/transport/stdio-transport.d.ts +43 -0
  347. package/dist/transport/stdio-transport.js +238 -0
  348. package/dist/transport/types.d.ts +125 -0
  349. package/dist/transport/types.js +16 -0
  350. package/dist/utils/concurrency.d.ts +123 -0
  351. package/dist/utils/concurrency.js +213 -0
  352. package/dist/utils/formatters.d.ts +16 -0
  353. package/dist/utils/formatters.js +37 -0
  354. package/dist/utils/index.d.ts +8 -0
  355. package/dist/utils/index.js +8 -0
  356. package/dist/utils/jsonpath.d.ts +87 -0
  357. package/dist/utils/jsonpath.js +326 -0
  358. package/dist/utils/markdown.d.ts +113 -0
  359. package/dist/utils/markdown.js +265 -0
  360. package/dist/utils/network.d.ts +14 -0
  361. package/dist/utils/network.js +17 -0
  362. package/dist/utils/sanitize.d.ts +92 -0
  363. package/dist/utils/sanitize.js +191 -0
  364. package/dist/utils/semantic.d.ts +194 -0
  365. package/dist/utils/semantic.js +1051 -0
  366. package/dist/utils/smart-truncate.d.ts +94 -0
  367. package/dist/utils/smart-truncate.js +361 -0
  368. package/dist/utils/timeout.d.ts +153 -0
  369. package/dist/utils/timeout.js +205 -0
  370. package/dist/utils/yaml-parser.d.ts +58 -0
  371. package/dist/utils/yaml-parser.js +86 -0
  372. package/dist/validation/index.d.ts +32 -0
  373. package/dist/validation/index.js +32 -0
  374. package/dist/validation/semantic-test-generator.d.ts +50 -0
  375. package/dist/validation/semantic-test-generator.js +176 -0
  376. package/dist/validation/semantic-types.d.ts +66 -0
  377. package/dist/validation/semantic-types.js +94 -0
  378. package/dist/validation/semantic-validator.d.ts +38 -0
  379. package/dist/validation/semantic-validator.js +340 -0
  380. package/dist/verification/index.d.ts +6 -0
  381. package/dist/verification/index.js +5 -0
  382. package/dist/verification/types.d.ts +133 -0
  383. package/dist/verification/types.js +5 -0
  384. package/dist/verification/verifier.d.ts +30 -0
  385. package/dist/verification/verifier.js +309 -0
  386. package/dist/version.d.ts +19 -0
  387. package/dist/version.js +48 -0
  388. package/dist/workflow/auto-generator.d.ts +27 -0
  389. package/dist/workflow/auto-generator.js +513 -0
  390. package/dist/workflow/discovery.d.ts +40 -0
  391. package/dist/workflow/discovery.js +195 -0
  392. package/dist/workflow/executor.d.ts +82 -0
  393. package/dist/workflow/executor.js +611 -0
  394. package/dist/workflow/index.d.ts +10 -0
  395. package/dist/workflow/index.js +10 -0
  396. package/dist/workflow/loader.d.ts +24 -0
  397. package/dist/workflow/loader.js +194 -0
  398. package/dist/workflow/state-tracker.d.ts +98 -0
  399. package/dist/workflow/state-tracker.js +424 -0
  400. package/dist/workflow/types.d.ts +337 -0
  401. package/dist/workflow/types.js +5 -0
  402. package/package.json +94 -0
  403. package/schemas/bellwether-check.schema.json +651 -0
@@ -0,0 +1,757 @@
1
+ /**
2
+ * HTML Report Generator - Creates interactive static HTML reports.
3
+ */
4
+ /**
5
+ * Generate a complete HTML report from interview results.
6
+ */
7
+ export function generateHtmlReport(result) {
8
+ const { discovery, toolProfiles, summary, limitations, recommendations, metadata } = result;
9
+ return `<!DOCTYPE html>
10
+ <html lang="en">
11
+ <head>
12
+ <meta charset="UTF-8">
13
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
14
+ <title>${escapeHtml(discovery.serverInfo.name)} - Bellwether Report</title>
15
+ <style>
16
+ ${getStyles()}
17
+ </style>
18
+ </head>
19
+ <body>
20
+ <div class="container">
21
+ <header class="header">
22
+ <h1>${escapeHtml(discovery.serverInfo.name)}</h1>
23
+ <p class="subtitle">Generated by <a href="https://github.com/dotsetlabs/bellwether">Bellwether</a> on ${formatDate(metadata.startTime)}</p>
24
+ </header>
25
+
26
+ <nav class="nav">
27
+ <a href="#overview" class="nav-link">Overview</a>
28
+ <a href="#tools" class="nav-link">Tools</a>
29
+ ${result.workflowResults && result.workflowResults.length > 0 ? '<a href="#workflows" class="nav-link">Workflows</a>' : ''}
30
+ <a href="#findings" class="nav-link">Findings</a>
31
+ <a href="#security" class="nav-link">Security</a>
32
+ </nav>
33
+
34
+ <main>
35
+ <!-- Overview Section -->
36
+ <section id="overview" class="section">
37
+ <h2>Overview</h2>
38
+ <p class="summary">${escapeHtml(summary)}</p>
39
+
40
+ <div class="stats-grid">
41
+ <div class="stat-card">
42
+ <div class="stat-value">${toolProfiles.length}</div>
43
+ <div class="stat-label">Tools</div>
44
+ </div>
45
+ <div class="stat-card">
46
+ <div class="stat-value">${metadata.toolCallCount}</div>
47
+ <div class="stat-label">Tool Calls</div>
48
+ </div>
49
+ <div class="stat-card">
50
+ <div class="stat-value">${metadata.errorCount}</div>
51
+ <div class="stat-label">Errors</div>
52
+ </div>
53
+ <div class="stat-card">
54
+ <div class="stat-value">${formatDuration(metadata.durationMs)}</div>
55
+ <div class="stat-label">Duration</div>
56
+ </div>
57
+ </div>
58
+
59
+ <div class="info-grid">
60
+ <div class="info-item">
61
+ <span class="info-label">Server Version:</span>
62
+ <span class="info-value">${escapeHtml(discovery.serverInfo.version)}</span>
63
+ </div>
64
+ <div class="info-item">
65
+ <span class="info-label">Protocol Version:</span>
66
+ <span class="info-value">${escapeHtml(discovery.protocolVersion)}</span>
67
+ </div>
68
+ <div class="info-item">
69
+ <span class="info-label">Model:</span>
70
+ <span class="info-value">${escapeHtml(metadata.model ?? 'unknown')}</span>
71
+ </div>
72
+ ${metadata.personas && metadata.personas.length > 0 ? `
73
+ <div class="info-item">
74
+ <span class="info-label">Personas:</span>
75
+ <span class="info-value">${metadata.personas.map(p => escapeHtml(p.name)).join(', ')}</span>
76
+ </div>
77
+ ` : ''}
78
+ </div>
79
+
80
+ <h3>Capabilities</h3>
81
+ <div class="capabilities">
82
+ ${discovery.capabilities.tools ? '<span class="capability enabled">Tools</span>' : '<span class="capability disabled">Tools</span>'}
83
+ ${discovery.capabilities.prompts ? '<span class="capability enabled">Prompts</span>' : '<span class="capability disabled">Prompts</span>'}
84
+ ${discovery.capabilities.resources ? '<span class="capability enabled">Resources</span>' : '<span class="capability disabled">Resources</span>'}
85
+ ${discovery.capabilities.logging ? '<span class="capability enabled">Logging</span>' : '<span class="capability disabled">Logging</span>'}
86
+ </div>
87
+ </section>
88
+
89
+ <!-- Tools Section -->
90
+ <section id="tools" class="section">
91
+ <h2>Tools</h2>
92
+
93
+ <div class="search-bar">
94
+ <input type="text" id="tool-search" placeholder="Search tools..." onkeyup="filterTools()">
95
+ </div>
96
+
97
+ <div id="tools-list" class="tools-grid">
98
+ ${generateToolCards(discovery.tools, toolProfiles)}
99
+ </div>
100
+ </section>
101
+
102
+ <!-- Workflows Section -->
103
+ ${result.workflowResults && result.workflowResults.length > 0 ? generateWorkflowsSection(result) : ''}
104
+
105
+ <!-- Findings Section -->
106
+ <section id="findings" class="section">
107
+ <h2>Findings</h2>
108
+
109
+ <div class="filter-bar">
110
+ <label>
111
+ <input type="checkbox" id="filter-behavior" checked onchange="filterFindings()"> Behavior
112
+ </label>
113
+ <label>
114
+ <input type="checkbox" id="filter-limitations" checked onchange="filterFindings()"> Limitations
115
+ </label>
116
+ <label>
117
+ <input type="checkbox" id="filter-security" checked onchange="filterFindings()"> Security
118
+ </label>
119
+ </div>
120
+
121
+ <table class="findings-table" id="findings-table">
122
+ <thead>
123
+ <tr>
124
+ <th onclick="sortTable(0)">Tool ↕</th>
125
+ <th onclick="sortTable(1)">Category ↕</th>
126
+ <th onclick="sortTable(2)">Finding ↕</th>
127
+ </tr>
128
+ </thead>
129
+ <tbody>
130
+ ${generateFindingsRows(toolProfiles)}
131
+ </tbody>
132
+ </table>
133
+ </section>
134
+
135
+ <!-- Security Section -->
136
+ <section id="security" class="section">
137
+ <h2>Security Considerations</h2>
138
+ ${generateSecuritySection(toolProfiles)}
139
+ </section>
140
+
141
+ <!-- Limitations Section -->
142
+ ${limitations.length > 0 ? `
143
+ <section id="limitations" class="section">
144
+ <h2>Known Limitations</h2>
145
+ <ul class="limitations-list">
146
+ ${limitations.map(l => `<li>${escapeHtml(l)}</li>`).join('\n ')}
147
+ </ul>
148
+ </section>
149
+ ` : ''}
150
+
151
+ <!-- Recommendations Section -->
152
+ ${recommendations.length > 0 ? `
153
+ <section id="recommendations" class="section">
154
+ <h2>Recommendations</h2>
155
+ <ul class="recommendations-list">
156
+ ${recommendations.map(r => `<li>${escapeHtml(r)}</li>`).join('\n ')}
157
+ </ul>
158
+ </section>
159
+ ` : ''}
160
+ </main>
161
+
162
+ <footer class="footer">
163
+ <p>Generated by <a href="https://github.com/dotsetlabs/bellwether">Bellwether</a> - MCP Server Behavioral Profiler</p>
164
+ <p>Interview completed in ${formatDuration(metadata.durationMs)} with ${metadata.toolCallCount} tool calls</p>
165
+ </footer>
166
+ </div>
167
+
168
+ <script>
169
+ ${getScripts()}
170
+ </script>
171
+ </body>
172
+ </html>`;
173
+ }
174
+ /**
175
+ * Generate tool cards HTML.
176
+ */
177
+ function generateToolCards(tools, profiles) {
178
+ return tools.map(tool => {
179
+ const profile = profiles.find(p => p.name === tool.name);
180
+ const hasWarnings = profile && (profile.securityNotes.length > 0 || profile.limitations.length > 0);
181
+ const statusClass = hasWarnings ? 'has-warnings' : 'ok';
182
+ return ` <div class="tool-card ${statusClass}" data-tool="${escapeHtml(tool.name)}">
183
+ <div class="tool-header">
184
+ <h3>${escapeHtml(tool.name)}</h3>
185
+ <span class="tool-status ${statusClass}">${hasWarnings ? '⚠️' : '✓'}</span>
186
+ </div>
187
+ <p class="tool-description">${escapeHtml(tool.description || 'No description')}</p>
188
+ ${profile ? `
189
+ <div class="tool-stats">
190
+ <span class="tool-stat">${profile.behavioralNotes.length} behaviors</span>
191
+ <span class="tool-stat">${profile.limitations.length} limitations</span>
192
+ <span class="tool-stat">${profile.securityNotes.length} security notes</span>
193
+ </div>
194
+ ` : ''}
195
+ <button class="expand-btn" onclick="toggleToolDetails('${escapeHtml(tool.name)}')">Show Details</button>
196
+ <div class="tool-details" id="details-${escapeHtml(tool.name)}" style="display: none;">
197
+ ${tool.inputSchema ? `
198
+ <h4>Input Schema</h4>
199
+ <pre><code>${escapeHtml(JSON.stringify(tool.inputSchema, null, 2))}</code></pre>
200
+ ` : ''}
201
+ ${profile && profile.behavioralNotes.length > 0 ? `
202
+ <h4>Observed Behavior</h4>
203
+ <ul>${profile.behavioralNotes.map(n => `<li>${escapeHtml(n)}</li>`).join('')}</ul>
204
+ ` : ''}
205
+ ${profile && profile.limitations.length > 0 ? `
206
+ <h4>Limitations</h4>
207
+ <ul class="warning-list">${profile.limitations.map(l => `<li>${escapeHtml(l)}</li>`).join('')}</ul>
208
+ ` : ''}
209
+ ${profile && profile.securityNotes.length > 0 ? `
210
+ <h4>Security Notes</h4>
211
+ <ul class="security-list">${profile.securityNotes.map(s => `<li>${escapeHtml(s)}</li>`).join('')}</ul>
212
+ ` : ''}
213
+ </div>
214
+ </div>`;
215
+ }).join('\n');
216
+ }
217
+ /**
218
+ * Generate workflows section HTML.
219
+ */
220
+ function generateWorkflowsSection(result) {
221
+ if (!result.workflowResults || result.workflowResults.length === 0) {
222
+ return '';
223
+ }
224
+ const workflows = result.workflowResults.map(wr => {
225
+ const statusClass = wr.success ? 'success' : 'failure';
226
+ const statusIcon = wr.success ? '✅' : '❌';
227
+ const stepsHtml = wr.steps.map((sr, i) => {
228
+ const stepClass = sr.success ? 'step-success' : 'step-failure';
229
+ return `<div class="workflow-step ${stepClass}">
230
+ <span class="step-number">${i + 1}</span>
231
+ <span class="step-tool">${escapeHtml(sr.step.tool)}</span>
232
+ <span class="step-status">${sr.success ? '✓' : '✗'}</span>
233
+ ${sr.analysis ? `<p class="step-analysis">${escapeHtml(sr.analysis)}</p>` : ''}
234
+ ${sr.error ? `<p class="step-error">Error: ${escapeHtml(sr.error)}</p>` : ''}
235
+ </div>`;
236
+ }).join('\n');
237
+ // Generate mermaid diagram
238
+ const mermaidDiagram = generateMermaidDiagram(wr);
239
+ return `<div class="workflow-card ${statusClass}">
240
+ <div class="workflow-header">
241
+ <h3>${statusIcon} ${escapeHtml(wr.workflow.name)}</h3>
242
+ </div>
243
+ <p class="workflow-description">${escapeHtml(wr.workflow.description)}</p>
244
+ ${wr.summary ? `<blockquote class="workflow-summary">${escapeHtml(wr.summary)}</blockquote>` : ''}
245
+
246
+ <div class="workflow-diagram">
247
+ <pre class="mermaid">${mermaidDiagram}</pre>
248
+ </div>
249
+
250
+ <div class="workflow-steps">
251
+ <h4>Steps</h4>
252
+ ${stepsHtml}
253
+ </div>
254
+ </div>`;
255
+ }).join('\n');
256
+ return ` <section id="workflows" class="section">
257
+ <h2>Workflows</h2>
258
+ <div class="workflows-grid">
259
+ ${workflows}
260
+ </div>
261
+ </section>`;
262
+ }
263
+ /**
264
+ * Generate mermaid diagram for workflow.
265
+ */
266
+ function generateMermaidDiagram(wr) {
267
+ if (!wr)
268
+ return '';
269
+ let diagram = 'flowchart LR\n';
270
+ for (let i = 0; i < wr.steps.length; i++) {
271
+ const step = wr.steps[i];
272
+ const nodeClass = step.success ? ':::success' : ':::failure';
273
+ diagram += ` S${i}["${step.step.tool}"]${nodeClass}\n`;
274
+ if (i < wr.steps.length - 1) {
275
+ diagram += ` S${i} --> S${i + 1}\n`;
276
+ }
277
+ }
278
+ diagram += ' classDef success fill:#90EE90\n';
279
+ diagram += ' classDef failure fill:#FFB6C1\n';
280
+ return diagram;
281
+ }
282
+ /**
283
+ * Generate findings table rows.
284
+ */
285
+ function generateFindingsRows(profiles) {
286
+ const rows = [];
287
+ for (const profile of profiles) {
288
+ for (const note of profile.behavioralNotes) {
289
+ rows.push(` <tr class="finding-behavior">
290
+ <td>${escapeHtml(profile.name)}</td>
291
+ <td><span class="badge badge-behavior">Behavior</span></td>
292
+ <td>${escapeHtml(note)}</td>
293
+ </tr>`);
294
+ }
295
+ for (const limitation of profile.limitations) {
296
+ rows.push(` <tr class="finding-limitations">
297
+ <td>${escapeHtml(profile.name)}</td>
298
+ <td><span class="badge badge-limitation">Limitation</span></td>
299
+ <td>${escapeHtml(limitation)}</td>
300
+ </tr>`);
301
+ }
302
+ for (const secNote of profile.securityNotes) {
303
+ rows.push(` <tr class="finding-security">
304
+ <td>${escapeHtml(profile.name)}</td>
305
+ <td><span class="badge badge-security">Security</span></td>
306
+ <td>${escapeHtml(secNote)}</td>
307
+ </tr>`);
308
+ }
309
+ }
310
+ return rows.join('\n');
311
+ }
312
+ /**
313
+ * Generate security section HTML.
314
+ */
315
+ function generateSecuritySection(profiles) {
316
+ const findings = [];
317
+ for (const profile of profiles) {
318
+ for (const note of profile.securityNotes) {
319
+ const severity = classifySeverity(note);
320
+ findings.push({ tool: profile.name, note, severity });
321
+ }
322
+ }
323
+ if (findings.length === 0) {
324
+ return '<p class="no-findings">No security considerations identified.</p>';
325
+ }
326
+ const critical = findings.filter(f => f.severity === 'critical');
327
+ const warnings = findings.filter(f => f.severity === 'warning');
328
+ const info = findings.filter(f => f.severity === 'info');
329
+ let html = '';
330
+ if (critical.length > 0) {
331
+ html += ` <div class="security-group critical">
332
+ <h3>Critical Issues</h3>
333
+ <ul>
334
+ ${critical.map(f => `<li><strong>${escapeHtml(f.tool)}:</strong> ${escapeHtml(f.note)}</li>`).join('\n ')}
335
+ </ul>
336
+ </div>\n`;
337
+ }
338
+ if (warnings.length > 0) {
339
+ html += ` <div class="security-group warning">
340
+ <h3>Warnings</h3>
341
+ <ul>
342
+ ${warnings.map(f => `<li><strong>${escapeHtml(f.tool)}:</strong> ${escapeHtml(f.note)}</li>`).join('\n ')}
343
+ </ul>
344
+ </div>\n`;
345
+ }
346
+ if (info.length > 0) {
347
+ html += ` <div class="security-group info">
348
+ <h3>Informational</h3>
349
+ <ul>
350
+ ${info.map(f => `<li><strong>${escapeHtml(f.tool)}:</strong> ${escapeHtml(f.note)}</li>`).join('\n ')}
351
+ </ul>
352
+ </div>\n`;
353
+ }
354
+ return html;
355
+ }
356
+ /**
357
+ * Classify security severity.
358
+ */
359
+ function classifySeverity(note) {
360
+ const lower = note.toLowerCase();
361
+ if (['injection', 'rce', 'remote code', 'arbitrary code', 'command execution'].some(kw => lower.includes(kw))) {
362
+ return 'critical';
363
+ }
364
+ if (['risk', 'vulnerab', 'dangerous', 'unsafe', 'leak', 'exposure'].some(kw => lower.includes(kw))) {
365
+ return 'warning';
366
+ }
367
+ return 'info';
368
+ }
369
+ /**
370
+ * Get CSS styles for the HTML report.
371
+ */
372
+ function getStyles() {
373
+ return `
374
+ :root {
375
+ --primary: #6366f1;
376
+ --primary-dark: #4f46e5;
377
+ --success: #22c55e;
378
+ --warning: #f59e0b;
379
+ --danger: #ef4444;
380
+ --gray-50: #f9fafb;
381
+ --gray-100: #f3f4f6;
382
+ --gray-200: #e5e7eb;
383
+ --gray-300: #d1d5db;
384
+ --gray-600: #4b5563;
385
+ --gray-800: #1f2937;
386
+ --gray-900: #111827;
387
+ }
388
+
389
+ * { box-sizing: border-box; margin: 0; padding: 0; }
390
+
391
+ body {
392
+ font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
393
+ background: var(--gray-50);
394
+ color: var(--gray-800);
395
+ line-height: 1.6;
396
+ }
397
+
398
+ .container { max-width: 1200px; margin: 0 auto; padding: 2rem; }
399
+
400
+ .header {
401
+ text-align: center;
402
+ margin-bottom: 2rem;
403
+ padding-bottom: 2rem;
404
+ border-bottom: 1px solid var(--gray-200);
405
+ }
406
+
407
+ .header h1 { font-size: 2.5rem; color: var(--gray-900); }
408
+ .subtitle { color: var(--gray-600); margin-top: 0.5rem; }
409
+ .subtitle a { color: var(--primary); }
410
+
411
+ .nav {
412
+ display: flex;
413
+ gap: 1rem;
414
+ justify-content: center;
415
+ margin-bottom: 2rem;
416
+ flex-wrap: wrap;
417
+ }
418
+
419
+ .nav-link {
420
+ padding: 0.5rem 1rem;
421
+ background: white;
422
+ border: 1px solid var(--gray-200);
423
+ border-radius: 0.5rem;
424
+ text-decoration: none;
425
+ color: var(--gray-800);
426
+ transition: all 0.2s;
427
+ }
428
+
429
+ .nav-link:hover { background: var(--primary); color: white; border-color: var(--primary); }
430
+
431
+ .section {
432
+ background: white;
433
+ border-radius: 1rem;
434
+ padding: 2rem;
435
+ margin-bottom: 2rem;
436
+ box-shadow: 0 1px 3px rgba(0,0,0,0.1);
437
+ }
438
+
439
+ .section h2 {
440
+ font-size: 1.5rem;
441
+ margin-bottom: 1.5rem;
442
+ padding-bottom: 0.5rem;
443
+ border-bottom: 2px solid var(--primary);
444
+ }
445
+
446
+ .summary { font-size: 1.1rem; color: var(--gray-600); margin-bottom: 1.5rem; }
447
+
448
+ .stats-grid {
449
+ display: grid;
450
+ grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
451
+ gap: 1rem;
452
+ margin-bottom: 2rem;
453
+ }
454
+
455
+ .stat-card {
456
+ background: var(--gray-50);
457
+ padding: 1.5rem;
458
+ border-radius: 0.5rem;
459
+ text-align: center;
460
+ }
461
+
462
+ .stat-value { font-size: 2rem; font-weight: bold; color: var(--primary); }
463
+ .stat-label { color: var(--gray-600); font-size: 0.875rem; }
464
+
465
+ .info-grid {
466
+ display: grid;
467
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
468
+ gap: 0.5rem;
469
+ margin-bottom: 1.5rem;
470
+ }
471
+
472
+ .info-item { padding: 0.5rem; background: var(--gray-50); border-radius: 0.25rem; }
473
+ .info-label { font-weight: 600; color: var(--gray-600); }
474
+ .info-value { color: var(--gray-800); }
475
+
476
+ .capabilities { display: flex; gap: 0.5rem; flex-wrap: wrap; }
477
+
478
+ .capability {
479
+ padding: 0.25rem 0.75rem;
480
+ border-radius: 9999px;
481
+ font-size: 0.875rem;
482
+ }
483
+
484
+ .capability.enabled { background: #dcfce7; color: #166534; }
485
+ .capability.disabled { background: var(--gray-100); color: var(--gray-600); }
486
+
487
+ .search-bar { margin-bottom: 1.5rem; }
488
+
489
+ .search-bar input {
490
+ width: 100%;
491
+ padding: 0.75rem 1rem;
492
+ border: 1px solid var(--gray-200);
493
+ border-radius: 0.5rem;
494
+ font-size: 1rem;
495
+ }
496
+
497
+ .tools-grid {
498
+ display: grid;
499
+ grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
500
+ gap: 1rem;
501
+ }
502
+
503
+ .tool-card {
504
+ background: var(--gray-50);
505
+ border: 1px solid var(--gray-200);
506
+ border-radius: 0.75rem;
507
+ padding: 1.5rem;
508
+ transition: all 0.2s;
509
+ }
510
+
511
+ .tool-card:hover { box-shadow: 0 4px 6px rgba(0,0,0,0.1); }
512
+ .tool-card.has-warnings { border-left: 4px solid var(--warning); }
513
+ .tool-card.ok { border-left: 4px solid var(--success); }
514
+
515
+ .tool-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 0.5rem; }
516
+ .tool-header h3 { font-size: 1.125rem; color: var(--gray-900); }
517
+ .tool-status.has-warnings { color: var(--warning); }
518
+ .tool-status.ok { color: var(--success); }
519
+
520
+ .tool-description { color: var(--gray-600); font-size: 0.875rem; margin-bottom: 1rem; }
521
+
522
+ .tool-stats { display: flex; gap: 1rem; margin-bottom: 1rem; flex-wrap: wrap; }
523
+ .tool-stat { font-size: 0.75rem; color: var(--gray-600); }
524
+
525
+ .expand-btn {
526
+ background: var(--primary);
527
+ color: white;
528
+ border: none;
529
+ padding: 0.5rem 1rem;
530
+ border-radius: 0.375rem;
531
+ cursor: pointer;
532
+ font-size: 0.875rem;
533
+ }
534
+
535
+ .expand-btn:hover { background: var(--primary-dark); }
536
+
537
+ .tool-details {
538
+ margin-top: 1rem;
539
+ padding-top: 1rem;
540
+ border-top: 1px solid var(--gray-200);
541
+ }
542
+
543
+ .tool-details h4 { font-size: 0.875rem; color: var(--gray-600); margin: 1rem 0 0.5rem; }
544
+ .tool-details pre { background: var(--gray-800); color: #e5e7eb; padding: 1rem; border-radius: 0.5rem; overflow-x: auto; font-size: 0.75rem; }
545
+ .tool-details ul { list-style: disc; margin-left: 1.5rem; }
546
+ .tool-details li { margin-bottom: 0.25rem; font-size: 0.875rem; }
547
+ .warning-list li { color: var(--warning); }
548
+ .security-list li { color: var(--danger); }
549
+
550
+ .workflows-grid { display: flex; flex-direction: column; gap: 1.5rem; }
551
+
552
+ .workflow-card {
553
+ background: var(--gray-50);
554
+ border: 1px solid var(--gray-200);
555
+ border-radius: 0.75rem;
556
+ padding: 1.5rem;
557
+ }
558
+
559
+ .workflow-card.success { border-left: 4px solid var(--success); }
560
+ .workflow-card.failure { border-left: 4px solid var(--danger); }
561
+
562
+ .workflow-header h3 { font-size: 1.25rem; margin-bottom: 0.5rem; }
563
+ .workflow-description { color: var(--gray-600); margin-bottom: 1rem; }
564
+ .workflow-summary { font-style: italic; border-left: 3px solid var(--primary); padding-left: 1rem; margin-bottom: 1rem; }
565
+
566
+ .workflow-diagram { background: white; padding: 1rem; border-radius: 0.5rem; margin-bottom: 1rem; overflow-x: auto; }
567
+
568
+ .workflow-steps h4 { margin-bottom: 0.5rem; }
569
+
570
+ .workflow-step {
571
+ display: flex;
572
+ align-items: center;
573
+ gap: 1rem;
574
+ padding: 0.75rem;
575
+ margin-bottom: 0.5rem;
576
+ background: white;
577
+ border-radius: 0.375rem;
578
+ flex-wrap: wrap;
579
+ }
580
+
581
+ .workflow-step.step-success { border-left: 3px solid var(--success); }
582
+ .workflow-step.step-failure { border-left: 3px solid var(--danger); }
583
+
584
+ .step-number { font-weight: bold; color: var(--gray-600); }
585
+ .step-tool { font-weight: 600; }
586
+ .step-status { margin-left: auto; }
587
+ .step-analysis { width: 100%; font-size: 0.875rem; color: var(--gray-600); margin-top: 0.5rem; }
588
+ .step-error { width: 100%; font-size: 0.875rem; color: var(--danger); margin-top: 0.5rem; }
589
+
590
+ .filter-bar { margin-bottom: 1rem; display: flex; gap: 1rem; flex-wrap: wrap; }
591
+ .filter-bar label { display: flex; align-items: center; gap: 0.25rem; cursor: pointer; }
592
+
593
+ .findings-table {
594
+ width: 100%;
595
+ border-collapse: collapse;
596
+ font-size: 0.875rem;
597
+ }
598
+
599
+ .findings-table th, .findings-table td {
600
+ padding: 0.75rem;
601
+ text-align: left;
602
+ border-bottom: 1px solid var(--gray-200);
603
+ }
604
+
605
+ .findings-table th {
606
+ background: var(--gray-50);
607
+ font-weight: 600;
608
+ cursor: pointer;
609
+ }
610
+
611
+ .findings-table th:hover { background: var(--gray-100); }
612
+
613
+ .badge {
614
+ padding: 0.25rem 0.5rem;
615
+ border-radius: 9999px;
616
+ font-size: 0.75rem;
617
+ font-weight: 500;
618
+ }
619
+
620
+ .badge-behavior { background: #dbeafe; color: #1e40af; }
621
+ .badge-limitation { background: #fef3c7; color: #92400e; }
622
+ .badge-security { background: #fee2e2; color: #991b1b; }
623
+
624
+ .security-group { padding: 1rem; border-radius: 0.5rem; margin-bottom: 1rem; }
625
+ .security-group.critical { background: #fee2e2; }
626
+ .security-group.warning { background: #fef3c7; }
627
+ .security-group.info { background: #dbeafe; }
628
+ .security-group h3 { margin-bottom: 0.5rem; }
629
+ .security-group ul { list-style: disc; margin-left: 1.5rem; }
630
+ .security-group li { margin-bottom: 0.25rem; }
631
+
632
+ .no-findings { color: var(--success); font-style: italic; }
633
+
634
+ .limitations-list, .recommendations-list { list-style: disc; margin-left: 1.5rem; }
635
+ .limitations-list li, .recommendations-list li { margin-bottom: 0.5rem; }
636
+
637
+ .footer {
638
+ text-align: center;
639
+ padding: 2rem;
640
+ color: var(--gray-600);
641
+ font-size: 0.875rem;
642
+ }
643
+
644
+ .footer a { color: var(--primary); }
645
+
646
+ @media (max-width: 640px) {
647
+ .container { padding: 1rem; }
648
+ .header h1 { font-size: 1.75rem; }
649
+ .tools-grid { grid-template-columns: 1fr; }
650
+ }
651
+ `;
652
+ }
653
+ /**
654
+ * Get JavaScript for interactive features.
655
+ */
656
+ function getScripts() {
657
+ return `
658
+ function filterTools() {
659
+ const search = document.getElementById('tool-search').value.toLowerCase();
660
+ const cards = document.querySelectorAll('.tool-card');
661
+
662
+ cards.forEach(card => {
663
+ const name = card.getAttribute('data-tool').toLowerCase();
664
+ const description = card.querySelector('.tool-description').textContent.toLowerCase();
665
+ card.style.display = (name.includes(search) || description.includes(search)) ? '' : 'none';
666
+ });
667
+ }
668
+
669
+ function toggleToolDetails(toolName) {
670
+ const details = document.getElementById('details-' + toolName);
671
+ const btn = details.previousElementSibling;
672
+
673
+ if (details.style.display === 'none') {
674
+ details.style.display = 'block';
675
+ btn.textContent = 'Hide Details';
676
+ } else {
677
+ details.style.display = 'none';
678
+ btn.textContent = 'Show Details';
679
+ }
680
+ }
681
+
682
+ function filterFindings() {
683
+ const showBehavior = document.getElementById('filter-behavior').checked;
684
+ const showLimitations = document.getElementById('filter-limitations').checked;
685
+ const showSecurity = document.getElementById('filter-security').checked;
686
+
687
+ document.querySelectorAll('.findings-table tbody tr').forEach(row => {
688
+ const classes = row.className;
689
+ let show = false;
690
+
691
+ if (classes.includes('finding-behavior') && showBehavior) show = true;
692
+ if (classes.includes('finding-limitations') && showLimitations) show = true;
693
+ if (classes.includes('finding-security') && showSecurity) show = true;
694
+
695
+ row.style.display = show ? '' : 'none';
696
+ });
697
+ }
698
+
699
+ function sortTable(columnIndex) {
700
+ const table = document.getElementById('findings-table');
701
+ const tbody = table.querySelector('tbody');
702
+ const rows = Array.from(tbody.querySelectorAll('tr'));
703
+
704
+ const sorted = rows.sort((a, b) => {
705
+ const aText = a.cells[columnIndex].textContent;
706
+ const bText = b.cells[columnIndex].textContent;
707
+ return aText.localeCompare(bText);
708
+ });
709
+
710
+ // Check if already sorted ascending
711
+ const isAscending = tbody.getAttribute('data-sort') === columnIndex + '-asc';
712
+ if (isAscending) {
713
+ sorted.reverse();
714
+ tbody.setAttribute('data-sort', columnIndex + '-desc');
715
+ } else {
716
+ tbody.setAttribute('data-sort', columnIndex + '-asc');
717
+ }
718
+
719
+ sorted.forEach(row => tbody.appendChild(row));
720
+ }
721
+
722
+ // Initialize mermaid if available
723
+ if (typeof mermaid !== 'undefined') {
724
+ mermaid.initialize({ startOnLoad: true, theme: 'neutral' });
725
+ }
726
+ `;
727
+ }
728
+ /**
729
+ * Escape HTML special characters.
730
+ */
731
+ function escapeHtml(str) {
732
+ return str
733
+ .replace(/&/g, '&amp;')
734
+ .replace(/</g, '&lt;')
735
+ .replace(/>/g, '&gt;')
736
+ .replace(/"/g, '&quot;')
737
+ .replace(/'/g, '&#039;');
738
+ }
739
+ /**
740
+ * Format date for display.
741
+ */
742
+ function formatDate(date) {
743
+ return date.toISOString().split('T')[0];
744
+ }
745
+ /**
746
+ * Format duration for display.
747
+ */
748
+ function formatDuration(ms) {
749
+ if (ms < 1000)
750
+ return `${ms}ms`;
751
+ if (ms < 60000)
752
+ return `${(ms / 1000).toFixed(1)}s`;
753
+ const minutes = Math.floor(ms / 60000);
754
+ const seconds = Math.floor((ms % 60000) / 1000);
755
+ return `${minutes}m ${seconds}s`;
756
+ }
757
+ //# sourceMappingURL=html-reporter.js.map