@biggora/claude-plugins 1.2.0 → 1.3.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 (265) hide show
  1. package/README.md +13 -4
  2. package/package.json +1 -1
  3. package/registry/registry.json +334 -244
  4. package/specs/coding.md +30 -0
  5. package/specs/pod.md +2 -0
  6. package/src/commands/skills/add.js +63 -7
  7. package/src/commands/skills/list.js +23 -52
  8. package/src/commands/skills/remove.js +26 -27
  9. package/src/commands/skills/resolve.js +155 -0
  10. package/src/commands/skills/update.js +58 -74
  11. package/src/skills/captcha/README.md +221 -0
  12. package/src/skills/captcha/SKILL.md +355 -0
  13. package/src/skills/captcha/references/captcha-types.md +254 -0
  14. package/src/skills/captcha/references/services.md +172 -0
  15. package/src/skills/captcha/references/stealth.md +238 -0
  16. package/src/skills/captcha/scripts/solve_captcha.py +323 -0
  17. package/src/skills/captcha/scripts/solve_image_grid.py +350 -0
  18. package/src/skills/google-merchant-api/SKILL.md +581 -0
  19. package/src/skills/google-merchant-api/references/accounts.md +247 -0
  20. package/src/skills/google-merchant-api/references/content-api-legacy.md +216 -0
  21. package/src/skills/google-merchant-api/references/datasources.md +233 -0
  22. package/src/skills/google-merchant-api/references/inventories.md +201 -0
  23. package/src/skills/google-merchant-api/references/migration.md +267 -0
  24. package/src/skills/google-merchant-api/references/products.md +316 -0
  25. package/src/skills/google-merchant-api/references/promotions.md +201 -0
  26. package/src/skills/google-merchant-api/references/reports.md +240 -0
  27. package/src/skills/lv-aggregators-api/SKILL.md +113 -0
  28. package/src/skills/lv-aggregators-api/references/integration-guide.md +368 -0
  29. package/src/skills/lv-aggregators-api/references/kurpirkt.md +103 -0
  30. package/src/skills/lv-aggregators-api/references/salidzini.md +122 -0
  31. package/src/skills/nest-best-practices/SKILL.md +251 -0
  32. package/src/skills/nest-best-practices/references/best-practices-request-lifecycle.md +158 -0
  33. package/src/skills/nest-best-practices/references/cli-monorepo.md +106 -0
  34. package/src/skills/nest-best-practices/references/cli-overview.md +157 -0
  35. package/src/skills/nest-best-practices/references/core-controllers.md +165 -0
  36. package/src/skills/nest-best-practices/references/core-dependency-injection.md +179 -0
  37. package/src/skills/nest-best-practices/references/core-middleware.md +139 -0
  38. package/src/skills/nest-best-practices/references/core-modules.md +138 -0
  39. package/src/skills/nest-best-practices/references/core-providers.md +188 -0
  40. package/src/skills/nest-best-practices/references/faq-raw-body-hybrid.md +122 -0
  41. package/src/skills/nest-best-practices/references/fundamentals-circular-dependency.md +89 -0
  42. package/src/skills/nest-best-practices/references/fundamentals-custom-decorators.md +107 -0
  43. package/src/skills/nest-best-practices/references/fundamentals-dynamic-modules.md +125 -0
  44. package/src/skills/nest-best-practices/references/fundamentals-exception-filters.md +202 -0
  45. package/src/skills/nest-best-practices/references/fundamentals-execution-context.md +107 -0
  46. package/src/skills/nest-best-practices/references/fundamentals-guards.md +136 -0
  47. package/src/skills/nest-best-practices/references/fundamentals-interceptors.md +187 -0
  48. package/src/skills/nest-best-practices/references/fundamentals-lazy-loading.md +89 -0
  49. package/src/skills/nest-best-practices/references/fundamentals-lifecycle-events.md +87 -0
  50. package/src/skills/nest-best-practices/references/fundamentals-module-reference.md +107 -0
  51. package/src/skills/nest-best-practices/references/fundamentals-pipes.md +197 -0
  52. package/src/skills/nest-best-practices/references/fundamentals-provider-scopes.md +92 -0
  53. package/src/skills/nest-best-practices/references/fundamentals-testing.md +142 -0
  54. package/src/skills/nest-best-practices/references/graphql-overview.md +233 -0
  55. package/src/skills/nest-best-practices/references/graphql-resolvers-mutations.md +199 -0
  56. package/src/skills/nest-best-practices/references/graphql-scalars-unions-enums.md +180 -0
  57. package/src/skills/nest-best-practices/references/graphql-subscriptions.md +228 -0
  58. package/src/skills/nest-best-practices/references/microservices-grpc.md +175 -0
  59. package/src/skills/nest-best-practices/references/microservices-overview.md +221 -0
  60. package/src/skills/nest-best-practices/references/microservices-transports.md +119 -0
  61. package/src/skills/nest-best-practices/references/openapi-swagger.md +207 -0
  62. package/src/skills/nest-best-practices/references/recipes-authentication.md +97 -0
  63. package/src/skills/nest-best-practices/references/recipes-cqrs.md +176 -0
  64. package/src/skills/nest-best-practices/references/recipes-crud-generator.md +87 -0
  65. package/src/skills/nest-best-practices/references/recipes-documentation.md +93 -0
  66. package/src/skills/nest-best-practices/references/recipes-mongoose.md +153 -0
  67. package/src/skills/nest-best-practices/references/recipes-prisma.md +98 -0
  68. package/src/skills/nest-best-practices/references/recipes-terminus.md +148 -0
  69. package/src/skills/nest-best-practices/references/recipes-typeorm.md +122 -0
  70. package/src/skills/nest-best-practices/references/security-authorization.md +196 -0
  71. package/src/skills/nest-best-practices/references/security-cors-helmet-rate-limiting.md +204 -0
  72. package/src/skills/nest-best-practices/references/security-encryption-hashing.md +93 -0
  73. package/src/skills/nest-best-practices/references/techniques-caching.md +142 -0
  74. package/src/skills/nest-best-practices/references/techniques-compression-streaming-sse.md +194 -0
  75. package/src/skills/nest-best-practices/references/techniques-configuration.md +132 -0
  76. package/src/skills/nest-best-practices/references/techniques-database.md +153 -0
  77. package/src/skills/nest-best-practices/references/techniques-events.md +163 -0
  78. package/src/skills/nest-best-practices/references/techniques-fastify.md +137 -0
  79. package/src/skills/nest-best-practices/references/techniques-file-upload.md +140 -0
  80. package/src/skills/nest-best-practices/references/techniques-http-module.md +176 -0
  81. package/src/skills/nest-best-practices/references/techniques-logging.md +146 -0
  82. package/src/skills/nest-best-practices/references/techniques-mvc-serve-static.md +132 -0
  83. package/src/skills/nest-best-practices/references/techniques-queues.md +162 -0
  84. package/src/skills/nest-best-practices/references/techniques-serialization.md +158 -0
  85. package/src/skills/nest-best-practices/references/techniques-sessions-cookies.md +167 -0
  86. package/src/skills/nest-best-practices/references/techniques-task-scheduling.md +166 -0
  87. package/src/skills/nest-best-practices/references/techniques-validation.md +126 -0
  88. package/src/skills/nest-best-practices/references/techniques-versioning.md +153 -0
  89. package/src/skills/nest-best-practices/references/websockets-advanced.md +96 -0
  90. package/src/skills/nest-best-practices/references/websockets-gateways.md +215 -0
  91. package/src/skills/tailwindcss-best-practices/SKILL.md +180 -0
  92. package/src/skills/tailwindcss-best-practices/references/best-practices-utility-patterns.md +87 -0
  93. package/src/skills/tailwindcss-best-practices/references/core-installation.md +109 -0
  94. package/src/skills/tailwindcss-best-practices/references/core-preflight.md +200 -0
  95. package/src/skills/tailwindcss-best-practices/references/core-responsive.md +163 -0
  96. package/src/skills/tailwindcss-best-practices/references/core-source-detection.md +114 -0
  97. package/src/skills/tailwindcss-best-practices/references/core-theme.md +108 -0
  98. package/src/skills/tailwindcss-best-practices/references/core-utility-classes.md +59 -0
  99. package/src/skills/tailwindcss-best-practices/references/core-variants.md +204 -0
  100. package/src/skills/tailwindcss-best-practices/references/effects-form-controls.md +76 -0
  101. package/src/skills/tailwindcss-best-practices/references/effects-mask.md +91 -0
  102. package/src/skills/tailwindcss-best-practices/references/effects-scroll-snap.md +59 -0
  103. package/src/skills/tailwindcss-best-practices/references/effects-text-shadow.md +78 -0
  104. package/src/skills/tailwindcss-best-practices/references/effects-transition-animation.md +80 -0
  105. package/src/skills/tailwindcss-best-practices/references/effects-visibility-interactivity.md +82 -0
  106. package/src/skills/tailwindcss-best-practices/references/features-content-detection.md +175 -0
  107. package/src/skills/tailwindcss-best-practices/references/features-custom-styles.md +203 -0
  108. package/src/skills/tailwindcss-best-practices/references/features-dark-mode.md +137 -0
  109. package/src/skills/tailwindcss-best-practices/references/features-functions-directives.md +241 -0
  110. package/src/skills/tailwindcss-best-practices/references/features-upgrade.md +160 -0
  111. package/src/skills/tailwindcss-best-practices/references/layout-aspect-ratio.md +39 -0
  112. package/src/skills/tailwindcss-best-practices/references/layout-columns.md +80 -0
  113. package/src/skills/tailwindcss-best-practices/references/layout-display.md +110 -0
  114. package/src/skills/tailwindcss-best-practices/references/layout-flexbox.md +112 -0
  115. package/src/skills/tailwindcss-best-practices/references/layout-grid.md +87 -0
  116. package/src/skills/tailwindcss-best-practices/references/layout-height.md +97 -0
  117. package/src/skills/tailwindcss-best-practices/references/layout-inset.md +103 -0
  118. package/src/skills/tailwindcss-best-practices/references/layout-logical-properties.md +92 -0
  119. package/src/skills/tailwindcss-best-practices/references/layout-margin.md +126 -0
  120. package/src/skills/tailwindcss-best-practices/references/layout-min-max-sizing.md +63 -0
  121. package/src/skills/tailwindcss-best-practices/references/layout-object-fit-position.md +64 -0
  122. package/src/skills/tailwindcss-best-practices/references/layout-overflow.md +57 -0
  123. package/src/skills/tailwindcss-best-practices/references/layout-padding.md +77 -0
  124. package/src/skills/tailwindcss-best-practices/references/layout-position.md +85 -0
  125. package/src/skills/tailwindcss-best-practices/references/layout-tables.md +67 -0
  126. package/src/skills/tailwindcss-best-practices/references/layout-width.md +102 -0
  127. package/src/skills/tailwindcss-best-practices/references/transform-base.md +68 -0
  128. package/src/skills/tailwindcss-best-practices/references/transform-rotate.md +70 -0
  129. package/src/skills/tailwindcss-best-practices/references/transform-scale.md +83 -0
  130. package/src/skills/tailwindcss-best-practices/references/transform-skew.md +62 -0
  131. package/src/skills/tailwindcss-best-practices/references/transform-translate.md +77 -0
  132. package/src/skills/tailwindcss-best-practices/references/typography-font-text.md +142 -0
  133. package/src/skills/tailwindcss-best-practices/references/typography-list-style.md +65 -0
  134. package/src/skills/tailwindcss-best-practices/references/typography-text-align.md +60 -0
  135. package/src/skills/tailwindcss-best-practices/references/visual-background.md +76 -0
  136. package/src/skills/tailwindcss-best-practices/references/visual-border.md +108 -0
  137. package/src/skills/tailwindcss-best-practices/references/visual-effects.md +111 -0
  138. package/src/skills/tailwindcss-best-practices/references/visual-svg.md +82 -0
  139. package/src/skills/test-mobile-app/SKILL.md +11 -6
  140. package/src/skills/test-mobile-app/scripts/analyze_apk.py +15 -4
  141. package/src/skills/test-mobile-app/scripts/check_environment.py +5 -5
  142. package/src/skills/test-mobile-app/scripts/run_tests.py +1 -1
  143. package/src/skills/test-web-ui/SKILL.md +264 -84
  144. package/src/skills/test-web-ui/scripts/discover.py +25 -12
  145. package/src/skills/test-web-ui/scripts/run_tests.py +3 -2
  146. package/src/skills/typescript-expert/SKILL.md +145 -0
  147. package/src/skills/typescript-expert/commands/typescript-fix.md +65 -0
  148. package/src/skills/typescript-expert/references/advanced-conditional-types.md +190 -0
  149. package/src/skills/typescript-expert/references/advanced-decorators.md +243 -0
  150. package/src/skills/typescript-expert/references/advanced-mapped-types.md +223 -0
  151. package/src/skills/typescript-expert/references/advanced-template-literals.md +209 -0
  152. package/src/skills/typescript-expert/references/advanced-type-guards.md +308 -0
  153. package/src/skills/typescript-expert/references/best-practices-patterns.md +313 -0
  154. package/src/skills/typescript-expert/references/best-practices-performance.md +185 -0
  155. package/src/skills/typescript-expert/references/best-practices-tsconfig.md +242 -0
  156. package/src/skills/typescript-expert/references/core-generics.md +246 -0
  157. package/src/skills/typescript-expert/references/core-interfaces-types.md +231 -0
  158. package/src/skills/typescript-expert/references/core-type-system.md +261 -0
  159. package/src/skills/typescript-expert/references/core-utility-types.md +235 -0
  160. package/src/skills/typescript-expert/references/features-ts5x.md +370 -0
  161. package/src/skills/vite-best-practices/SKILL.md +115 -0
  162. package/src/skills/vite-best-practices/references/build-and-ssr.md +255 -0
  163. package/src/skills/vite-best-practices/references/core-config.md +231 -0
  164. package/src/skills/vite-best-practices/references/core-features.md +222 -0
  165. package/src/skills/vite-best-practices/references/core-plugin-api.md +294 -0
  166. package/src/skills/vite-best-practices/references/environment-api.md +108 -0
  167. package/src/skills/vite-best-practices/references/rolldown-migration.md +242 -0
  168. package/codex-cli-workspace/iteration-1/benchmark.json +0 -122
  169. package/codex-cli-workspace/iteration-1/eval-1-ci-integration/eval_metadata.json +0 -13
  170. package/codex-cli-workspace/iteration-1/eval-1-ci-integration/with_skill/grading.json +0 -52
  171. package/codex-cli-workspace/iteration-1/eval-1-ci-integration/with_skill/outputs/response.md +0 -163
  172. package/codex-cli-workspace/iteration-1/eval-1-ci-integration/with_skill/timing.json +0 -5
  173. package/codex-cli-workspace/iteration-1/eval-1-ci-integration/without_skill/grading.json +0 -58
  174. package/codex-cli-workspace/iteration-1/eval-1-ci-integration/without_skill/outputs/response.md +0 -151
  175. package/codex-cli-workspace/iteration-1/eval-1-ci-integration/without_skill/timing.json +0 -5
  176. package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/eval_metadata.json +0 -13
  177. package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/grading.json +0 -52
  178. package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/outputs/response.md +0 -86
  179. package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/timing.json +0 -5
  180. package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/grading.json +0 -58
  181. package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/outputs/response.md +0 -164
  182. package/codex-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/timing.json +0 -5
  183. package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/eval_metadata.json +0 -13
  184. package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/with_skill/grading.json +0 -52
  185. package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/with_skill/outputs/response.md +0 -130
  186. package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/with_skill/timing.json +0 -5
  187. package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/without_skill/grading.json +0 -64
  188. package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/without_skill/outputs/response.md +0 -209
  189. package/codex-cli-workspace/iteration-1/eval-3-profiles-troubleshooting/without_skill/timing.json +0 -5
  190. package/codex-cli-workspace/iteration-1/review.html +0 -1325
  191. package/gemini-cli-workspace/iteration-1/benchmark.json +0 -86
  192. package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/eval_metadata.json +0 -37
  193. package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/with_skill/grading.json +0 -37
  194. package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/with_skill/outputs/response.md +0 -401
  195. package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/with_skill/timing.json +0 -5
  196. package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/without_skill/grading.json +0 -37
  197. package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/without_skill/outputs/response.md +0 -405
  198. package/gemini-cli-workspace/iteration-1/eval-1-cicd-setup/without_skill/timing.json +0 -5
  199. package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/eval_metadata.json +0 -37
  200. package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/grading.json +0 -37
  201. package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/outputs/response.md +0 -212
  202. package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/with_skill/timing.json +0 -5
  203. package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/grading.json +0 -37
  204. package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/outputs/response.md +0 -427
  205. package/gemini-cli-workspace/iteration-1/eval-2-mcp-server-config/without_skill/timing.json +0 -5
  206. package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/eval_metadata.json +0 -32
  207. package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/with_skill/grading.json +0 -32
  208. package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/with_skill/outputs/response.md +0 -171
  209. package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/with_skill/timing.json +0 -5
  210. package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/without_skill/grading.json +0 -32
  211. package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/without_skill/outputs/response.md +0 -199
  212. package/gemini-cli-workspace/iteration-1/eval-3-custom-slash-command/without_skill/timing.json +0 -5
  213. package/gemini-cli-workspace/iteration-1/review.html +0 -1325
  214. package/gemini-cli-workspace/iteration-2/benchmark.json +0 -173
  215. package/gemini-cli-workspace/iteration-2/benchmark.md +0 -28
  216. package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/eval_metadata.json +0 -37
  217. package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/with_skill/grading.json +0 -37
  218. package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/with_skill/outputs/response.md +0 -195
  219. package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/with_skill/timing.json +0 -5
  220. package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/without_skill/grading.json +0 -37
  221. package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/without_skill/outputs/response.md +0 -377
  222. package/gemini-cli-workspace/iteration-2/eval-1-cicd-setup/without_skill/timing.json +0 -5
  223. package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/eval_metadata.json +0 -37
  224. package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/with_skill/grading.json +0 -37
  225. package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/with_skill/outputs/response.md +0 -127
  226. package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/with_skill/timing.json +0 -5
  227. package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/without_skill/grading.json +0 -37
  228. package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/without_skill/outputs/response.md +0 -164
  229. package/gemini-cli-workspace/iteration-2/eval-2-mcp-server-config/without_skill/timing.json +0 -5
  230. package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/eval_metadata.json +0 -32
  231. package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/with_skill/grading.json +0 -32
  232. package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/with_skill/outputs/response.md +0 -91
  233. package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/with_skill/timing.json +0 -5
  234. package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/without_skill/grading.json +0 -32
  235. package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/without_skill/outputs/response.md +0 -112
  236. package/gemini-cli-workspace/iteration-2/eval-3-custom-slash-command/without_skill/timing.json +0 -5
  237. package/gemini-cli-workspace/iteration-2/eval-viewer.html +0 -1325
  238. package/screen-recording-workspace/evals.json +0 -41
  239. package/screen-recording-workspace/iteration-1/benchmark.json +0 -102
  240. package/screen-recording-workspace/iteration-1/eval-0-fullscreen/eval_metadata.json +0 -31
  241. package/screen-recording-workspace/iteration-1/eval-0-fullscreen/with_skill/grading.json +0 -11
  242. package/screen-recording-workspace/iteration-1/eval-0-fullscreen/with_skill/outputs/demo.mp4 +0 -0
  243. package/screen-recording-workspace/iteration-1/eval-0-fullscreen/with_skill/timing.json +0 -5
  244. package/screen-recording-workspace/iteration-1/eval-0-fullscreen/without_skill/grading.json +0 -11
  245. package/screen-recording-workspace/iteration-1/eval-0-fullscreen/without_skill/outputs/demo.mp4 +0 -0
  246. package/screen-recording-workspace/iteration-1/eval-0-fullscreen/without_skill/timing.json +0 -5
  247. package/screen-recording-workspace/iteration-1/eval-1-region-audio/eval_metadata.json +0 -31
  248. package/screen-recording-workspace/iteration-1/eval-1-region-audio/with_skill/grading.json +0 -11
  249. package/screen-recording-workspace/iteration-1/eval-1-region-audio/with_skill/outputs/region_capture.mp4 +0 -0
  250. package/screen-recording-workspace/iteration-1/eval-1-region-audio/with_skill/timing.json +0 -5
  251. package/screen-recording-workspace/iteration-1/eval-1-region-audio/without_skill/grading.json +0 -11
  252. package/screen-recording-workspace/iteration-1/eval-1-region-audio/without_skill/outputs/region_capture.mp4 +0 -0
  253. package/screen-recording-workspace/iteration-1/eval-1-region-audio/without_skill/timing.json +0 -5
  254. package/screen-recording-workspace/iteration-1/eval-2-python-fallback/eval_metadata.json +0 -31
  255. package/screen-recording-workspace/iteration-1/eval-2-python-fallback/with_skill/grading.json +0 -11
  256. package/screen-recording-workspace/iteration-1/eval-2-python-fallback/with_skill/outputs/fallback_recording.mp4 +0 -0
  257. package/screen-recording-workspace/iteration-1/eval-2-python-fallback/with_skill/timing.json +0 -5
  258. package/screen-recording-workspace/iteration-1/eval-2-python-fallback/without_skill/grading.json +0 -11
  259. package/screen-recording-workspace/iteration-1/eval-2-python-fallback/without_skill/outputs/fallback_recording.mp4 +0 -0
  260. package/screen-recording-workspace/iteration-1/eval-2-python-fallback/without_skill/outputs/record_screen.py +0 -67
  261. package/screen-recording-workspace/iteration-1/eval-2-python-fallback/without_skill/timing.json +0 -5
  262. package/screen-recording-workspace/iteration-1/review.html +0 -1325
  263. package/src/skills/codex-cli/evals/evals.json +0 -47
  264. package/src/skills/gemini-cli/evals/evals.json +0 -46
  265. package/src/skills/tm-search/evals/evals.json +0 -23
@@ -1,1325 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>Eval Review</title>
7
- <link rel="preconnect" href="https://fonts.googleapis.com">
8
- <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
9
- <link href="https://fonts.googleapis.com/css2?family=Poppins:wght@500;600&family=Lora:wght@400;500&display=swap" rel="stylesheet">
10
- <script src="https://cdn.sheetjs.com/xlsx-0.20.3/package/dist/xlsx.full.min.js" integrity="sha384-EnyY0/GSHQGSxSgMwaIPzSESbqoOLSexfnSMN2AP+39Ckmn92stwABZynq1JyzdT" crossorigin="anonymous"></script>
11
- <style>
12
- :root {
13
- --bg: #faf9f5;
14
- --surface: #ffffff;
15
- --border: #e8e6dc;
16
- --text: #141413;
17
- --text-muted: #b0aea5;
18
- --accent: #d97757;
19
- --accent-hover: #c4613f;
20
- --green: #788c5d;
21
- --green-bg: #eef2e8;
22
- --red: #c44;
23
- --red-bg: #fceaea;
24
- --header-bg: #141413;
25
- --header-text: #faf9f5;
26
- --radius: 6px;
27
- }
28
-
29
- * { box-sizing: border-box; margin: 0; padding: 0; }
30
-
31
- body {
32
- font-family: 'Lora', Georgia, serif;
33
- background: var(--bg);
34
- color: var(--text);
35
- height: 100vh;
36
- display: flex;
37
- flex-direction: column;
38
- }
39
-
40
- /* ---- Header ---- */
41
- .header {
42
- background: var(--header-bg);
43
- color: var(--header-text);
44
- padding: 1rem 2rem;
45
- display: flex;
46
- justify-content: space-between;
47
- align-items: center;
48
- flex-shrink: 0;
49
- }
50
- .header h1 {
51
- font-family: 'Poppins', sans-serif;
52
- font-size: 1.25rem;
53
- font-weight: 600;
54
- }
55
- .header .instructions {
56
- font-size: 0.8rem;
57
- opacity: 0.7;
58
- margin-top: 0.25rem;
59
- }
60
- .header .progress {
61
- font-size: 0.875rem;
62
- opacity: 0.8;
63
- text-align: right;
64
- }
65
-
66
- /* ---- Main content ---- */
67
- .main {
68
- flex: 1;
69
- overflow-y: auto;
70
- padding: 1.5rem 2rem;
71
- display: flex;
72
- flex-direction: column;
73
- gap: 1.25rem;
74
- }
75
-
76
- /* ---- Sections ---- */
77
- .section {
78
- background: var(--surface);
79
- border: 1px solid var(--border);
80
- border-radius: var(--radius);
81
- flex-shrink: 0;
82
- }
83
- .section-header {
84
- font-family: 'Poppins', sans-serif;
85
- padding: 0.75rem 1rem;
86
- font-size: 0.75rem;
87
- font-weight: 500;
88
- text-transform: uppercase;
89
- letter-spacing: 0.05em;
90
- color: var(--text-muted);
91
- border-bottom: 1px solid var(--border);
92
- background: var(--bg);
93
- }
94
- .section-body {
95
- padding: 1rem;
96
- }
97
-
98
- /* ---- Config badge ---- */
99
- .config-badge {
100
- display: inline-block;
101
- padding: 0.2rem 0.625rem;
102
- border-radius: 9999px;
103
- font-family: 'Poppins', sans-serif;
104
- font-size: 0.6875rem;
105
- font-weight: 600;
106
- text-transform: uppercase;
107
- letter-spacing: 0.03em;
108
- margin-left: 0.75rem;
109
- vertical-align: middle;
110
- }
111
- .config-badge.config-primary {
112
- background: rgba(33, 150, 243, 0.12);
113
- color: #1976d2;
114
- }
115
- .config-badge.config-baseline {
116
- background: rgba(255, 193, 7, 0.15);
117
- color: #f57f17;
118
- }
119
-
120
- /* ---- Prompt ---- */
121
- .prompt-text {
122
- white-space: pre-wrap;
123
- font-size: 0.9375rem;
124
- line-height: 1.6;
125
- }
126
-
127
- /* ---- Outputs ---- */
128
- .output-file {
129
- border: 1px solid var(--border);
130
- border-radius: var(--radius);
131
- overflow: hidden;
132
- }
133
- .output-file + .output-file {
134
- margin-top: 1rem;
135
- }
136
- .output-file-header {
137
- padding: 0.5rem 0.75rem;
138
- font-size: 0.8rem;
139
- font-weight: 600;
140
- color: var(--text-muted);
141
- background: var(--bg);
142
- border-bottom: 1px solid var(--border);
143
- font-family: 'SF Mono', SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace;
144
- display: flex;
145
- justify-content: space-between;
146
- align-items: center;
147
- }
148
- .output-file-header .dl-btn {
149
- font-size: 0.7rem;
150
- color: var(--accent);
151
- text-decoration: none;
152
- cursor: pointer;
153
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
154
- font-weight: 500;
155
- opacity: 0.8;
156
- }
157
- .output-file-header .dl-btn:hover {
158
- opacity: 1;
159
- text-decoration: underline;
160
- }
161
- .output-file-content {
162
- padding: 0.75rem;
163
- overflow-x: auto;
164
- }
165
- .output-file-content pre {
166
- font-size: 0.8125rem;
167
- line-height: 1.5;
168
- white-space: pre-wrap;
169
- word-break: break-word;
170
- font-family: 'SF Mono', SFMono-Regular, Consolas, 'Liberation Mono', Menlo, monospace;
171
- }
172
- .output-file-content img {
173
- max-width: 100%;
174
- height: auto;
175
- border-radius: 4px;
176
- }
177
- .output-file-content iframe {
178
- width: 100%;
179
- height: 600px;
180
- border: none;
181
- }
182
- .output-file-content table {
183
- border-collapse: collapse;
184
- font-size: 0.8125rem;
185
- width: 100%;
186
- }
187
- .output-file-content table td,
188
- .output-file-content table th {
189
- border: 1px solid var(--border);
190
- padding: 0.375rem 0.5rem;
191
- text-align: left;
192
- }
193
- .output-file-content table th {
194
- background: var(--bg);
195
- font-weight: 600;
196
- }
197
- .output-file-content .download-link {
198
- display: inline-flex;
199
- align-items: center;
200
- gap: 0.5rem;
201
- padding: 0.5rem 1rem;
202
- background: var(--bg);
203
- border: 1px solid var(--border);
204
- border-radius: 4px;
205
- color: var(--accent);
206
- text-decoration: none;
207
- font-size: 0.875rem;
208
- cursor: pointer;
209
- }
210
- .output-file-content .download-link:hover {
211
- background: var(--border);
212
- }
213
- .empty-state {
214
- color: var(--text-muted);
215
- font-style: italic;
216
- padding: 2rem;
217
- text-align: center;
218
- }
219
-
220
- /* ---- Feedback ---- */
221
- .prev-feedback {
222
- background: var(--bg);
223
- border: 1px solid var(--border);
224
- border-radius: 4px;
225
- padding: 0.625rem 0.75rem;
226
- margin-top: 0.75rem;
227
- font-size: 0.8125rem;
228
- color: var(--text-muted);
229
- line-height: 1.5;
230
- }
231
- .prev-feedback-label {
232
- font-size: 0.7rem;
233
- font-weight: 600;
234
- text-transform: uppercase;
235
- letter-spacing: 0.04em;
236
- margin-bottom: 0.25rem;
237
- color: var(--text-muted);
238
- }
239
- .feedback-textarea {
240
- width: 100%;
241
- min-height: 100px;
242
- padding: 0.75rem;
243
- border: 1px solid var(--border);
244
- border-radius: 4px;
245
- font-family: inherit;
246
- font-size: 0.9375rem;
247
- line-height: 1.5;
248
- resize: vertical;
249
- color: var(--text);
250
- }
251
- .feedback-textarea:focus {
252
- outline: none;
253
- border-color: var(--accent);
254
- box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);
255
- }
256
- .feedback-status {
257
- font-size: 0.75rem;
258
- color: var(--text-muted);
259
- margin-top: 0.5rem;
260
- min-height: 1.1em;
261
- }
262
-
263
- /* ---- Grades (collapsible) ---- */
264
- .grades-toggle {
265
- display: flex;
266
- align-items: center;
267
- cursor: pointer;
268
- user-select: none;
269
- }
270
- .grades-toggle:hover {
271
- color: var(--accent);
272
- }
273
- .grades-toggle .arrow {
274
- margin-right: 0.5rem;
275
- transition: transform 0.15s;
276
- font-size: 0.75rem;
277
- }
278
- .grades-toggle .arrow.open {
279
- transform: rotate(90deg);
280
- }
281
- .grades-content {
282
- display: none;
283
- margin-top: 0.75rem;
284
- }
285
- .grades-content.open {
286
- display: block;
287
- }
288
- .grades-summary {
289
- font-size: 0.875rem;
290
- margin-bottom: 0.75rem;
291
- display: flex;
292
- align-items: center;
293
- gap: 0.5rem;
294
- }
295
- .grade-badge {
296
- display: inline-block;
297
- padding: 0.125rem 0.5rem;
298
- border-radius: 9999px;
299
- font-size: 0.75rem;
300
- font-weight: 600;
301
- }
302
- .grade-pass { background: var(--green-bg); color: var(--green); }
303
- .grade-fail { background: var(--red-bg); color: var(--red); }
304
- .assertion-list {
305
- list-style: none;
306
- }
307
- .assertion-item {
308
- padding: 0.625rem 0;
309
- border-bottom: 1px solid var(--border);
310
- font-size: 0.8125rem;
311
- }
312
- .assertion-item:last-child { border-bottom: none; }
313
- .assertion-status {
314
- font-weight: 600;
315
- margin-right: 0.5rem;
316
- }
317
- .assertion-status.pass { color: var(--green); }
318
- .assertion-status.fail { color: var(--red); }
319
- .assertion-evidence {
320
- color: var(--text-muted);
321
- font-size: 0.75rem;
322
- margin-top: 0.25rem;
323
- padding-left: 1.5rem;
324
- }
325
-
326
- /* ---- View tabs ---- */
327
- .view-tabs {
328
- display: flex;
329
- gap: 0;
330
- padding: 0 2rem;
331
- background: var(--bg);
332
- border-bottom: 1px solid var(--border);
333
- flex-shrink: 0;
334
- }
335
- .view-tab {
336
- font-family: 'Poppins', sans-serif;
337
- padding: 0.625rem 1.25rem;
338
- font-size: 0.8125rem;
339
- font-weight: 500;
340
- cursor: pointer;
341
- border: none;
342
- background: none;
343
- color: var(--text-muted);
344
- border-bottom: 2px solid transparent;
345
- transition: all 0.15s;
346
- }
347
- .view-tab:hover { color: var(--text); }
348
- .view-tab.active {
349
- color: var(--accent);
350
- border-bottom-color: var(--accent);
351
- }
352
- .view-panel { display: none; }
353
- .view-panel.active { display: flex; flex-direction: column; flex: 1; overflow: hidden; }
354
-
355
- /* ---- Benchmark view ---- */
356
- .benchmark-view {
357
- padding: 1.5rem 2rem;
358
- overflow-y: auto;
359
- flex: 1;
360
- }
361
- .benchmark-table {
362
- border-collapse: collapse;
363
- background: var(--surface);
364
- border: 1px solid var(--border);
365
- border-radius: var(--radius);
366
- font-size: 0.8125rem;
367
- width: 100%;
368
- margin-bottom: 1.5rem;
369
- }
370
- .benchmark-table th, .benchmark-table td {
371
- padding: 0.625rem 0.75rem;
372
- text-align: left;
373
- border: 1px solid var(--border);
374
- }
375
- .benchmark-table th {
376
- font-family: 'Poppins', sans-serif;
377
- background: var(--header-bg);
378
- color: var(--header-text);
379
- font-weight: 500;
380
- font-size: 0.75rem;
381
- text-transform: uppercase;
382
- letter-spacing: 0.04em;
383
- }
384
- .benchmark-table tr:hover { background: var(--bg); }
385
- .benchmark-table tr.benchmark-row-with { background: rgba(33, 150, 243, 0.06); }
386
- .benchmark-table tr.benchmark-row-without { background: rgba(255, 193, 7, 0.06); }
387
- .benchmark-table tr.benchmark-row-with:hover { background: rgba(33, 150, 243, 0.12); }
388
- .benchmark-table tr.benchmark-row-without:hover { background: rgba(255, 193, 7, 0.12); }
389
- .benchmark-table tr.benchmark-row-avg { font-weight: 600; border-top: 2px solid var(--border); }
390
- .benchmark-table tr.benchmark-row-avg.benchmark-row-with { background: rgba(33, 150, 243, 0.12); }
391
- .benchmark-table tr.benchmark-row-avg.benchmark-row-without { background: rgba(255, 193, 7, 0.12); }
392
- .benchmark-delta-positive { color: var(--green); font-weight: 600; }
393
- .benchmark-delta-negative { color: var(--red); font-weight: 600; }
394
- .benchmark-notes {
395
- background: var(--surface);
396
- border: 1px solid var(--border);
397
- border-radius: var(--radius);
398
- padding: 1rem;
399
- }
400
- .benchmark-notes h3 {
401
- font-family: 'Poppins', sans-serif;
402
- font-size: 0.875rem;
403
- margin-bottom: 0.75rem;
404
- }
405
- .benchmark-notes ul {
406
- list-style: disc;
407
- padding-left: 1.25rem;
408
- }
409
- .benchmark-notes li {
410
- font-size: 0.8125rem;
411
- line-height: 1.6;
412
- margin-bottom: 0.375rem;
413
- }
414
- .benchmark-empty {
415
- color: var(--text-muted);
416
- font-style: italic;
417
- text-align: center;
418
- padding: 3rem;
419
- }
420
-
421
- /* ---- Navigation ---- */
422
- .nav {
423
- display: flex;
424
- justify-content: space-between;
425
- align-items: center;
426
- padding: 1rem 2rem;
427
- border-top: 1px solid var(--border);
428
- background: var(--surface);
429
- flex-shrink: 0;
430
- }
431
- .nav-btn {
432
- font-family: 'Poppins', sans-serif;
433
- padding: 0.5rem 1.25rem;
434
- border: 1px solid var(--border);
435
- border-radius: var(--radius);
436
- background: var(--surface);
437
- cursor: pointer;
438
- font-size: 0.875rem;
439
- font-weight: 500;
440
- color: var(--text);
441
- transition: all 0.15s;
442
- }
443
- .nav-btn:hover:not(:disabled) {
444
- background: var(--bg);
445
- border-color: var(--text-muted);
446
- }
447
- .nav-btn:disabled {
448
- opacity: 0.4;
449
- cursor: not-allowed;
450
- }
451
- .done-btn {
452
- font-family: 'Poppins', sans-serif;
453
- padding: 0.5rem 1.5rem;
454
- border: 1px solid var(--border);
455
- border-radius: var(--radius);
456
- background: var(--surface);
457
- color: var(--text);
458
- cursor: pointer;
459
- font-size: 0.875rem;
460
- font-weight: 500;
461
- transition: all 0.15s;
462
- }
463
- .done-btn:hover {
464
- background: var(--bg);
465
- border-color: var(--text-muted);
466
- }
467
- .done-btn.ready {
468
- border: none;
469
- background: var(--accent);
470
- color: white;
471
- font-weight: 600;
472
- }
473
- .done-btn.ready:hover {
474
- background: var(--accent-hover);
475
- }
476
- /* ---- Done overlay ---- */
477
- .done-overlay {
478
- display: none;
479
- position: fixed;
480
- inset: 0;
481
- background: rgba(0, 0, 0, 0.5);
482
- z-index: 100;
483
- justify-content: center;
484
- align-items: center;
485
- }
486
- .done-overlay.visible {
487
- display: flex;
488
- }
489
- .done-card {
490
- background: var(--surface);
491
- border-radius: 12px;
492
- padding: 2rem 3rem;
493
- text-align: center;
494
- box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
495
- max-width: 500px;
496
- }
497
- .done-card h2 {
498
- font-size: 1.5rem;
499
- margin-bottom: 0.5rem;
500
- }
501
- .done-card p {
502
- color: var(--text-muted);
503
- margin-bottom: 1.5rem;
504
- line-height: 1.5;
505
- }
506
- .done-card .btn-row {
507
- display: flex;
508
- gap: 0.5rem;
509
- justify-content: center;
510
- }
511
- .done-card button {
512
- padding: 0.5rem 1.25rem;
513
- border: 1px solid var(--border);
514
- border-radius: var(--radius);
515
- background: var(--surface);
516
- cursor: pointer;
517
- font-size: 0.875rem;
518
- }
519
- .done-card button:hover {
520
- background: var(--bg);
521
- }
522
- /* ---- Toast ---- */
523
- .toast {
524
- position: fixed;
525
- bottom: 5rem;
526
- left: 50%;
527
- transform: translateX(-50%);
528
- background: var(--header-bg);
529
- color: var(--header-text);
530
- padding: 0.625rem 1.25rem;
531
- border-radius: var(--radius);
532
- font-size: 0.875rem;
533
- opacity: 0;
534
- transition: opacity 0.3s;
535
- pointer-events: none;
536
- z-index: 200;
537
- }
538
- .toast.visible {
539
- opacity: 1;
540
- }
541
- </style>
542
- </head>
543
- <body>
544
- <div id="app" style="height:100vh; display:flex; flex-direction:column;">
545
- <div class="header">
546
- <div>
547
- <h1>Eval Review: <span id="skill-name"></span></h1>
548
- <div class="instructions">Review each output and leave feedback below. Navigate with arrow keys or buttons. When done, copy feedback and paste into Claude Code.</div>
549
- </div>
550
- <div class="progress" id="progress"></div>
551
- </div>
552
-
553
- <!-- View tabs (only shown when benchmark data exists) -->
554
- <div class="view-tabs" id="view-tabs" style="display:none;">
555
- <button class="view-tab active" onclick="switchView('outputs')">Outputs</button>
556
- <button class="view-tab" onclick="switchView('benchmark')">Benchmark</button>
557
- </div>
558
-
559
- <!-- Outputs panel (qualitative review) -->
560
- <div class="view-panel active" id="panel-outputs">
561
- <div class="main">
562
- <!-- Prompt -->
563
- <div class="section">
564
- <div class="section-header">Prompt <span class="config-badge" id="config-badge" style="display:none;"></span></div>
565
- <div class="section-body">
566
- <div class="prompt-text" id="prompt-text"></div>
567
- </div>
568
- </div>
569
-
570
- <!-- Outputs -->
571
- <div class="section">
572
- <div class="section-header">Output</div>
573
- <div class="section-body" id="outputs-body">
574
- <div class="empty-state">No output files found</div>
575
- </div>
576
- </div>
577
-
578
- <!-- Previous Output (collapsible) -->
579
- <div class="section" id="prev-outputs-section" style="display:none;">
580
- <div class="section-header">
581
- <div class="grades-toggle" onclick="togglePrevOutputs()">
582
- <span class="arrow" id="prev-outputs-arrow">&#9654;</span>
583
- Previous Output
584
- </div>
585
- </div>
586
- <div class="grades-content" id="prev-outputs-content"></div>
587
- </div>
588
-
589
- <!-- Grades (collapsible) -->
590
- <div class="section" id="grades-section" style="display:none;">
591
- <div class="section-header">
592
- <div class="grades-toggle" onclick="toggleGrades()">
593
- <span class="arrow" id="grades-arrow">&#9654;</span>
594
- Formal Grades
595
- </div>
596
- </div>
597
- <div class="grades-content" id="grades-content"></div>
598
- </div>
599
-
600
- <!-- Feedback -->
601
- <div class="section">
602
- <div class="section-header">Your Feedback</div>
603
- <div class="section-body">
604
- <textarea
605
- class="feedback-textarea"
606
- id="feedback"
607
- placeholder="What do you think of this output? Any issues, suggestions, or things that look great?"
608
- ></textarea>
609
- <div class="feedback-status" id="feedback-status"></div>
610
- <div class="prev-feedback" id="prev-feedback" style="display:none;">
611
- <div class="prev-feedback-label">Previous feedback</div>
612
- <div id="prev-feedback-text"></div>
613
- </div>
614
- </div>
615
- </div>
616
- </div>
617
-
618
- <div class="nav" id="outputs-nav">
619
- <button class="nav-btn" id="prev-btn" onclick="navigate(-1)">&#8592; Previous</button>
620
- <button class="done-btn" id="done-btn" onclick="showDoneDialog()">Submit All Reviews</button>
621
- <button class="nav-btn" id="next-btn" onclick="navigate(1)">Next &#8594;</button>
622
- </div>
623
- </div><!-- end panel-outputs -->
624
-
625
- <!-- Benchmark panel (quantitative stats) -->
626
- <div class="view-panel" id="panel-benchmark">
627
- <div class="benchmark-view" id="benchmark-content">
628
- <div class="benchmark-empty">No benchmark data available. Run a benchmark to see quantitative results here.</div>
629
- </div>
630
- </div>
631
- </div>
632
-
633
- <!-- Done overlay -->
634
- <div class="done-overlay" id="done-overlay">
635
- <div class="done-card">
636
- <h2>Review Complete</h2>
637
- <p>Your feedback has been saved. Go back to your Claude Code session and tell Claude you're done reviewing.</p>
638
- <div class="btn-row">
639
- <button onclick="closeDoneDialog()">OK</button>
640
- </div>
641
- </div>
642
- </div>
643
-
644
- <!-- Toast -->
645
- <div class="toast" id="toast"></div>
646
-
647
- <script>
648
- // ---- Embedded data (injected by generate_review.py) ----
649
- const EMBEDDED_DATA = {"skill_name": "codex-cli", "runs": [{"id": "eval-1-ci-integration-with_skill", "prompt": "I want to set up OpenAI Codex CLI in my GitHub Actions pipeline to auto-fix lint errors on PRs. How do I configure it with full-auto mode and workspace-write sandbox? My OPENAI_API_KEY is stored in GitHub secrets.", "eval_id": 1, "outputs": [{"name": "response.md", "type": "text", "content": "# Setting Up OpenAI Codex CLI in GitHub Actions to Auto-Fix Lint Errors\n\n## Overview\n\nYou can use `codex exec` (the non-interactive mode of Codex CLI) inside a GitHub Actions workflow to automatically fix lint errors on pull requests. The key pieces are:\n\n- **`--full-auto` flag** -- shortcut for `-a on-request -s workspace-write`, which allows Codex to write files within the working directory without requiring interactive approval.\n- **`OPENAI_API_KEY`** -- passed from GitHub Secrets via the `env` block.\n\n## Workflow File\n\nCreate `.github/workflows/codex-lint-fix.yml` in your repository:\n\n```yaml\nname: Auto-fix lint errors with Codex CLI\n\non:\n pull_request:\n types: [opened, synchronize]\n\npermissions:\n contents: write\n\njobs:\n lint-fix:\n runs-on: ubuntu-latest\n steps:\n - name: Checkout PR branch\n uses: actions/checkout@v4\n with:\n ref: ${{ github.head_ref }}\n # Needed so the push step can write back to the PR branch\n token: ${{ secrets.GITHUB_TOKEN }}\n\n - name: Set up Node.js\n uses: actions/setup-node@v4\n with:\n node-version: '20'\n\n - name: Install dependencies\n run: npm ci\n\n - name: Install Codex CLI\n run: npm install -g @openai/codex\n\n - name: Run linter and capture errors\n id: lint\n run: |\n npm run lint 2>&1 | tee lint-output.txt || true\n\n - name: Auto-fix lint errors with Codex\n run: |\n codex exec \\\n --full-auto \\\n --model gpt-5-codex \\\n \"Read lint-output.txt for the lint errors in this project. Fix all the lint errors in the source files. Do not modify any test files unless the lint error is in a test file.\"\n env:\n OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}\n\n - name: Commit and push fixes\n run: |\n git config user.name \"codex-bot\"\n git config user.email \"codex-bot@users.noreply.github.com\"\n git add -A\n git diff --cached --quiet || git commit -m \"fix: auto-fix lint errors via Codex CLI\"\n git push\n```\n\n## How It Works\n\n1. **Checkout** -- The PR branch is checked out with write access so fixes can be pushed back.\n2. **Lint** -- Your existing lint command runs and its output is captured to a file. The `|| true` prevents the step from failing the workflow (lint errors are expected).\n3. **Codex exec** -- Runs in non-interactive mode with `--full-auto`, which sets:\n - **Approval policy**: `on-request` (Codex proceeds without interactive prompts for standard file edits)\n - **Sandbox**: `workspace-write` (Codex can only write files within the checked-out repository directory)\n4. **Commit and push** -- If Codex made any changes, they are committed and pushed to the PR branch.\n\n## Understanding `--full-auto`\n\nThe `--full-auto` flag is a convenience shortcut equivalent to:\n\n```bash\ncodex exec -a on-request -s workspace-write \"task\"\n```\n\n| Setting | Value | Meaning |\n|---------|-------|---------|\n| Approval policy (`-a`) | `on-request` | Codex runs autonomously for standard operations; only pauses on sensitive actions |\n| Sandbox (`-s`) | `workspace-write` | Codex can read/write files in the working directory but cannot access the broader filesystem |\n\nThis is the recommended combination for CI pipelines because it gives Codex enough freedom to edit source files while preventing it from modifying anything outside the project.\n\n### If You Want Fully Unattended Execution\n\nIf you want Codex to never pause for any reason (no approval prompts at all), use explicit flags instead of `--full-auto`:\n\n```bash\ncodex exec -a never -s workspace-write \"Fix all lint errors\"\n```\n\nThe `-a never` policy means Codex will never ask for approval, even for potentially sensitive actions. Use this when you are confident in the prompt and the sandbox constraint is sufficient protection.\n\n## Configuring the API Key\n\nYour `OPENAI_API_KEY` stored in GitHub Secrets is passed to Codex through the `env` block on the step:\n\n```yaml\nenv:\n OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}\n```\n\nTo add the secret:\n1. Go to your repository on GitHub.\n2. Navigate to **Settings > Secrets and variables > Actions**.\n3. Click **New repository secret**.\n4. Name: `OPENAI_API_KEY`, Value: your OpenAI API key.\n\n## Optional: Enable Network Access\n\nBy default, the `workspace-write` sandbox disables network access. If your lint fix workflow requires installing packages or fetching resources, enable network with a config override:\n\n```yaml\n- name: Auto-fix lint errors with Codex\n run: |\n codex exec \\\n --full-auto \\\n -c 'sandbox_workspace_write.network_access=true' \\\n \"Fix all lint errors found in lint-output.txt\"\n env:\n OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}\n```\n\n## Optional: Use a Config Profile\n\nInstead of passing all flags on the command line, you can create a `.codex/config.toml` in your repo with a CI profile:\n\n```toml\n[profiles.ci]\nmodel = \"gpt-5-codex\"\napproval_policy = \"never\"\n\n[profiles.ci.sandbox_workspace_write]\nnetwork_access = false\n```\n\nThen reference it in the workflow:\n\n```yaml\n- name: Auto-fix lint errors with Codex\n run: |\n codex exec --profile ci \"Fix all lint errors found in lint-output.txt\"\n env:\n OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}\n```\n\n## Troubleshooting\n\n| Problem | Solution |\n|---------|----------|\n| Landlock sandbox error on Ubuntu runner | Update the runner image or, in isolated CI only, use `--dangerously-bypass-approvals-and-sandbox` |\n| Network access denied during fix | Add `-c 'sandbox_workspace_write.network_access=true'` |\n| Codex keeps prompting for approval | Make sure you are using `codex exec` (not bare `codex`), and pass `--full-auto` or `-a never` explicitly |\n| Auth loop / browser auth attempt | Ensure `OPENAI_API_KEY` is set in the `env` block -- Codex uses it automatically and skips browser auth |\n"}], "grading": {"expectations": [{"text": "Shows npm install -g @openai/codex for global installation", "passed": true, "evidence": "Line 44: 'run: npm install -g @openai/codex'"}, {"text": "Uses 'codex exec' for non-interactive CI mode (not bare 'codex')", "passed": true, "evidence": "Line 53: 'codex exec \\' used in the workflow step"}, {"text": "Uses --full-auto flag and correctly explains it equals -a on-request -s workspace-write", "passed": true, "evidence": "Line 54: '--full-auto' and line 80-83: 'The --full-auto flag is a convenience shortcut equivalent to: codex exec -a on-request -s workspace-write'"}, {"text": "Shows OPENAI_API_KEY set from ${{ secrets.OPENAI_API_KEY }} in env", "passed": true, "evidence": "Line 58: 'OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}'"}, {"text": "Provides a complete GitHub Actions workflow YAML snippet", "passed": true, "evidence": "Lines 14-67: Complete workflow with checkout, node setup, install, lint, codex exec, and commit/push steps"}, {"text": "Does not invent non-existent CLI flags or options", "passed": true, "evidence": "All flags used (--full-auto, -a never, -s workspace-write, -c, --model, --profile, --dangerously-bypass-approvals-and-sandbox) match the skill documentation"}], "summary": {"passed": 6, "failed": 0, "total": 6, "pass_rate": 1.0}, "claims": [{"claim": "--full-auto equals -a on-request -s workspace-write", "type": "factual", "verified": true, "evidence": "Matches skill doc line 64: '# Equivalent to: -a on-request -s workspace-write'"}], "eval_feedback": {"suggestions": [], "overall": "No suggestions, evals look solid"}}}, {"id": "eval-1-ci-integration-without_skill", "prompt": "I want to set up OpenAI Codex CLI in my GitHub Actions pipeline to auto-fix lint errors on PRs. How do I configure it with full-auto mode and workspace-write sandbox? My OPENAI_API_KEY is stored in GitHub secrets.", "eval_id": 1, "outputs": [{"name": "response.md", "type": "text", "content": "# Setting Up OpenAI Codex CLI in GitHub Actions for Auto-Fixing Lint Errors\n\n## Overview\n\nOpenAI Codex CLI is a terminal-based AI coding agent. You can integrate it into a GitHub Actions workflow to automatically fix lint errors on pull requests using its `full-auto` approval mode and `write-only` sandbox policy.\n\n## Key Concepts\n\n- **`full-auto` mode**: Codex CLI automatically approves all actions (file reads, writes, and command execution) without human confirmation. Set via `--approval-mode full-auto`.\n- **`write-only` sandbox** (also called `workspace-write`): Restricts file writes to the current working directory (the repo workspace). Network access and writes outside the workspace are blocked. Set via `--sandbox write-only`.\n\n## GitHub Actions Workflow\n\nCreate `.github/workflows/codex-lint-fix.yml`:\n\n```yaml\nname: Codex Auto-Fix Lint Errors\n\non:\n pull_request:\n types: [opened, synchronize]\n\npermissions:\n contents: write\n pull-requests: write\n\njobs:\n lint-fix:\n runs-on: ubuntu-latest\n\n steps:\n - name: Checkout PR branch\n uses: actions/checkout@v4\n with:\n ref: ${{ github.head_ref }}\n fetch-depth: 0\n token: ${{ secrets.GITHUB_TOKEN }}\n\n - name: Set up Node.js\n uses: actions/setup-node@v4\n with:\n node-version: '20'\n\n - name: Install dependencies\n run: npm ci\n\n - name: Install OpenAI Codex CLI\n run: npm install -g @openai/codex\n\n - name: Run linter and capture errors\n id: lint\n continue-on-error: true\n run: |\n npx eslint . --format json > lint-results.json 2>&1 || true\n if [ -s lint-results.json ]; then\n echo \"has_errors=true\" >> \"$GITHUB_OUTPUT\"\n else\n echo \"has_errors=false\" >> \"$GITHUB_OUTPUT\"\n fi\n\n - name: Auto-fix lint errors with Codex CLI\n if: steps.lint.outputs.has_errors == 'true'\n env:\n OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}\n run: |\n codex \\\n --approval-mode full-auto \\\n --sandbox write-only \\\n \"Read the lint errors in lint-results.json and fix all the reported ESLint errors and warnings in the source files. Do not change any logic, only fix lint issues.\"\n\n - name: Commit and push fixes\n run: |\n git config user.name \"github-actions[bot]\"\n git config user.email \"github-actions[bot]@users.noreply.github.com\"\n git add -A\n git diff --cached --quiet && echo \"No changes to commit\" && exit 0\n git commit -m \"fix: auto-fix lint errors via Codex CLI\"\n git push\n```\n\n## Configuration Breakdown\n\n### Authentication\n\nThe `OPENAI_API_KEY` secret must be configured in your repository:\n\n1. Go to your repo on GitHub.\n2. Navigate to **Settings > Secrets and variables > Actions**.\n3. Click **New repository secret**.\n4. Name: `OPENAI_API_KEY`, Value: your OpenAI API key.\n\nThe workflow references it as `${{ secrets.OPENAI_API_KEY }}` and passes it to the Codex CLI step via the `env` block.\n\n### Approval Mode\n\n`--approval-mode full-auto` tells Codex CLI to execute all suggested changes without waiting for human approval. This is required in CI where there is no interactive terminal.\n\nThe three approval modes are:\n- `suggest` (default) -- requires approval for everything\n- `auto-edit` -- auto-approves file edits but asks before running commands\n- `full-auto` -- auto-approves all actions including command execution\n\n### Sandbox Policy\n\n`--sandbox write-only` (workspace-write) restricts Codex to:\n- Reading any file in the workspace\n- Writing only to files within the current working directory\n- No network access from spawned commands\n- No writes outside the repo checkout directory\n\nThis provides a safety boundary so Codex cannot modify system files or exfiltrate data, even in `full-auto` mode.\n\n### Alternative: Using a Codex Config File\n\nInstead of passing flags on the command line, you can create a `codex.json` or use the `~/.codex/config.json` approach. In CI, you can write it inline:\n\n```yaml\n - name: Configure Codex CLI\n run: |\n mkdir -p ~/.codex\n cat > ~/.codex/config.json << 'EOF'\n {\n \"approvalMode\": \"full-auto\",\n \"sandbox\": \"write-only\",\n \"model\": \"o4-mini\"\n }\n EOF\n```\n\n### Model Selection\n\nBy default, Codex CLI uses `o4-mini`. You can specify a different model:\n\n```bash\ncodex --model o3 --approval-mode full-auto --sandbox write-only \"Fix lint errors...\"\n```\n\n## Security Considerations\n\n1. **Never hardcode your API key** in the workflow file. Always use GitHub Secrets.\n2. **`write-only` sandbox** is recommended for CI to prevent unintended network calls or file access outside the workspace.\n3. **Review the generated commits**. Even though Codex runs automatically, the changes are pushed to the PR branch where reviewers can inspect them.\n4. **Limit permissions**. The workflow uses `contents: write` and `pull-requests: write` which are the minimum needed to push commits and interact with the PR.\n5. **Cost control**. Each Codex invocation calls the OpenAI API. Consider adding conditions to skip the step if there are no lint errors, as shown in the workflow above.\n\n## Troubleshooting\n\n- **\"codex: command not found\"**: Make sure the install step uses `npm install -g @openai/codex` and that the Node.js version is 20 or higher.\n- **Authentication errors**: Verify the `OPENAI_API_KEY` secret is set correctly and the key is valid.\n- **Push failures**: Ensure the checkout step uses `token: ${{ secrets.GITHUB_TOKEN }}` and the workflow has `contents: write` permission.\n- **No changes committed**: If Codex cannot fix certain lint errors, the `git diff --cached --quiet` check will skip the commit gracefully.\n"}], "grading": {"expectations": [{"text": "Shows npm install -g @openai/codex for global installation", "passed": true, "evidence": "Line 49: 'run: npm install -g @openai/codex'"}, {"text": "Uses 'codex exec' for non-interactive CI mode (not bare 'codex')", "passed": false, "evidence": "Line 66: Uses bare 'codex \\' instead of 'codex exec'. The exec subcommand is required for non-interactive CI mode."}, {"text": "Uses --full-auto flag and correctly explains it equals -a on-request -s workspace-write", "passed": false, "evidence": "Uses hallucinated '--approval-mode full-auto' flag (correct is --full-auto or --ask-for-approval/-a) and '--sandbox write-only' (correct is -s workspace-write). Also incorrectly describes approval modes as 'suggest', 'auto-edit', 'full-auto' instead of the real ones: 'untrusted', 'on-request', 'never', 'reject'."}, {"text": "Shows OPENAI_API_KEY set from ${{ secrets.OPENAI_API_KEY }} in env", "passed": true, "evidence": "Line 64: 'OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}'"}, {"text": "Provides a complete GitHub Actions workflow YAML snippet", "passed": true, "evidence": "Lines 16-79: Complete workflow YAML with checkout, node, install, lint, codex, and commit steps"}, {"text": "Does not invent non-existent CLI flags or options", "passed": false, "evidence": "Hallucinated flags: '--approval-mode full-auto' (not a real flag), '--sandbox write-only' (not a real flag), '--model o3'. Hallucinated modes: 'suggest', 'auto-edit'. Hallucinated config format: 'codex.json'/'config.json' instead of config.toml."}], "summary": {"passed": 3, "failed": 3, "total": 6, "pass_rate": 0.5}, "claims": [{"claim": "Approval modes are suggest, auto-edit, full-auto", "type": "factual", "verified": false, "evidence": "Real approval modes are: untrusted, on-request, never, reject"}, {"claim": "Config file is codex.json or config.json", "type": "factual", "verified": false, "evidence": "Real config file is ~/.codex/config.toml (TOML format, not JSON)"}], "eval_feedback": {"suggestions": [], "overall": "Baseline shows significant hallucination of CLI flags and config format without the skill"}}}, {"id": "eval-2-mcp-server-config-with_skill", "prompt": "How do I add a custom MCP server to codex CLI? I have a Python-based MCP server that needs a GITHUB_TOKEN env var. Also want to run codex itself as an MCP server for other agents to consume.", "eval_id": 2, "outputs": [{"name": "response.md", "type": "text", "content": "# Adding a Custom MCP Server to Codex CLI and Running Codex as an MCP Server\n\n## Adding Your Python-based MCP Server with GITHUB_TOKEN\n\nAdd the following to your Codex configuration file at `~/.codex/config.toml`:\n\n```toml\n[mcp_servers.my-python-server]\ncommand = \"python\"\nargs = [\"/path/to/your/mcp_server.py\"]\nenv = { GITHUB_TOKEN = \"$GITHUB_TOKEN\" }\n```\n\nKey details:\n\n- **`command`** -- the executable to launch your server (e.g., `python`, `python3`, or an absolute path to your Python interpreter).\n- **`args`** -- an array of arguments passed to the command. Point this at your MCP server script or module entry point.\n- **`env`** -- a table of environment variables forwarded to the server process. Using `\"$GITHUB_TOKEN\"` tells Codex to pull the value from your shell environment at runtime, so you do not hardcode the token in the config file. Make sure `GITHUB_TOKEN` is exported in your shell (e.g., via `export GITHUB_TOKEN=\"ghp_...\"` in your `.bashrc`/`.zshrc` or a `.env` loader).\n\nIf your Python server is packaged as a module you run with `python -m`, the config would look like:\n\n```toml\n[mcp_servers.my-python-server]\ncommand = \"python\"\nargs = [\"-m\", \"my_mcp_server\"]\nenv = { GITHUB_TOKEN = \"$GITHUB_TOKEN\" }\n```\n\nIf your MCP server exposes an HTTP endpoint instead of running as a stdio subprocess, use the `url` form:\n\n```toml\n[mcp_servers.my-python-server]\nurl = \"https://localhost:8080/mcp\"\n```\n\nIn that case you would manage the `GITHUB_TOKEN` env var in whatever process starts the HTTP server, not in the Codex config.\n\n### Verifying it works\n\nAfter adding the config, launch Codex and use the `/mcp` slash command in the TUI to list all available MCP tools. Your server's tools should appear in the list.\n\n### Per-project config\n\nYou can also place MCP server config in a project-level `.codex/config.toml` (in your project root) if the server is only relevant to a specific project. Project config is only loaded for trusted projects.\n\n---\n\n## Running Codex Itself as an MCP Server\n\nTo expose Codex CLI as an MCP server that other agents can consume, run:\n\n```bash\ncodex mcp\n```\n\nThis starts Codex in MCP server mode, making its capabilities (code reading, editing, execution) available as MCP tools to any MCP-compatible client or agent. Other agents can then connect to Codex the same way Codex connects to third-party MCP servers -- either via stdio or by pointing at the running process.\n\n---\n\n## Full Config Example Combining Both\n\nHere is a complete `~/.codex/config.toml` that sets up your Python MCP server, a GitHub MCP server (for reference), and general Codex settings:\n\n```toml\nmodel = \"gpt-5-codex\"\napproval_policy = \"on-request\"\n\n[sandbox_workspace_write]\nnetwork_access = false\n\n[mcp_servers.github]\ncommand = \"npx\"\nargs = [\"-y\", \"@modelcontextprotocol/server-github\"]\nenv = { GITHUB_TOKEN = \"$GITHUB_TOKEN\" }\n\n[mcp_servers.my-python-server]\ncommand = \"python\"\nargs = [\"/path/to/your/mcp_server.py\"]\nenv = { GITHUB_TOKEN = \"$GITHUB_TOKEN\" }\n```\n\nIf you need network access for your MCP server to make API calls, set `network_access = true` under `[sandbox_workspace_write]`, or pass it as a one-off flag:\n\n```bash\ncodex -c 'sandbox_workspace_write.network_access=true' \"Use my-python-server to fetch data\"\n```\n"}], "grading": {"expectations": [{"text": "Points to ~/.codex/config.toml for MCP server configuration", "passed": true, "evidence": "Line 5: 'Add the following to your Codex configuration file at ~/.codex/config.toml'"}, {"text": "Shows [mcp_servers.name] TOML section with command, args fields", "passed": true, "evidence": "Line 8: '[mcp_servers.my-python-server]' with command and args fields"}, {"text": "Shows $GITHUB_TOKEN or $ENV_VAR pattern for environment variable references", "passed": true, "evidence": "Line 11: 'env = { GITHUB_TOKEN = \"$GITHUB_TOKEN\" }'"}, {"text": "Shows 'codex mcp' command for running codex as an MCP server", "passed": true, "evidence": "Line 53: 'codex mcp'"}, {"text": "Uses correct Python command (python/python3) in the MCP server command field", "passed": true, "evidence": "Line 9: 'command = \"python\"'"}, {"text": "Does not invent non-existent config keys or MCP server fields", "passed": true, "evidence": "All config keys (command, args, env, url, model, approval_policy, network_access) match the skill documentation"}], "summary": {"passed": 6, "failed": 0, "total": 6, "pass_rate": 1.0}, "claims": [{"claim": "MCP servers can use url form for HTTP endpoints", "type": "factual", "verified": true, "evidence": "Matches skill doc: '[mcp_servers.my-api] url = \"https://my-server.example.com/mcp\"'"}], "eval_feedback": {"suggestions": [], "overall": "No suggestions, evals look solid"}}}, {"id": "eval-2-mcp-server-config-without_skill", "prompt": "How do I add a custom MCP server to codex CLI? I have a Python-based MCP server that needs a GITHUB_TOKEN env var. Also want to run codex itself as an MCP server for other agents to consume.", "eval_id": 2, "outputs": [{"name": "response.md", "type": "text", "content": "# Adding a Custom MCP Server to Codex CLI & Running Codex as an MCP Server\n\n## Part 1: Adding a Custom Python MCP Server with GITHUB_TOKEN\n\nCodex CLI supports MCP (Model Context Protocol) servers that extend its capabilities with custom tools. You configure them in your Codex CLI configuration file.\n\n### Configuration File Location\n\nCodex CLI reads its configuration from a JSON file. The typical location is:\n\n- **Linux/macOS**: `~/.codex/config.json` (or potentially `~/.config/codex/config.json`)\n- You can also use a project-local configuration\n\n### Adding Your Python MCP Server\n\nIn your Codex CLI configuration file, add an `mcpServers` section. For a Python-based MCP server that needs the `GITHUB_TOKEN` environment variable:\n\n```json\n{\n \"mcpServers\": {\n \"my-github-server\": {\n \"command\": \"python\",\n \"args\": [\"/path/to/your/mcp_server.py\"],\n \"env\": {\n \"GITHUB_TOKEN\": \"your-github-token-here\"\n }\n }\n }\n}\n```\n\n### Configuration Fields\n\n| Field | Description |\n|-------|-------------|\n| `command` | The executable to run (e.g., `python`, `python3`, `node`) |\n| `args` | Array of arguments passed to the command |\n| `env` | Object of environment variables to set for the server process |\n\n### Using a Virtual Environment\n\nIf your Python MCP server has dependencies managed via a virtual environment, point to the Python binary inside the venv:\n\n```json\n{\n \"mcpServers\": {\n \"my-github-server\": {\n \"command\": \"/path/to/venv/bin/python\",\n \"args\": [\"/path/to/your/mcp_server.py\"],\n \"env\": {\n \"GITHUB_TOKEN\": \"your-github-token-here\"\n }\n }\n }\n}\n```\n\n### Using uvx or pipx\n\nIf your MCP server is distributed as a Python package, you can use `uvx` (from the `uv` tool) or `pipx`:\n\n```json\n{\n \"mcpServers\": {\n \"my-github-server\": {\n \"command\": \"uvx\",\n \"args\": [\"my-github-mcp-server\"],\n \"env\": {\n \"GITHUB_TOKEN\": \"your-github-token-here\"\n }\n }\n }\n}\n```\n\n### Security Tip for the Token\n\nRather than hardcoding the token in the config file, you can reference an environment variable that is already set in your shell session. However, the `env` field in the MCP config explicitly sets environment variables for the child process. To avoid storing secrets in config files, consider:\n\n1. Setting `GITHUB_TOKEN` in your shell profile (`.bashrc`, `.zshrc`) so it is inherited by child processes automatically. In that case you may not need the `env` field at all.\n2. Using a secrets manager or `.env` file that your MCP server reads directly.\n\n### Verifying the Server is Connected\n\nAfter configuring the MCP server, start Codex CLI. It should automatically launch and connect to your MCP server via stdio. The tools provided by your server will be available in the Codex session. You can verify by asking Codex to list available tools or by using one of the tools your server exposes.\n\n---\n\n## Part 2: Running Codex CLI as an MCP Server\n\nCodex CLI can itself be run as an MCP server, allowing other agents or tools that support MCP to consume it as a tool provider.\n\n### Starting Codex as an MCP Server\n\nUse the `--mcp-server` flag (or similar) to start Codex in MCP server mode:\n\n```bash\ncodex --mcp-server\n```\n\nThis starts Codex CLI listening on **stdio** (stdin/stdout) using the MCP protocol, making it available as a tool for other MCP-compatible clients.\n\n### Consuming Codex from Another Agent\n\nIn the consuming agent's MCP configuration, add Codex as a server:\n\n```json\n{\n \"mcpServers\": {\n \"codex\": {\n \"command\": \"codex\",\n \"args\": [\"--mcp-server\"],\n \"env\": {\n \"OPENAI_API_KEY\": \"your-openai-api-key\"\n }\n }\n }\n}\n```\n\n### Example: Adding Codex as an MCP Server in Claude Desktop\n\nIf you are using Claude Desktop (which also supports MCP), you can add Codex to the Claude Desktop MCP config (typically `claude_desktop_config.json`):\n\n```json\n{\n \"mcpServers\": {\n \"codex\": {\n \"command\": \"npx\",\n \"args\": [\"@openai/codex\", \"--mcp-server\"],\n \"env\": {\n \"OPENAI_API_KEY\": \"your-openai-api-key\"\n }\n }\n }\n}\n```\n\n### What Codex Exposes as an MCP Server\n\nWhen running as an MCP server, Codex typically exposes tools that allow the consuming agent to:\n\n- Execute coding tasks via Codex's AI capabilities\n- Run shell commands in a sandboxed environment\n- Perform file operations (read, write, edit)\n- Leverage Codex's code generation and reasoning abilities\n\n---\n\n## Summary\n\n| Goal | Configuration |\n|------|---------------|\n| Add custom MCP server to Codex | Add entry to `mcpServers` in Codex config with `command`, `args`, and `env` |\n| Pass `GITHUB_TOKEN` to MCP server | Use the `env` field in the server config |\n| Run Codex as an MCP server | Start with `codex --mcp-server` flag |\n| Consume Codex from another agent | Add Codex as an MCP server entry in the consuming agent's config |\n\n### Key Points\n\n- MCP servers communicate over **stdio** by default (stdin/stdout JSON-RPC messages).\n- The `env` field lets you pass environment variables like `GITHUB_TOKEN` to the server process.\n- Codex CLI can both **consume** MCP servers (as a client) and **be consumed** as an MCP server (acting as a server for other agents).\n- Make sure your Python MCP server implements the MCP protocol correctly (handles `initialize`, `tools/list`, `tools/call` methods over stdio).\n"}], "grading": {"expectations": [{"text": "Points to ~/.codex/config.toml for MCP server configuration", "passed": false, "evidence": "Uses '~/.codex/config.json' (line 11) \u00e2\u20ac\u201d wrong format. Codex CLI uses TOML (config.toml), not JSON."}, {"text": "Shows [mcp_servers.name] TOML section with command, args fields", "passed": false, "evidence": "Uses JSON 'mcpServers' key (line 20) instead of TOML [mcp_servers.name] sections. Entire config is in JSON format."}, {"text": "Shows $GITHUB_TOKEN or $ENV_VAR pattern for environment variable references", "passed": false, "evidence": "Hardcodes 'your-github-token-here' (line 25) instead of using the $GITHUB_TOKEN reference pattern that Codex supports for pulling from shell environment."}, {"text": "Shows 'codex mcp' command for running codex as an MCP server", "passed": false, "evidence": "Uses hallucinated 'codex --mcp-server' flag (line 98). The correct command is 'codex mcp' (a subcommand, not a flag)."}, {"text": "Uses correct Python command (python/python3) in the MCP server command field", "passed": true, "evidence": "Line 22: '\"command\": \"python\"'"}, {"text": "Does not invent non-existent config keys or MCP server fields", "passed": false, "evidence": "Hallucinated: JSON config format (mcpServers), --mcp-server flag, config.json file. Real format is TOML with [mcp_servers.name] sections."}], "summary": {"passed": 1, "failed": 5, "total": 6, "pass_rate": 0.167}, "claims": [{"claim": "Codex uses config.json for configuration", "type": "factual", "verified": false, "evidence": "Codex CLI uses ~/.codex/config.toml (TOML format)"}, {"claim": "codex --mcp-server starts MCP server mode", "type": "factual", "verified": false, "evidence": "Correct command is 'codex mcp' (subcommand)"}], "eval_feedback": {"suggestions": [], "overall": "Baseline heavily hallucinated \u00e2\u20ac\u201d wrong config format, wrong MCP command, hardcoded secrets"}}}, {"id": "eval-3-profiles-troubleshooting-with_skill", "prompt": "I'm getting Landlock errors on WSL2 when running codex. Also, I need to set up two profiles in config.toml \u00e2\u20ac\u201d one for safe daily dev and one for CI with no approvals. How do I fix the Landlock issue and configure the profiles?", "eval_id": 3, "outputs": [{"name": "response.md", "type": "text", "content": "# Fixing Landlock Errors on WSL2 and Setting Up Codex Profiles\n\n## Fixing the Landlock Error\n\nLandlock is a Linux kernel security feature that Codex uses for filesystem sandboxing. WSL2 kernels often ship with an older version that does not fully support Landlock, which causes Codex to fail when it tries to enforce sandbox restrictions.\n\n### Options to fix it\n\n**Option 1: Update your WSL2 kernel** (recommended long-term fix)\n\nUpdate WSL2 to get a kernel version that supports Landlock (5.13+, ideally 6.1+):\n\n```bash\n# From a Windows PowerShell (admin):\nwsl --update\n\n# Then restart WSL:\nwsl --shutdown\n```\n\nAfter restarting, verify the kernel version inside WSL:\n\n```bash\nuname -r\n```\n\nIf the version is 5.13 or higher, Landlock should work and the error should be resolved.\n\n**Option 2: Bypass the sandbox** (for isolated/trusted environments only)\n\nIf updating the kernel is not an option, you can bypass the sandbox entirely using the `--dangerously-bypass-approvals-and-sandbox` flag (aliased as `--yolo`):\n\n```bash\ncodex --dangerously-bypass-approvals-and-sandbox \"your task here\"\n```\n\nOnly use this in isolated environments (containers, throwaway VMs, CI runners) since it removes all filesystem and approval restrictions.\n\n**Option 3: Use `danger-full-access` sandbox mode**\n\nA slightly less drastic alternative is to use the unrestricted sandbox mode while keeping approval prompts active:\n\n```bash\ncodex -s danger-full-access \"your task here\"\n```\n\nThis avoids the Landlock syscall entirely while still requiring approval for sensitive actions.\n\n---\n\n## Setting Up Two Profiles in config.toml\n\nEdit (or create) `~/.codex/config.toml` and add the following:\n\n```toml\n# Default settings (used when no profile is specified)\nmodel = \"gpt-5-codex\"\napproval_policy = \"on-request\"\n\n[sandbox_workspace_write]\nnetwork_access = false\n\n# ----------------------------------------------------------\n# Profile: safe daily development\n# ----------------------------------------------------------\n[profiles.dev]\nmodel = \"gpt-5-codex\"\napproval_policy = \"on-request\"\n# This is equivalent to --full-auto: it will ask before\n# sensitive actions but auto-approve reads and workspace writes.\n\n# ----------------------------------------------------------\n# Profile: CI with no approvals\n# ----------------------------------------------------------\n[profiles.ci]\nmodel = \"gpt-5-codex\"\napproval_policy = \"never\"\n# \"never\" means Codex will not pause for any approval prompts,\n# making it suitable for fully automated CI pipelines.\n```\n\n### Using the profiles\n\n```bash\n# Daily development (safe, asks before sensitive actions)\ncodex --profile dev \"Refactor the auth module\"\n\n# CI pipeline (no approvals, fully automated)\ncodex --profile ci \"Run tests and fix failures\"\n\n# Short form\ncodex -p dev \"task\"\ncodex -p ci \"task\"\n```\n\n### Setting a default profile\n\nIf you want `dev` to be your default without typing `--profile` every time, add this at the top level of your config:\n\n```toml\nprofile = \"dev\"\n```\n\nThen you only need to explicitly pass `--profile ci` when running in CI.\n\n### GitHub Actions example using the CI profile\n\n```yaml\n- name: Run Codex\n run: |\n npm i -g @openai/codex\n codex exec --profile ci \"Update CHANGELOG for this release\"\n env:\n OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}\n```\n\n### Config precedence reminder\n\nCLI flags always override profile settings, which override user config defaults:\n\n1. CLI flags (`--model`, `-a`, `-c key=value`)\n2. Project config (`.codex/config.toml` in project root)\n3. User config (`~/.codex/config.toml`, including profiles)\n4. Built-in defaults\n\nSo even with a profile active, you can override individual settings on the command line, for example:\n\n```bash\ncodex --profile ci --model gpt-5-pro \"task\"\n```\n"}], "grading": {"expectations": [{"text": "Mentions updating WSL2 kernel or using --dangerously-bypass-approvals-and-sandbox (--yolo) as workarounds", "passed": true, "evidence": "Option 1: 'wsl --update' to update kernel. Option 2: '--dangerously-bypass-approvals-and-sandbox' with '(aliased as --yolo)'"}, {"text": "Shows correct [profiles.name] TOML syntax for defining profiles", "passed": true, "evidence": "Lines 66-79: '[profiles.dev]' and '[profiles.ci]' sections in config.toml"}, {"text": "CI profile uses approval_policy = 'never' for fully automated mode", "passed": true, "evidence": "Line 77: 'approval_policy = \"never\"'"}, {"text": "Dev profile uses a safe approval_policy like 'on-request' or 'untrusted'", "passed": true, "evidence": "Line 68: 'approval_policy = \"on-request\"'"}, {"text": "Shows --profile flag for selecting a profile at runtime", "passed": true, "evidence": "Lines 85-93: 'codex --profile dev' and 'codex -p dev'"}, {"text": "Does not invent non-existent troubleshooting steps or config options", "passed": true, "evidence": "All flags (--dangerously-bypass-approvals-and-sandbox, --yolo, -s danger-full-access, --profile, -p) and config keys (approval_policy, model, profile) match the skill documentation"}], "summary": {"passed": 6, "failed": 0, "total": 6, "pass_rate": 1.0}, "claims": [{"claim": "danger-full-access sandbox mode avoids Landlock syscall", "type": "factual", "verified": true, "evidence": "Reasonable inference \u00e2\u20ac\u201d danger-full-access provides unrestricted access per the skill doc"}], "eval_feedback": {"suggestions": [], "overall": "No suggestions, evals look solid"}}}, {"id": "eval-3-profiles-troubleshooting-without_skill", "prompt": "I'm getting Landlock errors on WSL2 when running codex. Also, I need to set up two profiles in config.toml \u00e2\u20ac\u201d one for safe daily dev and one for CI with no approvals. How do I fix the Landlock issue and configure the profiles?", "eval_id": 3, "outputs": [{"name": "response.md", "type": "text", "content": "# Fixing Landlock Errors on WSL2 and Configuring Codex Profiles\n\n## Part 1: Fixing Landlock Errors on WSL2\n\n### What is the problem?\n\nLandlock is a Linux kernel security module (available since kernel 5.13) that Codex CLI uses for sandboxing \u00e2\u20ac\u201d it restricts which files and directories a process can access. On WSL2, Landlock support depends on your WSL kernel version, and many WSL2 distributions ship with kernels that either do not support Landlock or have incomplete support, leading to errors when Codex tries to enable its sandbox.\n\nTypical error messages look like:\n\n```\nError: Landlock is not supported by the running kernel\n```\n\nor\n\n```\nLandlock sandbox initialization failed\n```\n\n### Solutions\n\n#### Option 1: Disable the sandbox (quickest fix)\n\nYou can disable Codex's sandboxing entirely by passing the `--no-sandbox` flag or by setting the `sandbox` option to `false` in your configuration:\n\n**Via CLI flag:**\n```bash\ncodex --no-sandbox \"your prompt here\"\n```\n\n**Via environment variable:**\n```bash\nexport CODEX_SANDBOX_TYPE=none\ncodex \"your prompt here\"\n```\n\n**Via config.toml** (see Part 2 below for file location):\n```toml\nsandbox = \"none\"\n```\n\n#### Option 2: Update your WSL2 kernel\n\nIf you want to keep sandboxing enabled, update your WSL kernel to a version that supports Landlock (5.13+, ideally 6.1+):\n\n1. Open PowerShell as Administrator on the Windows host:\n ```powershell\n wsl --update\n ```\n\n2. Restart WSL:\n ```powershell\n wsl --shutdown\n ```\n\n3. Verify the kernel version inside WSL:\n ```bash\n uname -r\n ```\n You need at least 5.13. WSL2 kernels based on 5.15 or 6.x should have Landlock support.\n\n4. Check Landlock availability:\n ```bash\n # Check if Landlock is enabled in the kernel\n cat /sys/kernel/security/lsm\n ```\n The output should include `landlock` in the comma-separated list. If it does not, your kernel was compiled without Landlock support.\n\n#### Option 3: Use a custom WSL kernel with Landlock enabled\n\nIf `wsl --update` does not give you a Landlock-capable kernel, you can compile a custom WSL kernel with Landlock enabled:\n\n1. Clone the WSL2 kernel source from Microsoft's GitHub repository.\n2. Enable `CONFIG_SECURITY_LANDLOCK=y` in the kernel config.\n3. Build and point WSL to the custom kernel via `.wslconfig`:\n\n In `%USERPROFILE%\\.wslconfig`:\n ```ini\n [wsl2]\n kernel=C:\\\\path\\\\to\\\\custom\\\\bzImage\n ```\n\n4. Restart WSL with `wsl --shutdown`.\n\nThis is an advanced approach and usually unnecessary if `wsl --update` brings you to a recent kernel.\n\n---\n\n## Part 2: Configuring Two Profiles in config.toml\n\nCodex CLI uses a TOML configuration file located at:\n\n```\n~/.codex/config.toml\n```\n\n(On some setups this may be at `~/.config/codex/config.toml` depending on the version.)\n\n### Profile structure\n\nCodex supports a default configuration section and named profiles. The default section applies when no profile is specified. Named profiles are defined under `[profile.<name>]` sections and override the defaults.\n\n### Configuration file with two profiles\n\n```toml\n# =============================================================\n# ~/.codex/config.toml\n# =============================================================\n\n# Default model used across all profiles\nmodel = \"o4-mini\"\n\n# Default approval mode\napproval_mode = \"suggest\"\n\n# =============================================================\n# Profile 1: \"dev\" \u00e2\u20ac\u201d Safe daily development\n# =============================================================\n[profile.dev]\nmodel = \"o4-mini\"\napproval_mode = \"suggest\"\nsandbox = \"permissive\"\n\n# In \"suggest\" mode, Codex will:\n# - Automatically approve read-only operations (file reads, searches)\n# - Prompt you for approval before any write operation (file edits,\n# shell commands that modify the filesystem)\n# - This is the safest interactive mode for daily development\n\n# \"permissive\" sandbox allows reads anywhere but restricts writes\n# to the current working directory and common temp directories.\n\n# =============================================================\n# Profile 2: \"ci\" \u00e2\u20ac\u201d CI/CD with no approvals needed\n# =============================================================\n[profile.ci]\nmodel = \"o4-mini\"\napproval_mode = \"full-auto\"\nsandbox = \"none\"\n\n# In \"full-auto\" mode, Codex will:\n# - Automatically approve ALL operations without prompting\n# - Execute read and write operations, shell commands, etc.\n# - Suitable for CI/CD pipelines where no human is present\n#\n# sandbox = \"none\" disables Landlock sandboxing entirely,\n# which avoids Landlock issues in CI environments (Docker,\n# WSL, older kernels) and removes filesystem restrictions.\n```\n\n### Using profiles\n\nTo run Codex with a specific profile, use the `--profile` flag:\n\n```bash\n# Daily development (safe mode with approvals)\ncodex --profile dev \"refactor the auth module\"\n\n# CI pipeline (no approvals, no sandbox)\ncodex --profile ci \"run the test suite and fix any failures\"\n```\n\n### Approval mode reference\n\n| Mode | Behavior |\n|------|----------|\n| `suggest` | Auto-approves reads; prompts for writes and commands |\n| `auto-edit` | Auto-approves reads and file edits; prompts for shell commands |\n| `full-auto` | Auto-approves everything \u00e2\u20ac\u201d no prompts at all |\n\n### Sandbox mode reference\n\n| Mode | Behavior |\n|------|----------|\n| `permissive` | Allows reads everywhere; restricts writes to cwd and temp dirs |\n| `none` | Disables sandboxing entirely (needed when Landlock is unavailable) |\n\n### CI/CD pipeline example\n\nIn a GitHub Actions workflow or similar CI environment:\n\n```yaml\n- name: Run Codex\n env:\n OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}\n run: |\n codex --profile ci \"run tests and report results\"\n```\n\nOr without profiles, using flags directly:\n\n```yaml\n- name: Run Codex\n env:\n OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}\n run: |\n codex --approval-mode full-auto --sandbox none \"run tests and report results\"\n```\n\n---\n\n## Summary\n\n| Problem | Solution |\n|---------|----------|\n| Landlock errors on WSL2 | Set `sandbox = \"none\"` in config or use `--no-sandbox`, or update the WSL kernel via `wsl --update` |\n| Safe daily dev profile | Use `approval_mode = \"suggest\"` with `sandbox = \"permissive\"` |\n| CI profile with no approvals | Use `approval_mode = \"full-auto\"` with `sandbox = \"none\"` |\n"}], "grading": {"expectations": [{"text": "Mentions updating WSL2 kernel or using --dangerously-bypass-approvals-and-sandbox (--yolo) as workarounds", "passed": false, "evidence": "Mentions 'wsl --update' (correct) but does NOT mention --dangerously-bypass-approvals-and-sandbox or --yolo. Instead invents '--no-sandbox' flag and 'CODEX_SANDBOX_TYPE=none' env var, neither of which exist."}, {"text": "Shows correct [profiles.name] TOML syntax for defining profiles", "passed": false, "evidence": "Uses '[profile.dev]' and '[profile.ci]' (singular 'profile') instead of the correct '[profiles.dev]' and '[profiles.ci]' (plural 'profiles')."}, {"text": "CI profile uses approval_policy = 'never' for fully automated mode", "passed": false, "evidence": "Uses 'approval_mode = \"full-auto\"' \u00e2\u20ac\u201d wrong key name (should be approval_policy) and wrong value (should be 'never'). 'full-auto' is a CLI shortcut flag, not an approval_policy value."}, {"text": "Dev profile uses a safe approval_policy like 'on-request' or 'untrusted'", "passed": false, "evidence": "Uses 'approval_mode = \"suggest\"' \u00e2\u20ac\u201d wrong key name (should be approval_policy) and 'suggest' is not a valid approval policy value. Valid values are: untrusted, on-request, never, reject."}, {"text": "Shows --profile flag for selecting a profile at runtime", "passed": true, "evidence": "Line 157: 'codex --profile dev' and 'codex --profile ci'"}, {"text": "Does not invent non-existent troubleshooting steps or config options", "passed": false, "evidence": "Hallucinated: '--no-sandbox' flag, 'CODEX_SANDBOX_TYPE=none' env var, 'sandbox = none/permissive' config keys, 'approval_mode' key (should be approval_policy), 'suggest'/'auto-edit'/'full-auto' as config values, '[profile.name]' syntax (should be [profiles.name])."}], "summary": {"passed": 1, "failed": 5, "total": 6, "pass_rate": 0.167}, "claims": [{"claim": "Approval modes are suggest, auto-edit, full-auto", "type": "factual", "verified": false, "evidence": "Real approval_policy values: untrusted, on-request, never, reject"}, {"claim": "--no-sandbox disables sandboxing", "type": "factual", "verified": false, "evidence": "No --no-sandbox flag exists. The bypass flag is --dangerously-bypass-approvals-and-sandbox (--yolo)"}, {"claim": "Profile sections use [profile.name] syntax", "type": "factual", "verified": false, "evidence": "Correct syntax is [profiles.name] (plural)"}], "eval_feedback": {"suggestions": [], "overall": "Baseline massively hallucinated \u00e2\u20ac\u201d wrong config keys, wrong values, invented flags and env vars"}}}], "previous_feedback": {}, "previous_outputs": {}, "benchmark": {"metadata": {"skill_name": "codex-cli", "skill_path": "src/skills/codex-cli/SKILL.md", "executor_model": "claude-opus-4-6", "timestamp": "2026-03-11T00:00:00Z", "evals_run": 3, "runs_per_configuration": 1}, "runs": [{"eval_id": 1, "eval_name": "ci-integration", "configuration": "with_skill", "result": {"pass_rate": 1.0, "passed": 6, "failed": 0, "total": 6}, "timing": {"total_tokens": 68449, "duration_ms": 60126}}, {"eval_id": 1, "eval_name": "ci-integration", "configuration": "without_skill", "result": {"pass_rate": 0.5, "passed": 3, "failed": 3, "total": 6}, "timing": {"total_tokens": 64960, "duration_ms": 44585}}, {"eval_id": 2, "eval_name": "mcp-server-config", "configuration": "with_skill", "result": {"pass_rate": 1.0, "passed": 6, "failed": 0, "total": 6}, "timing": {"total_tokens": 67753, "duration_ms": 38647}}, {"eval_id": 2, "eval_name": "mcp-server-config", "configuration": "without_skill", "result": {"pass_rate": 0.167, "passed": 1, "failed": 5, "total": 6}, "timing": {"total_tokens": 64950, "duration_ms": 41699}}, {"eval_id": 3, "eval_name": "profiles-troubleshooting", "configuration": "with_skill", "result": {"pass_rate": 1.0, "passed": 6, "failed": 0, "total": 6}, "timing": {"total_tokens": 67804, "duration_ms": 38914}}, {"eval_id": 3, "eval_name": "profiles-troubleshooting", "configuration": "without_skill", "result": {"pass_rate": 0.167, "passed": 1, "failed": 5, "total": 6}, "timing": {"total_tokens": 65148, "duration_ms": 51576}}], "run_summary": {"with_skill": {"mean_pass_rate": 1.0, "stddev_pass_rate": 0.0, "mean_tokens": 68002, "mean_duration_ms": 45896}, "without_skill": {"mean_pass_rate": 0.278, "stddev_pass_rate": 0.192, "mean_tokens": 65019, "mean_duration_ms": 45953}, "delta": {"pass_rate": "+72.2%", "tokens": "+4.6%", "duration": "-0.1%"}}, "notes": "The skill achieves 100% pass rate across all 3 evals vs 27.8% baseline. Without the skill, Claude heavily hallucinated CLI flags (--approval-mode, --sandbox write-only, --no-sandbox, --mcp-server), config format (JSON instead of TOML), config keys (approval_mode instead of approval_policy), and approval mode values (suggest/auto-edit/full-auto instead of untrusted/on-request/never/reject). The skill completely eliminates these hallucinations at negligible additional token cost."}};
650
-
651
- // ---- State ----
652
- let feedbackMap = {}; // run_id -> feedback text
653
- let currentIndex = 0;
654
- let visitedRuns = new Set();
655
-
656
- // ---- Init ----
657
- async function init() {
658
- // Load saved feedback from server — but only if this isn't a fresh
659
- // iteration (indicated by previous_feedback being present). When
660
- // previous feedback exists, the feedback.json on disk is stale from
661
- // the prior iteration and should not pre-fill the textareas.
662
- const hasPrevious = Object.keys(EMBEDDED_DATA.previous_feedback || {}).length > 0
663
- || Object.keys(EMBEDDED_DATA.previous_outputs || {}).length > 0;
664
- if (!hasPrevious) {
665
- try {
666
- const resp = await fetch("/api/feedback");
667
- const data = await resp.json();
668
- if (data.reviews) {
669
- for (const r of data.reviews) feedbackMap[r.run_id] = r.feedback;
670
- }
671
- } catch { /* first run, no feedback yet */ }
672
- }
673
-
674
- document.getElementById("skill-name").textContent = EMBEDDED_DATA.skill_name;
675
- showRun(0);
676
-
677
- // Wire up feedback auto-save
678
- const textarea = document.getElementById("feedback");
679
- let saveTimeout = null;
680
- textarea.addEventListener("input", () => {
681
- clearTimeout(saveTimeout);
682
- document.getElementById("feedback-status").textContent = "";
683
- saveTimeout = setTimeout(() => saveCurrentFeedback(), 800);
684
- });
685
- }
686
-
687
- // ---- Navigation ----
688
- function navigate(delta) {
689
- const newIndex = currentIndex + delta;
690
- if (newIndex >= 0 && newIndex < EMBEDDED_DATA.runs.length) {
691
- saveCurrentFeedback();
692
- showRun(newIndex);
693
- }
694
- }
695
-
696
- function updateNavButtons() {
697
- document.getElementById("prev-btn").disabled = currentIndex === 0;
698
- document.getElementById("next-btn").disabled =
699
- currentIndex === EMBEDDED_DATA.runs.length - 1;
700
- }
701
-
702
- // ---- Show a run ----
703
- function showRun(index) {
704
- currentIndex = index;
705
- const run = EMBEDDED_DATA.runs[index];
706
-
707
- // Progress
708
- document.getElementById("progress").textContent =
709
- `${index + 1} of ${EMBEDDED_DATA.runs.length}`;
710
-
711
- // Prompt
712
- document.getElementById("prompt-text").textContent = run.prompt;
713
-
714
- // Config badge
715
- const badge = document.getElementById("config-badge");
716
- const configMatch = run.id.match(/(with_skill|without_skill|new_skill|old_skill)/);
717
- if (configMatch) {
718
- const config = configMatch[1];
719
- const isBaseline = config === "without_skill" || config === "old_skill";
720
- badge.textContent = config.replace(/_/g, " ");
721
- badge.className = "config-badge " + (isBaseline ? "config-baseline" : "config-primary");
722
- badge.style.display = "inline-block";
723
- } else {
724
- badge.style.display = "none";
725
- }
726
-
727
- // Outputs
728
- renderOutputs(run);
729
-
730
- // Previous outputs
731
- renderPrevOutputs(run);
732
-
733
- // Grades
734
- renderGrades(run);
735
-
736
- // Previous feedback
737
- const prevFb = (EMBEDDED_DATA.previous_feedback || {})[run.id];
738
- const prevEl = document.getElementById("prev-feedback");
739
- if (prevFb) {
740
- document.getElementById("prev-feedback-text").textContent = prevFb;
741
- prevEl.style.display = "block";
742
- } else {
743
- prevEl.style.display = "none";
744
- }
745
-
746
- // Feedback
747
- document.getElementById("feedback").value = feedbackMap[run.id] || "";
748
- document.getElementById("feedback-status").textContent = "";
749
-
750
- updateNavButtons();
751
-
752
- // Track visited runs and promote done button when all visited
753
- visitedRuns.add(index);
754
- const doneBtn = document.getElementById("done-btn");
755
- if (visitedRuns.size >= EMBEDDED_DATA.runs.length) {
756
- doneBtn.classList.add("ready");
757
- }
758
-
759
- // Scroll main content to top
760
- document.querySelector(".main").scrollTop = 0;
761
- }
762
-
763
- // ---- Render outputs ----
764
- function renderOutputs(run) {
765
- const container = document.getElementById("outputs-body");
766
- container.innerHTML = "";
767
-
768
- const outputs = run.outputs || [];
769
- if (outputs.length === 0) {
770
- container.innerHTML = '<div class="empty-state">No output files</div>';
771
- return;
772
- }
773
-
774
- for (const file of outputs) {
775
- const fileDiv = document.createElement("div");
776
- fileDiv.className = "output-file";
777
-
778
- // Always show file header with download link
779
- const header = document.createElement("div");
780
- header.className = "output-file-header";
781
- const nameSpan = document.createElement("span");
782
- nameSpan.textContent = file.name;
783
- header.appendChild(nameSpan);
784
- const dlBtn = document.createElement("a");
785
- dlBtn.className = "dl-btn";
786
- dlBtn.textContent = "Download";
787
- dlBtn.download = file.name;
788
- dlBtn.href = getDownloadUri(file);
789
- header.appendChild(dlBtn);
790
- fileDiv.appendChild(header);
791
-
792
- const content = document.createElement("div");
793
- content.className = "output-file-content";
794
-
795
- if (file.type === "text") {
796
- const pre = document.createElement("pre");
797
- pre.textContent = file.content;
798
- content.appendChild(pre);
799
- } else if (file.type === "image") {
800
- const img = document.createElement("img");
801
- img.src = file.data_uri;
802
- img.alt = file.name;
803
- content.appendChild(img);
804
- } else if (file.type === "pdf") {
805
- const iframe = document.createElement("iframe");
806
- iframe.src = file.data_uri;
807
- content.appendChild(iframe);
808
- } else if (file.type === "xlsx") {
809
- renderXlsx(content, file.data_b64);
810
- } else if (file.type === "binary") {
811
- const a = document.createElement("a");
812
- a.className = "download-link";
813
- a.href = file.data_uri;
814
- a.download = file.name;
815
- a.textContent = "Download " + file.name;
816
- content.appendChild(a);
817
- } else if (file.type === "error") {
818
- const pre = document.createElement("pre");
819
- pre.textContent = file.content;
820
- pre.style.color = "var(--red)";
821
- content.appendChild(pre);
822
- }
823
-
824
- fileDiv.appendChild(content);
825
- container.appendChild(fileDiv);
826
- }
827
- }
828
-
829
- // ---- XLSX rendering via SheetJS ----
830
- function renderXlsx(container, b64Data) {
831
- try {
832
- const raw = Uint8Array.from(atob(b64Data), c => c.charCodeAt(0));
833
- const wb = XLSX.read(raw, { type: "array" });
834
-
835
- for (let i = 0; i < wb.SheetNames.length; i++) {
836
- const sheetName = wb.SheetNames[i];
837
- const ws = wb.Sheets[sheetName];
838
-
839
- if (wb.SheetNames.length > 1) {
840
- const sheetLabel = document.createElement("div");
841
- sheetLabel.style.cssText =
842
- "font-weight:600; font-size:0.8rem; color:#b0aea5; margin-top:0.5rem; margin-bottom:0.25rem;";
843
- sheetLabel.textContent = "Sheet: " + sheetName;
844
- container.appendChild(sheetLabel);
845
- }
846
-
847
- const htmlStr = XLSX.utils.sheet_to_html(ws, { editable: false });
848
- const wrapper = document.createElement("div");
849
- wrapper.innerHTML = htmlStr;
850
- container.appendChild(wrapper);
851
- }
852
- } catch (err) {
853
- container.textContent = "Error rendering spreadsheet: " + err.message;
854
- }
855
- }
856
-
857
- // ---- Grades ----
858
- function renderGrades(run) {
859
- const section = document.getElementById("grades-section");
860
- const content = document.getElementById("grades-content");
861
-
862
- if (!run.grading) {
863
- section.style.display = "none";
864
- return;
865
- }
866
-
867
- const grading = run.grading;
868
- section.style.display = "block";
869
- // Reset to collapsed
870
- content.classList.remove("open");
871
- document.getElementById("grades-arrow").classList.remove("open");
872
-
873
- const summary = grading.summary || {};
874
- const expectations = grading.expectations || [];
875
-
876
- let html = '<div style="padding: 1rem;">';
877
-
878
- // Summary line
879
- const passRate = summary.pass_rate != null
880
- ? Math.round(summary.pass_rate * 100) + "%"
881
- : "?";
882
- const badgeClass = summary.pass_rate >= 0.8 ? "grade-pass" : summary.pass_rate >= 0.5 ? "" : "grade-fail";
883
- html += '<div class="grades-summary">';
884
- html += '<span class="grade-badge ' + badgeClass + '">' + passRate + '</span>';
885
- html += '<span>' + (summary.passed || 0) + ' passed, ' + (summary.failed || 0) + ' failed of ' + (summary.total || 0) + '</span>';
886
- html += '</div>';
887
-
888
- // Assertions list
889
- html += '<ul class="assertion-list">';
890
- for (const exp of expectations) {
891
- const statusClass = exp.passed ? "pass" : "fail";
892
- const statusIcon = exp.passed ? "\u2713" : "\u2717";
893
- html += '<li class="assertion-item">';
894
- html += '<span class="assertion-status ' + statusClass + '">' + statusIcon + '</span>';
895
- html += '<span>' + escapeHtml(exp.text) + '</span>';
896
- if (exp.evidence) {
897
- html += '<div class="assertion-evidence">' + escapeHtml(exp.evidence) + '</div>';
898
- }
899
- html += '</li>';
900
- }
901
- html += '</ul>';
902
-
903
- html += '</div>';
904
- content.innerHTML = html;
905
- }
906
-
907
- function toggleGrades() {
908
- const content = document.getElementById("grades-content");
909
- const arrow = document.getElementById("grades-arrow");
910
- content.classList.toggle("open");
911
- arrow.classList.toggle("open");
912
- }
913
-
914
- // ---- Previous outputs (collapsible) ----
915
- function renderPrevOutputs(run) {
916
- const section = document.getElementById("prev-outputs-section");
917
- const content = document.getElementById("prev-outputs-content");
918
- const prevOutputs = (EMBEDDED_DATA.previous_outputs || {})[run.id];
919
-
920
- if (!prevOutputs || prevOutputs.length === 0) {
921
- section.style.display = "none";
922
- return;
923
- }
924
-
925
- section.style.display = "block";
926
- // Reset to collapsed
927
- content.classList.remove("open");
928
- document.getElementById("prev-outputs-arrow").classList.remove("open");
929
-
930
- // Render the files into the content area
931
- content.innerHTML = "";
932
- const wrapper = document.createElement("div");
933
- wrapper.style.padding = "1rem";
934
-
935
- for (const file of prevOutputs) {
936
- const fileDiv = document.createElement("div");
937
- fileDiv.className = "output-file";
938
-
939
- const header = document.createElement("div");
940
- header.className = "output-file-header";
941
- const nameSpan = document.createElement("span");
942
- nameSpan.textContent = file.name;
943
- header.appendChild(nameSpan);
944
- const dlBtn = document.createElement("a");
945
- dlBtn.className = "dl-btn";
946
- dlBtn.textContent = "Download";
947
- dlBtn.download = file.name;
948
- dlBtn.href = getDownloadUri(file);
949
- header.appendChild(dlBtn);
950
- fileDiv.appendChild(header);
951
-
952
- const fc = document.createElement("div");
953
- fc.className = "output-file-content";
954
-
955
- if (file.type === "text") {
956
- const pre = document.createElement("pre");
957
- pre.textContent = file.content;
958
- fc.appendChild(pre);
959
- } else if (file.type === "image") {
960
- const img = document.createElement("img");
961
- img.src = file.data_uri;
962
- img.alt = file.name;
963
- fc.appendChild(img);
964
- } else if (file.type === "pdf") {
965
- const iframe = document.createElement("iframe");
966
- iframe.src = file.data_uri;
967
- fc.appendChild(iframe);
968
- } else if (file.type === "xlsx") {
969
- renderXlsx(fc, file.data_b64);
970
- } else if (file.type === "binary") {
971
- const a = document.createElement("a");
972
- a.className = "download-link";
973
- a.href = file.data_uri;
974
- a.download = file.name;
975
- a.textContent = "Download " + file.name;
976
- fc.appendChild(a);
977
- }
978
-
979
- fileDiv.appendChild(fc);
980
- wrapper.appendChild(fileDiv);
981
- }
982
-
983
- content.appendChild(wrapper);
984
- }
985
-
986
- function togglePrevOutputs() {
987
- const content = document.getElementById("prev-outputs-content");
988
- const arrow = document.getElementById("prev-outputs-arrow");
989
- content.classList.toggle("open");
990
- arrow.classList.toggle("open");
991
- }
992
-
993
- // ---- Feedback (saved to server -> feedback.json) ----
994
- function saveCurrentFeedback() {
995
- const run = EMBEDDED_DATA.runs[currentIndex];
996
- const text = document.getElementById("feedback").value;
997
-
998
- if (text.trim() === "") {
999
- delete feedbackMap[run.id];
1000
- } else {
1001
- feedbackMap[run.id] = text;
1002
- }
1003
-
1004
- // Build reviews array from map
1005
- const reviews = [];
1006
- for (const [run_id, feedback] of Object.entries(feedbackMap)) {
1007
- if (feedback.trim()) {
1008
- reviews.push({ run_id, feedback, timestamp: new Date().toISOString() });
1009
- }
1010
- }
1011
-
1012
- fetch("/api/feedback", {
1013
- method: "POST",
1014
- headers: { "Content-Type": "application/json" },
1015
- body: JSON.stringify({ reviews, status: "in_progress" }),
1016
- }).then(() => {
1017
- document.getElementById("feedback-status").textContent = "Saved";
1018
- }).catch(() => {
1019
- // Static mode or server unavailable — no-op on auto-save,
1020
- // feedback will be downloaded on final submit
1021
- document.getElementById("feedback-status").textContent = "Will download on submit";
1022
- });
1023
- }
1024
-
1025
- // ---- Done ----
1026
- function showDoneDialog() {
1027
- // Save current textarea to feedbackMap (but don't POST yet)
1028
- const run = EMBEDDED_DATA.runs[currentIndex];
1029
- const text = document.getElementById("feedback").value;
1030
- if (text.trim() === "") {
1031
- delete feedbackMap[run.id];
1032
- } else {
1033
- feedbackMap[run.id] = text;
1034
- }
1035
-
1036
- // POST once with status: complete — include ALL runs so the model
1037
- // can distinguish "no feedback" (looks good) from "not reviewed"
1038
- const reviews = [];
1039
- const ts = new Date().toISOString();
1040
- for (const r of EMBEDDED_DATA.runs) {
1041
- reviews.push({ run_id: r.id, feedback: feedbackMap[r.id] || "", timestamp: ts });
1042
- }
1043
- const payload = JSON.stringify({ reviews, status: "complete" }, null, 2);
1044
- fetch("/api/feedback", {
1045
- method: "POST",
1046
- headers: { "Content-Type": "application/json" },
1047
- body: payload,
1048
- }).then(() => {
1049
- document.getElementById("done-overlay").classList.add("visible");
1050
- }).catch(() => {
1051
- // Server not available (static mode) — download as file
1052
- const blob = new Blob([payload], { type: "application/json" });
1053
- const url = URL.createObjectURL(blob);
1054
- const a = document.createElement("a");
1055
- a.href = url;
1056
- a.download = "feedback.json";
1057
- a.click();
1058
- URL.revokeObjectURL(url);
1059
- document.getElementById("done-overlay").classList.add("visible");
1060
- });
1061
- }
1062
-
1063
- function closeDoneDialog() {
1064
- // Reset status back to in_progress
1065
- saveCurrentFeedback();
1066
- document.getElementById("done-overlay").classList.remove("visible");
1067
- }
1068
-
1069
- // ---- Toast ----
1070
- function showToast(message) {
1071
- const toast = document.getElementById("toast");
1072
- toast.textContent = message;
1073
- toast.classList.add("visible");
1074
- setTimeout(() => toast.classList.remove("visible"), 2000);
1075
- }
1076
-
1077
- // ---- Keyboard nav ----
1078
- document.addEventListener("keydown", (e) => {
1079
- // Don't capture when typing in textarea
1080
- if (e.target.tagName === "TEXTAREA") return;
1081
-
1082
- if (e.key === "ArrowLeft" || e.key === "ArrowUp") {
1083
- e.preventDefault();
1084
- navigate(-1);
1085
- } else if (e.key === "ArrowRight" || e.key === "ArrowDown") {
1086
- e.preventDefault();
1087
- navigate(1);
1088
- }
1089
- });
1090
-
1091
- // ---- Util ----
1092
- function getDownloadUri(file) {
1093
- if (file.data_uri) return file.data_uri;
1094
- if (file.data_b64) return "data:application/octet-stream;base64," + file.data_b64;
1095
- if (file.type === "text") return "data:text/plain;charset=utf-8," + encodeURIComponent(file.content);
1096
- return "#";
1097
- }
1098
-
1099
- function escapeHtml(text) {
1100
- const div = document.createElement("div");
1101
- div.textContent = text;
1102
- return div.innerHTML;
1103
- }
1104
-
1105
- // ---- View switching ----
1106
- function switchView(view) {
1107
- document.querySelectorAll(".view-tab").forEach(t => t.classList.remove("active"));
1108
- document.querySelectorAll(".view-panel").forEach(p => p.classList.remove("active"));
1109
- document.querySelector(`[onclick="switchView('${view}')"]`).classList.add("active");
1110
- document.getElementById("panel-" + view).classList.add("active");
1111
- }
1112
-
1113
- // ---- Benchmark rendering ----
1114
- function renderBenchmark() {
1115
- const data = EMBEDDED_DATA.benchmark;
1116
- if (!data) return;
1117
-
1118
- // Show the tabs
1119
- document.getElementById("view-tabs").style.display = "flex";
1120
-
1121
- const container = document.getElementById("benchmark-content");
1122
- const summary = data.run_summary || {};
1123
- const metadata = data.metadata || {};
1124
- const notes = data.notes || [];
1125
-
1126
- let html = "";
1127
-
1128
- // Header
1129
- html += "<h2 style='font-family: Poppins, sans-serif; margin-bottom: 0.5rem;'>Benchmark Results</h2>";
1130
- html += "<p style='color: var(--text-muted); font-size: 0.875rem; margin-bottom: 1.25rem;'>";
1131
- if (metadata.skill_name) html += "<strong>" + escapeHtml(metadata.skill_name) + "</strong> &mdash; ";
1132
- if (metadata.timestamp) html += metadata.timestamp + " &mdash; ";
1133
- if (metadata.evals_run) html += "Evals: " + metadata.evals_run.join(", ") + " &mdash; ";
1134
- html += (metadata.runs_per_configuration || "?") + " runs per configuration";
1135
- html += "</p>";
1136
-
1137
- // Summary table
1138
- html += '<table class="benchmark-table">';
1139
-
1140
- function fmtStat(stat, pct) {
1141
- if (!stat) return "—";
1142
- const suffix = pct ? "%" : "";
1143
- const m = pct ? (stat.mean * 100).toFixed(0) : stat.mean.toFixed(1);
1144
- const s = pct ? (stat.stddev * 100).toFixed(0) : stat.stddev.toFixed(1);
1145
- return m + suffix + " ± " + s + suffix;
1146
- }
1147
-
1148
- function deltaClass(val) {
1149
- if (!val) return "";
1150
- const n = parseFloat(val);
1151
- if (n > 0) return "benchmark-delta-positive";
1152
- if (n < 0) return "benchmark-delta-negative";
1153
- return "";
1154
- }
1155
-
1156
- // Discover config names dynamically (everything except "delta")
1157
- const configs = Object.keys(summary).filter(k => k !== "delta");
1158
- const configA = configs[0] || "config_a";
1159
- const configB = configs[1] || "config_b";
1160
- const labelA = configA.replace(/_/g, " ").replace(/\b\w/g, c => c.toUpperCase());
1161
- const labelB = configB.replace(/_/g, " ").replace(/\b\w/g, c => c.toUpperCase());
1162
- const a = summary[configA] || {};
1163
- const b = summary[configB] || {};
1164
- const delta = summary.delta || {};
1165
-
1166
- html += "<thead><tr><th>Metric</th><th>" + escapeHtml(labelA) + "</th><th>" + escapeHtml(labelB) + "</th><th>Delta</th></tr></thead>";
1167
- html += "<tbody>";
1168
-
1169
- html += "<tr><td><strong>Pass Rate</strong></td>";
1170
- html += "<td>" + fmtStat(a.pass_rate, true) + "</td>";
1171
- html += "<td>" + fmtStat(b.pass_rate, true) + "</td>";
1172
- html += '<td class="' + deltaClass(delta.pass_rate) + '">' + (delta.pass_rate || "—") + "</td></tr>";
1173
-
1174
- // Time (only show row if data exists)
1175
- if (a.time_seconds || b.time_seconds) {
1176
- html += "<tr><td><strong>Time (s)</strong></td>";
1177
- html += "<td>" + fmtStat(a.time_seconds, false) + "</td>";
1178
- html += "<td>" + fmtStat(b.time_seconds, false) + "</td>";
1179
- html += '<td class="' + deltaClass(delta.time_seconds) + '">' + (delta.time_seconds ? delta.time_seconds + "s" : "—") + "</td></tr>";
1180
- }
1181
-
1182
- // Tokens (only show row if data exists)
1183
- if (a.tokens || b.tokens) {
1184
- html += "<tr><td><strong>Tokens</strong></td>";
1185
- html += "<td>" + fmtStat(a.tokens, false) + "</td>";
1186
- html += "<td>" + fmtStat(b.tokens, false) + "</td>";
1187
- html += '<td class="' + deltaClass(delta.tokens) + '">' + (delta.tokens || "—") + "</td></tr>";
1188
- }
1189
-
1190
- html += "</tbody></table>";
1191
-
1192
- // Per-eval breakdown (if runs data available)
1193
- const runs = data.runs || [];
1194
- if (runs.length > 0) {
1195
- const evalIds = [...new Set(runs.map(r => r.eval_id))].sort((a, b) => a - b);
1196
-
1197
- html += "<h3 style='font-family: Poppins, sans-serif; margin-bottom: 0.75rem;'>Per-Eval Breakdown</h3>";
1198
-
1199
- const hasTime = runs.some(r => r.result && r.result.time_seconds != null);
1200
- const hasErrors = runs.some(r => r.result && r.result.errors > 0);
1201
-
1202
- for (const evalId of evalIds) {
1203
- const evalRuns = runs.filter(r => r.eval_id === evalId);
1204
- const evalName = evalRuns[0] && evalRuns[0].eval_name ? evalRuns[0].eval_name : "Eval " + evalId;
1205
-
1206
- html += "<h4 style='font-family: Poppins, sans-serif; margin: 1rem 0 0.5rem; color: var(--text);'>" + escapeHtml(evalName) + "</h4>";
1207
- html += '<table class="benchmark-table">';
1208
- html += "<thead><tr><th>Config</th><th>Run</th><th>Pass Rate</th>";
1209
- if (hasTime) html += "<th>Time (s)</th>";
1210
- if (hasErrors) html += "<th>Crashes During Execution</th>";
1211
- html += "</tr></thead>";
1212
- html += "<tbody>";
1213
-
1214
- // Group by config and render with average rows
1215
- const configGroups = [...new Set(evalRuns.map(r => r.configuration))];
1216
- for (let ci = 0; ci < configGroups.length; ci++) {
1217
- const config = configGroups[ci];
1218
- const configRuns = evalRuns.filter(r => r.configuration === config);
1219
- if (configRuns.length === 0) continue;
1220
-
1221
- const rowClass = ci === 0 ? "benchmark-row-with" : "benchmark-row-without";
1222
- const configLabel = config.replace(/_/g, " ").replace(/\b\w/g, c => c.toUpperCase());
1223
-
1224
- for (const run of configRuns) {
1225
- const r = run.result || {};
1226
- const prClass = r.pass_rate >= 0.8 ? "benchmark-delta-positive" : r.pass_rate < 0.5 ? "benchmark-delta-negative" : "";
1227
- html += '<tr class="' + rowClass + '">';
1228
- html += "<td>" + configLabel + "</td>";
1229
- html += "<td>" + run.run_number + "</td>";
1230
- html += '<td class="' + prClass + '">' + ((r.pass_rate || 0) * 100).toFixed(0) + "% (" + (r.passed || 0) + "/" + (r.total || 0) + ")</td>";
1231
- if (hasTime) html += "<td>" + (r.time_seconds != null ? r.time_seconds.toFixed(1) : "—") + "</td>";
1232
- if (hasErrors) html += "<td>" + (r.errors || 0) + "</td>";
1233
- html += "</tr>";
1234
- }
1235
-
1236
- // Average row
1237
- const rates = configRuns.map(r => (r.result || {}).pass_rate || 0);
1238
- const avgRate = rates.reduce((a, b) => a + b, 0) / rates.length;
1239
- const avgPrClass = avgRate >= 0.8 ? "benchmark-delta-positive" : avgRate < 0.5 ? "benchmark-delta-negative" : "";
1240
- html += '<tr class="benchmark-row-avg ' + rowClass + '">';
1241
- html += "<td>" + configLabel + "</td>";
1242
- html += "<td>Avg</td>";
1243
- html += '<td class="' + avgPrClass + '">' + (avgRate * 100).toFixed(0) + "%</td>";
1244
- if (hasTime) {
1245
- const times = configRuns.map(r => (r.result || {}).time_seconds).filter(t => t != null);
1246
- html += "<td>" + (times.length ? (times.reduce((a, b) => a + b, 0) / times.length).toFixed(1) : "—") + "</td>";
1247
- }
1248
- if (hasErrors) html += "<td></td>";
1249
- html += "</tr>";
1250
- }
1251
- html += "</tbody></table>";
1252
-
1253
- // Per-assertion detail for this eval
1254
- const runsWithExpectations = {};
1255
- for (const config of configGroups) {
1256
- runsWithExpectations[config] = evalRuns.filter(r => r.configuration === config && r.expectations && r.expectations.length > 0);
1257
- }
1258
- const hasAnyExpectations = Object.values(runsWithExpectations).some(runs => runs.length > 0);
1259
- if (hasAnyExpectations) {
1260
- // Collect all unique assertion texts across all configs
1261
- const allAssertions = [];
1262
- const seen = new Set();
1263
- for (const config of configGroups) {
1264
- for (const run of runsWithExpectations[config]) {
1265
- for (const exp of (run.expectations || [])) {
1266
- if (!seen.has(exp.text)) {
1267
- seen.add(exp.text);
1268
- allAssertions.push(exp.text);
1269
- }
1270
- }
1271
- }
1272
- }
1273
-
1274
- html += '<table class="benchmark-table" style="margin-top: 0.5rem;">';
1275
- html += "<thead><tr><th>Assertion</th>";
1276
- for (const config of configGroups) {
1277
- const label = config.replace(/_/g, " ").replace(/\b\w/g, c => c.toUpperCase());
1278
- html += "<th>" + escapeHtml(label) + "</th>";
1279
- }
1280
- html += "</tr></thead><tbody>";
1281
-
1282
- for (const assertionText of allAssertions) {
1283
- html += "<tr><td>" + escapeHtml(assertionText) + "</td>";
1284
-
1285
- for (const config of configGroups) {
1286
- html += "<td>";
1287
- for (const run of runsWithExpectations[config]) {
1288
- const exp = (run.expectations || []).find(e => e.text === assertionText);
1289
- if (exp) {
1290
- const cls = exp.passed ? "benchmark-delta-positive" : "benchmark-delta-negative";
1291
- const icon = exp.passed ? "\u2713" : "\u2717";
1292
- html += '<span class="' + cls + '" title="Run ' + run.run_number + ': ' + escapeHtml(exp.evidence || "") + '">' + icon + "</span> ";
1293
- } else {
1294
- html += "— ";
1295
- }
1296
- }
1297
- html += "</td>";
1298
- }
1299
- html += "</tr>";
1300
- }
1301
- html += "</tbody></table>";
1302
- }
1303
- }
1304
- }
1305
-
1306
- // Notes
1307
- if (notes.length > 0) {
1308
- html += '<div class="benchmark-notes">';
1309
- html += "<h3>Analysis Notes</h3>";
1310
- html += "<ul>";
1311
- for (const note of notes) {
1312
- html += "<li>" + escapeHtml(note) + "</li>";
1313
- }
1314
- html += "</ul></div>";
1315
- }
1316
-
1317
- container.innerHTML = html;
1318
- }
1319
-
1320
- // ---- Start ----
1321
- init();
1322
- renderBenchmark();
1323
- </script>
1324
- </body>
1325
- </html>