@hassangameryt/freeflow 0.3.0 → 0.5.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 (692) hide show
  1. package/.claude-plugin/marketplace.json +4 -4
  2. package/.claude-plugin/plugin.json +6 -2
  3. package/.codex-plugin/plugin.json +9 -9
  4. package/README.md +190 -127
  5. package/assets/feedback-loop.png +0 -0
  6. package/assets/freeflow_workflow_map.png +0 -0
  7. package/assets/workflow.png +0 -0
  8. package/{skills → capabilities}/output-router/SKILL.md +21 -17
  9. package/{skills → capabilities}/output-router/references/safety-policy.md +1 -1
  10. package/capabilities/output-router/references/setup.md +110 -0
  11. package/command-surface.json +62 -7
  12. package/hooks/freeflow-runtime-context.mjs +572 -154
  13. package/hooks/hooks.json +26 -1
  14. package/hooks/tests/check-runtime-context-hook.sh +375 -0
  15. package/package.json +24 -24
  16. package/pi-extension/dist/index.js +264 -80
  17. package/pi-extension/dist/integrations/mcp-config.js +29 -0
  18. package/pi-extension/dist/output-router/host-producer-identification.js +232 -0
  19. package/pi-extension/dist/output-router/native-safety-net.js +216 -0
  20. package/pi-extension/dist/output-router/observed-tool-routing.js +122 -0
  21. package/pi-extension/dist/output-router/renderers.js +1023 -0
  22. package/pi-extension/dist/output-router/router-tools.js +554 -0
  23. package/pi-extension/dist/output-router/schemas.js +383 -0
  24. package/pi-extension/dist/output-router/status.js +744 -0
  25. package/pi-extension/dist/output-router/utils.js +502 -0
  26. package/pi-extension/dist/runtime/runtime-context.js +798 -0
  27. package/pi-extension/dist/settings/settings-tui.js +314 -0
  28. package/pi-extension/dist/settings/settings-ui.js +1487 -0
  29. package/pi-extension/freeflow/index.js +1 -0
  30. package/pi-extension/src/index.ts +230 -56
  31. package/pi-extension/src/{host-producer-identification.ts → output-router/host-producer-identification.ts} +37 -7
  32. package/pi-extension/src/{native-safety-net.ts → output-router/native-safety-net.ts} +8 -12
  33. package/pi-extension/src/{observed-tool-routing.ts → output-router/observed-tool-routing.ts} +19 -11
  34. package/pi-extension/src/{renderers.ts → output-router/renderers.ts} +246 -55
  35. package/pi-extension/src/{router-tools.ts → output-router/router-tools.ts} +130 -29
  36. package/pi-extension/src/{schemas.ts → output-router/schemas.ts} +171 -43
  37. package/pi-extension/src/output-router/status.ts +791 -0
  38. package/pi-extension/src/{utils.ts → output-router/utils.ts} +80 -18
  39. package/pi-extension/src/runtime/runtime-context.ts +895 -0
  40. package/pi-extension/src/settings/settings-tui.ts +408 -0
  41. package/pi-extension/src/settings/settings-ui.ts +1730 -0
  42. package/pi-extension/tests/integration/pi-extension.test.js +4020 -0
  43. package/pi-extension/tests/output-router/observed-producers.test.js +132 -0
  44. package/pi-extension/tests/output-router/search.test.js +722 -0
  45. package/pi-extension/tests/settings/settings-tui.test.js +243 -0
  46. package/router/dist/benchmarks/benchmark-harness.d.ts +16 -13
  47. package/router/dist/benchmarks/benchmark-harness.js +62 -62
  48. package/router/dist/benchmarks/benchmarks.d.ts +80 -73
  49. package/router/dist/benchmarks/benchmarks.js +860 -761
  50. package/router/dist/benchmarks/codex-qa-benchmarks.d.ts +71 -67
  51. package/router/dist/benchmarks/codex-qa-benchmarks.js +429 -390
  52. package/router/dist/benchmarks/command-benchmarks.d.ts +107 -92
  53. package/router/dist/benchmarks/command-benchmarks.js +616 -571
  54. package/router/dist/benchmarks/context-mode-normalized-benchmarks.d.ts +64 -56
  55. package/router/dist/benchmarks/context-mode-normalized-benchmarks.js +679 -613
  56. package/router/dist/benchmarks/context-mode-real-deep-benchmark.d.ts +103 -95
  57. package/router/dist/benchmarks/context-mode-real-deep-benchmark.js +1188 -1103
  58. package/router/dist/benchmarks/context-saving-baseline.d.ts +47 -41
  59. package/router/dist/benchmarks/context-saving-baseline.js +405 -374
  60. package/router/dist/benchmarks/index-benchmarks.d.ts +74 -69
  61. package/router/dist/benchmarks/index-benchmarks.js +761 -661
  62. package/router/dist/benchmarks/storage-policy-benchmarks.d.ts +80 -69
  63. package/router/dist/benchmarks/storage-policy-benchmarks.js +549 -469
  64. package/router/dist/benchmarks/vault-index-benchmarks.d.ts +43 -34
  65. package/router/dist/benchmarks/vault-index-benchmarks.js +294 -229
  66. package/router/dist/config/config.d.ts +62 -55
  67. package/router/dist/config/config.js +552 -373
  68. package/router/dist/config/router-contract.d.ts +2 -2
  69. package/router/dist/config/router-contract.js +38 -38
  70. package/router/dist/config/schema.d.ts +19 -10
  71. package/router/dist/config/schema.js +586 -538
  72. package/router/dist/config/types.d.ts +413 -361
  73. package/router/dist/config/types.js +19 -8
  74. package/router/dist/evidence/bounded-evidence.d.ts +26 -26
  75. package/router/dist/evidence/bounded-evidence.js +265 -252
  76. package/router/dist/evidence/evidence-range-selector.d.ts +16 -14
  77. package/router/dist/evidence/evidence-range-selector.js +192 -191
  78. package/router/dist/evidence/evidence-search.d.ts +18 -16
  79. package/router/dist/evidence/evidence-search.js +401 -377
  80. package/router/dist/evidence/evidence.d.ts +17 -17
  81. package/router/dist/evidence/evidence.js +163 -159
  82. package/router/dist/evidence/failure-contracts.d.ts +25 -15
  83. package/router/dist/evidence/failure-contracts.js +132 -126
  84. package/router/dist/evidence/line-ranges.d.ts +17 -15
  85. package/router/dist/evidence/line-ranges.js +18 -18
  86. package/router/dist/experiments/local-index.d.ts +47 -41
  87. package/router/dist/experiments/local-index.js +477 -458
  88. package/router/dist/local/local-traversal.d.ts +18 -0
  89. package/router/dist/local/local-traversal.js +334 -0
  90. package/router/dist/processing/engine.d.ts +94 -70
  91. package/router/dist/processing/engine.js +462 -364
  92. package/router/dist/processing/reducers.d.ts +208 -194
  93. package/router/dist/processing/reducers.js +1434 -1256
  94. package/router/dist/processing/renderers.d.ts +24 -18
  95. package/router/dist/processing/renderers.js +161 -152
  96. package/router/dist/processing/scripts.d.ts +80 -67
  97. package/router/dist/processing/scripts.js +343 -290
  98. package/router/dist/repo/repo-traversal.d.ts +9 -9
  99. package/router/dist/repo/repo-traversal.js +205 -202
  100. package/router/dist/routing/observed-reducers.d.ts +25 -19
  101. package/router/dist/routing/observed-reducers.js +559 -545
  102. package/router/dist/routing/observed-routing.d.ts +23 -14
  103. package/router/dist/routing/observed-routing.js +319 -318
  104. package/router/dist/routing/parsers.d.ts +10 -10
  105. package/router/dist/routing/parsers.js +454 -406
  106. package/router/dist/routing/run-filters.d.ts +45 -41
  107. package/router/dist/routing/run-filters.js +215 -207
  108. package/router/dist/routing/run-reducers.d.ts +45 -26
  109. package/router/dist/routing/run-reducers.js +102 -90
  110. package/router/dist/sandbox/adapter-roots.d.ts +34 -26
  111. package/router/dist/sandbox/adapter-roots.js +43 -41
  112. package/router/dist/sandbox/eryx-python-adapter.d.ts +9 -5
  113. package/router/dist/sandbox/eryx-python-adapter.js +659 -588
  114. package/router/dist/sandbox/jq-wasm-adapter.d.ts +3 -3
  115. package/router/dist/sandbox/jq-wasm-adapter.js +319 -311
  116. package/router/dist/sandbox/quickjs-wasi-adapter.d.ts +9 -5
  117. package/router/dist/sandbox/quickjs-wasi-adapter.js +336 -333
  118. package/router/dist/sandbox/script-sandbox.d.ts +105 -84
  119. package/router/dist/sandbox/script-sandbox.js +294 -260
  120. package/router/dist/setup/script-transform-adapters.d.ts +23 -19
  121. package/router/dist/setup/script-transform-adapters.js +223 -204
  122. package/router/dist/tools/batch.d.ts +27 -16
  123. package/router/dist/tools/batch.js +493 -416
  124. package/router/dist/tools/run.d.ts +47 -35
  125. package/router/dist/tools/run.js +1172 -1087
  126. package/router/dist/tools/search.d.ts +53 -31
  127. package/router/dist/tools/search.js +1224 -1065
  128. package/router/dist/transform/engine.d.ts +121 -96
  129. package/router/dist/transform/engine.js +1853 -1736
  130. package/router/dist/vault/vault-index.d.ts +97 -78
  131. package/router/dist/vault/vault-index.js +347 -342
  132. package/router/dist/vault/vault.d.ts +109 -74
  133. package/router/dist/vault/vault.js +528 -533
  134. package/runtime/interaction-contract.md +16 -0
  135. package/skills/bypass/SKILL.md +52 -38
  136. package/skills/commit-work/SKILL.md +54 -46
  137. package/skills/commit-work/references/staging-decisions.md +46 -24
  138. package/skills/decision-gate/SKILL.md +48 -0
  139. package/skills/design-for-depth/SKILL.md +75 -59
  140. package/skills/design-for-depth/references/design-pressure-signals.md +56 -7
  141. package/skills/design-for-depth/references/interface-design-loop.md +156 -0
  142. package/skills/design-for-depth/references/software-design-philosophy.md +32 -3
  143. package/skills/diagnose-failure/SKILL.md +65 -60
  144. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +36 -0
  145. package/skills/diagnose-failure/references/flaky-and-performance.md +30 -18
  146. package/skills/discuss/SKILL.md +121 -0
  147. package/skills/discuss/references/checkpoints.md +66 -0
  148. package/skills/evaluate-skill/SKILL.md +62 -53
  149. package/skills/evaluate-skill/references/definition-schema.md +345 -0
  150. package/skills/evaluate-skill/references/evaluation-design.md +86 -0
  151. package/skills/evaluate-skill/references/execution-and-evidence.md +85 -0
  152. package/skills/evaluate-skill/references/review-and-revision.md +51 -0
  153. package/skills/evaluate-skill/scripts/lib/definitions.mjs +296 -0
  154. package/skills/evaluate-skill/scripts/lib/evidence.mjs +46 -0
  155. package/skills/evaluate-skill/scripts/lib/grade-comparison.mjs +60 -0
  156. package/skills/evaluate-skill/scripts/lib/grade-interaction.mjs +148 -0
  157. package/skills/evaluate-skill/scripts/lib/grade-support.mjs +126 -0
  158. package/skills/evaluate-skill/scripts/lib/grade-workspace.mjs +192 -0
  159. package/skills/evaluate-skill/scripts/lib/grade.mjs +56 -0
  160. package/skills/evaluate-skill/scripts/lib/pi.mjs +489 -0
  161. package/skills/evaluate-skill/scripts/lib/process.mjs +79 -0
  162. package/skills/evaluate-skill/scripts/lib/rpc.mjs +444 -0
  163. package/skills/evaluate-skill/scripts/lib/runner.mjs +346 -0
  164. package/skills/evaluate-skill/scripts/lib/sandbox.mjs +409 -0
  165. package/skills/evaluate-skill/scripts/lib/view.mjs +337 -0
  166. package/skills/evaluate-skill/scripts/pi-guard.mjs +164 -0
  167. package/skills/evaluate-skill/scripts/skill-eval.mjs +101 -0
  168. package/skills/execute-work/SKILL.md +99 -0
  169. package/skills/execute-work/references/code-practices.md +78 -0
  170. package/skills/execute-work/references/execution-loop.md +77 -0
  171. package/skills/finish-branch/SKILL.md +117 -0
  172. package/skills/finish-branch/references/integration-options.md +80 -0
  173. package/skills/handoff/SKILL.md +65 -39
  174. package/skills/handoff/references/templates.md +104 -27
  175. package/skills/launch-work/SKILL.md +116 -0
  176. package/skills/launch-work/references/launch-readiness.md +85 -0
  177. package/skills/migration-work/SKILL.md +115 -0
  178. package/skills/migration-work/references/migration-lifecycle.md +82 -0
  179. package/skills/mode-contract/SKILL.md +72 -52
  180. package/skills/release-work/SKILL.md +118 -0
  181. package/skills/release-work/references/release-evidence.md +78 -0
  182. package/skills/review-artifact/SKILL.md +77 -69
  183. package/skills/review-artifact/references/reviewer-prompt.md +133 -42
  184. package/skills/review-work/SKILL.md +82 -57
  185. package/skills/review-work/references/reviewer-prompt.md +97 -60
  186. package/skills/review-work/references/security-risk-lens.md +67 -0
  187. package/skills/setup-freeflow/SKILL.md +54 -68
  188. package/skills/setup-freeflow/references/activation-contract.md +84 -69
  189. package/skills/setup-freeflow/references/host-setup.md +50 -17
  190. package/skills/simplify-code/SKILL.md +80 -0
  191. package/skills/simplify-code/references/simplification-patterns.md +70 -0
  192. package/skills/tdd/SKILL.md +96 -0
  193. package/skills/tdd/references/test-design.md +96 -0
  194. package/skills/track-work/SKILL.md +84 -0
  195. package/skills/track-work/references/working-record-schema.md +378 -0
  196. package/skills/verify-work/SKILL.md +59 -38
  197. package/skills/verify-work/references/browser-runtime-evidence.md +55 -0
  198. package/skills/verify-work/references/integration-evidence.md +51 -0
  199. package/skills/verify-work/references/performance-evidence.md +57 -0
  200. package/skills/workflow/SKILL.md +53 -59
  201. package/skills/workflow/references/domain-skill-composition.md +57 -0
  202. package/skills/workflow/references/workflow-loop.md +92 -0
  203. package/skills/write-plan/SKILL.md +85 -58
  204. package/skills/write-plan/references/plan-shapes.md +132 -46
  205. package/skills/write-skill/SKILL.md +82 -51
  206. package/skills/write-skill/references/activation-boundaries.md +62 -0
  207. package/skills/write-skill/references/agent-first-instructions.md +87 -0
  208. package/skills/write-skill/references/progressive-disclosure.md +63 -0
  209. package/skills/write-skill/scripts/lib/skill-author.mjs +653 -0
  210. package/skills/write-skill/scripts/skill-author.mjs +104 -0
  211. package/skills/write-spec/SKILL.md +65 -54
  212. package/skills/write-spec/references/artifact-standards.md +60 -41
  213. package/skills/write-spec/references/decision-records.md +77 -0
  214. package/skills/write-spec/references/spec-shapes.md +119 -60
  215. package/evals/README.md +0 -32
  216. package/evals/fixtures/copy-conventions.md +0 -31
  217. package/evals/fixtures/output-router/README.md +0 -12
  218. package/evals/fixtures/output-router/failed-command-output.txt +0 -9
  219. package/evals/fixtures/output-router/large-router-manual.md +0 -63
  220. package/evals/fixtures/output-router/native-large-output.txt +0 -40
  221. package/evals/fixtures/output-router/noisy-test-output.txt +0 -30
  222. package/evals/fixtures/output-router/verification-output.txt +0 -8
  223. package/evals/fixtures/tiny-artifact-review-app/README.md +0 -5
  224. package/evals/fixtures/tiny-artifact-review-app/docs/billing-policy.md +0 -10
  225. package/evals/fixtures/tiny-artifact-review-app/docs/specs/billing-webhook-api-draft.md +0 -21
  226. package/evals/fixtures/tiny-artifact-review-app/docs/specs/failed-payment-grace-period.md +0 -24
  227. package/evals/fixtures/tiny-artifact-review-app/docs/specs/immediate-downgrade.md +0 -17
  228. package/evals/fixtures/tiny-artifact-review-app/tests/billing.test.md +0 -8
  229. package/evals/fixtures/tiny-commit-ambiguous-app/.freeflow-eval-setup.sh +0 -15
  230. package/evals/fixtures/tiny-commit-ambiguous-app/README.md +0 -3
  231. package/evals/fixtures/tiny-commit-ambiguous-app/src/billing.ts +0 -5
  232. package/evals/fixtures/tiny-commit-ambiguous-app/src/settings-page.ts +0 -1
  233. package/evals/fixtures/tiny-commit-ambiguous-app/tests/settings.test.md +0 -5
  234. package/evals/fixtures/tiny-commit-app/.freeflow-eval-setup.sh +0 -14
  235. package/evals/fixtures/tiny-commit-app/README.md +0 -3
  236. package/evals/fixtures/tiny-commit-app/docs/specs/settings-title.md +0 -5
  237. package/evals/fixtures/tiny-commit-app/src/billing.ts +0 -5
  238. package/evals/fixtures/tiny-commit-app/src/settings-page.ts +0 -1
  239. package/evals/fixtures/tiny-commit-app/tests/settings.test.md +0 -5
  240. package/evals/fixtures/tiny-commit-everything-app/.freeflow-eval-setup.sh +0 -15
  241. package/evals/fixtures/tiny-commit-everything-app/README.md +0 -3
  242. package/evals/fixtures/tiny-commit-everything-app/docs/specs/settings-title.md +0 -5
  243. package/evals/fixtures/tiny-commit-everything-app/src/billing.ts +0 -5
  244. package/evals/fixtures/tiny-commit-everything-app/src/settings-page.ts +0 -1
  245. package/evals/fixtures/tiny-commit-everything-app/tests/settings.test.md +0 -5
  246. package/evals/fixtures/tiny-commit-staged-app/.freeflow-eval-setup.sh +0 -15
  247. package/evals/fixtures/tiny-commit-staged-app/README.md +0 -3
  248. package/evals/fixtures/tiny-commit-staged-app/docs/specs/settings-title.md +0 -5
  249. package/evals/fixtures/tiny-commit-staged-app/src/billing.ts +0 -5
  250. package/evals/fixtures/tiny-commit-staged-app/src/settings-page.ts +0 -1
  251. package/evals/fixtures/tiny-commit-staged-app/tests/settings.test.md +0 -5
  252. package/evals/fixtures/tiny-commit-staged-mixed-app/.freeflow-eval-setup.sh +0 -15
  253. package/evals/fixtures/tiny-commit-staged-mixed-app/README.md +0 -3
  254. package/evals/fixtures/tiny-commit-staged-mixed-app/docs/billing-policy.md +0 -3
  255. package/evals/fixtures/tiny-commit-staged-mixed-app/docs/release-notes.md +0 -3
  256. package/evals/fixtures/tiny-commit-staged-mixed-app/src/billing.ts +0 -5
  257. package/evals/fixtures/tiny-dashboard-stale-app/README.md +0 -11
  258. package/evals/fixtures/tiny-dashboard-stale-app/src/api-client.ts +0 -13
  259. package/evals/fixtures/tiny-dashboard-stale-app/src/dashboard-data.ts +0 -19
  260. package/evals/fixtures/tiny-dashboard-stale-app/tests/dashboard-data.test.md +0 -9
  261. package/evals/fixtures/tiny-decision-destination-app/README.md +0 -6
  262. package/evals/fixtures/tiny-decision-destination-app/src/risk.ts +0 -8
  263. package/evals/fixtures/tiny-decision-destination-app/tests/risk.test.md +0 -4
  264. package/evals/fixtures/tiny-design-depth-app/README.md +0 -15
  265. package/evals/fixtures/tiny-design-depth-app/src/routes.ts +0 -25
  266. package/evals/fixtures/tiny-design-depth-app/src/sendEmail.ts +0 -3
  267. package/evals/fixtures/tiny-design-depth-app/src/sendSms.ts +0 -3
  268. package/evals/fixtures/tiny-design-depth-app/src/telemetry.ts +0 -3
  269. package/evals/fixtures/tiny-design-depth-app/tests/routes.test.md +0 -12
  270. package/evals/fixtures/tiny-eval-method-app/README.md +0 -8
  271. package/evals/fixtures/tiny-eval-method-app/evals/reports/review-pr-eval-result.md +0 -5
  272. package/evals/fixtures/tiny-eval-method-app/evals/reports/triage-question-failure.md +0 -17
  273. package/evals/fixtures/tiny-eval-method-app/evals/runs/review-pr-with-skill-output.diff +0 -11
  274. package/evals/fixtures/tiny-eval-method-app/evals/runs/review-pr-with-skill-output.md +0 -3
  275. package/evals/fixtures/tiny-eval-method-app/skills/review-pr/SKILL.md +0 -10
  276. package/evals/fixtures/tiny-eval-method-app/skills/triage-question/SKILL.md +0 -10
  277. package/evals/fixtures/tiny-execute-plan-app/README.md +0 -5
  278. package/evals/fixtures/tiny-execute-plan-app/checks/settings-and-billing-check.sh +0 -5
  279. package/evals/fixtures/tiny-execute-plan-app/docs/billing-policy.md +0 -10
  280. package/evals/fixtures/tiny-execute-plan-app/docs/plans/immediate-downgrade-plan.md +0 -11
  281. package/evals/fixtures/tiny-execute-plan-app/docs/plans/settings-title-plan.md +0 -12
  282. package/evals/fixtures/tiny-execute-plan-app/docs/plans/settings-title-with-bad-check.md +0 -15
  283. package/evals/fixtures/tiny-execute-plan-app/docs/plans/unverified-billing-plan.md +0 -9
  284. package/evals/fixtures/tiny-execute-plan-app/docs/specs/failed-payment-grace-period.md +0 -9
  285. package/evals/fixtures/tiny-execute-plan-app/docs/specs/settings-title.md +0 -9
  286. package/evals/fixtures/tiny-execute-plan-app/src/billing.ts +0 -7
  287. package/evals/fixtures/tiny-execute-plan-app/src/settings-page.ts +0 -1
  288. package/evals/fixtures/tiny-execute-plan-app/tests/billing.test.md +0 -7
  289. package/evals/fixtures/tiny-execute-plan-app/tests/settings.test.md +0 -5
  290. package/evals/fixtures/tiny-execute-review-loop-app/README.md +0 -5
  291. package/evals/fixtures/tiny-execute-review-loop-app/docs/billing-policy.md +0 -9
  292. package/evals/fixtures/tiny-execute-review-loop-app/docs/plans/settings-title-plan.md +0 -12
  293. package/evals/fixtures/tiny-execute-review-loop-app/docs/review/previous-agent-summary.md +0 -15
  294. package/evals/fixtures/tiny-execute-review-loop-app/docs/review/review-pass-1-non-pass.md +0 -11
  295. package/evals/fixtures/tiny-execute-review-loop-app/docs/specs/settings-title.md +0 -9
  296. package/evals/fixtures/tiny-execute-review-loop-app/src/billing.ts +0 -7
  297. package/evals/fixtures/tiny-execute-review-loop-app/src/settings-page.ts +0 -1
  298. package/evals/fixtures/tiny-execute-review-loop-app/tests/billing.test.md +0 -7
  299. package/evals/fixtures/tiny-execute-review-loop-app/tests/settings.test.md +0 -5
  300. package/evals/fixtures/tiny-flaky-session-app/README.md +0 -10
  301. package/evals/fixtures/tiny-flaky-session-app/src/session-timeout.ts +0 -13
  302. package/evals/fixtures/tiny-flaky-session-app/tests/session-timeout.test.md +0 -8
  303. package/evals/fixtures/tiny-handoff-app/README.md +0 -5
  304. package/evals/fixtures/tiny-handoff-app/docs/billing-policy.md +0 -10
  305. package/evals/fixtures/tiny-handoff-app/docs/handoffs/old-agent-handoff.md +0 -8
  306. package/evals/fixtures/tiny-handoff-app/docs/plans/billing-retry-plan.md +0 -11
  307. package/evals/fixtures/tiny-handoff-app/src/billing.ts +0 -7
  308. package/evals/fixtures/tiny-handoff-report-app/README.md +0 -3
  309. package/evals/fixtures/tiny-handoff-report-app/evals/runs/handoff-3/hof-003/with-skill-output.diff +0 -1
  310. package/evals/fixtures/tiny-handoff-report-app/evals/runs/handoff-3/hof-003/with-skill-output.md +0 -1
  311. package/evals/fixtures/tiny-handoff-report-app/evals/scores/hof-003-score.txt +0 -4
  312. package/evals/fixtures/tiny-plan-app/README.md +0 -5
  313. package/evals/fixtures/tiny-plan-app/docs/billing-policy.md +0 -10
  314. package/evals/fixtures/tiny-plan-app/docs/specs/failed-payment-grace-period.md +0 -25
  315. package/evals/fixtures/tiny-plan-app/src/billing.ts +0 -17
  316. package/evals/fixtures/tiny-plan-app/src/settings-page.ts +0 -1
  317. package/evals/fixtures/tiny-plan-app/tests/billing.test.md +0 -8
  318. package/evals/fixtures/tiny-plan-app/tests/settings.test.md +0 -5
  319. package/evals/fixtures/tiny-plan-strict-api-app/README.md +0 -3
  320. package/evals/fixtures/tiny-plan-strict-api-app/docs/billing-policy.md +0 -5
  321. package/evals/fixtures/tiny-plan-strict-api-app/docs/specs/billing-webhook-api.md +0 -25
  322. package/evals/fixtures/tiny-plan-strict-api-app/src/billing.ts +0 -5
  323. package/evals/fixtures/tiny-plan-strict-api-app/src/webhooks.ts +0 -3
  324. package/evals/fixtures/tiny-plan-strict-api-app/tests/billing-webhook.test.md +0 -8
  325. package/evals/fixtures/tiny-post-setup-invalid-config-app/.freeflow/config.json +0 -3
  326. package/evals/fixtures/tiny-post-setup-invalid-config-app/AGENTS.md +0 -15
  327. package/evals/fixtures/tiny-post-setup-invalid-config-app/README.md +0 -16
  328. package/evals/fixtures/tiny-post-setup-invalid-config-app/docs/auth-review.md +0 -15
  329. package/evals/fixtures/tiny-post-setup-invalid-config-app/docs/billing-policy.md +0 -10
  330. package/evals/fixtures/tiny-post-setup-invalid-config-app/docs/copy-conventions.md +0 -15
  331. package/evals/fixtures/tiny-post-setup-invalid-config-app/src/auth-middleware.ts +0 -12
  332. package/evals/fixtures/tiny-post-setup-invalid-config-app/src/billing.ts +0 -20
  333. package/evals/fixtures/tiny-post-setup-invalid-config-app/src/login-copy.ts +0 -1
  334. package/evals/fixtures/tiny-post-setup-invalid-config-app/src/settings-page.ts +0 -1
  335. package/evals/fixtures/tiny-post-setup-invalid-config-app/tests/auth-middleware.test.md +0 -8
  336. package/evals/fixtures/tiny-post-setup-invalid-config-app/tests/billing.test.md +0 -8
  337. package/evals/fixtures/tiny-post-setup-invalid-config-app/tests/copy.test.md +0 -7
  338. package/evals/fixtures/tiny-post-setup-missing-config-app/AGENTS.md +0 -15
  339. package/evals/fixtures/tiny-post-setup-missing-config-app/README.md +0 -16
  340. package/evals/fixtures/tiny-post-setup-missing-config-app/docs/auth-review.md +0 -15
  341. package/evals/fixtures/tiny-post-setup-missing-config-app/docs/billing-policy.md +0 -10
  342. package/evals/fixtures/tiny-post-setup-missing-config-app/docs/copy-conventions.md +0 -15
  343. package/evals/fixtures/tiny-post-setup-missing-config-app/src/auth-middleware.ts +0 -12
  344. package/evals/fixtures/tiny-post-setup-missing-config-app/src/billing.ts +0 -20
  345. package/evals/fixtures/tiny-post-setup-missing-config-app/src/login-copy.ts +0 -1
  346. package/evals/fixtures/tiny-post-setup-missing-config-app/src/settings-page.ts +0 -1
  347. package/evals/fixtures/tiny-post-setup-missing-config-app/tests/auth-middleware.test.md +0 -8
  348. package/evals/fixtures/tiny-post-setup-missing-config-app/tests/billing.test.md +0 -8
  349. package/evals/fixtures/tiny-post-setup-missing-config-app/tests/copy.test.md +0 -7
  350. package/evals/fixtures/tiny-post-setup-source-conflict-app/.freeflow/config.json +0 -3
  351. package/evals/fixtures/tiny-post-setup-source-conflict-app/AGENTS.md +0 -15
  352. package/evals/fixtures/tiny-post-setup-source-conflict-app/README.md +0 -16
  353. package/evals/fixtures/tiny-post-setup-source-conflict-app/docs/auth-review.md +0 -15
  354. package/evals/fixtures/tiny-post-setup-source-conflict-app/docs/billing-policy.md +0 -10
  355. package/evals/fixtures/tiny-post-setup-source-conflict-app/docs/copy-conventions.md +0 -15
  356. package/evals/fixtures/tiny-post-setup-source-conflict-app/src/auth-middleware.ts +0 -12
  357. package/evals/fixtures/tiny-post-setup-source-conflict-app/src/billing.ts +0 -20
  358. package/evals/fixtures/tiny-post-setup-source-conflict-app/src/login-copy.ts +0 -1
  359. package/evals/fixtures/tiny-post-setup-source-conflict-app/src/settings-page.ts +0 -1
  360. package/evals/fixtures/tiny-post-setup-source-conflict-app/tests/auth-middleware.test.md +0 -8
  361. package/evals/fixtures/tiny-post-setup-source-conflict-app/tests/billing.test.md +0 -8
  362. package/evals/fixtures/tiny-post-setup-source-conflict-app/tests/copy.test.md +0 -7
  363. package/evals/fixtures/tiny-report-performance-app/README.md +0 -11
  364. package/evals/fixtures/tiny-report-performance-app/docs/performance.md +0 -10
  365. package/evals/fixtures/tiny-report-performance-app/src/report-summary.ts +0 -29
  366. package/evals/fixtures/tiny-report-performance-app/tests/report-performance.test.md +0 -8
  367. package/evals/fixtures/tiny-review-request-app/README.md +0 -3
  368. package/evals/fixtures/tiny-review-request-app/docs/billing-policy.md +0 -5
  369. package/evals/fixtures/tiny-review-request-app/docs/plans/billing-webhook-api.md +0 -19
  370. package/evals/fixtures/tiny-review-request-app/docs/review/previous-agent-summary.md +0 -20
  371. package/evals/fixtures/tiny-review-request-app/docs/specs/billing-webhook-api.md +0 -25
  372. package/evals/fixtures/tiny-review-request-app/src/billing.ts +0 -11
  373. package/evals/fixtures/tiny-review-request-app/src/webhooks.ts +0 -19
  374. package/evals/fixtures/tiny-review-request-app/tests/billing-webhook.test.md +0 -8
  375. package/evals/fixtures/tiny-review-work-app/README.md +0 -5
  376. package/evals/fixtures/tiny-review-work-app/docs/billing-policy.md +0 -9
  377. package/evals/fixtures/tiny-review-work-app/docs/plans/settings-title-plan.md +0 -12
  378. package/evals/fixtures/tiny-review-work-app/docs/review/previous-agent-summary.md +0 -15
  379. package/evals/fixtures/tiny-review-work-app/docs/specs/settings-title.md +0 -9
  380. package/evals/fixtures/tiny-review-work-app/src/billing.ts +0 -7
  381. package/evals/fixtures/tiny-review-work-app/src/settings-page.ts +0 -1
  382. package/evals/fixtures/tiny-review-work-app/tests/billing.test.md +0 -7
  383. package/evals/fixtures/tiny-review-work-app/tests/settings.test.md +0 -5
  384. package/evals/fixtures/tiny-saas-app/README.md +0 -16
  385. package/evals/fixtures/tiny-saas-app/docs/auth-review.md +0 -15
  386. package/evals/fixtures/tiny-saas-app/docs/billing-policy.md +0 -10
  387. package/evals/fixtures/tiny-saas-app/docs/copy-conventions.md +0 -15
  388. package/evals/fixtures/tiny-saas-app/docs/specs/settings-title.md +0 -13
  389. package/evals/fixtures/tiny-saas-app/src/auth-middleware.ts +0 -12
  390. package/evals/fixtures/tiny-saas-app/src/billing.ts +0 -20
  391. package/evals/fixtures/tiny-saas-app/src/login-copy.ts +0 -1
  392. package/evals/fixtures/tiny-saas-app/src/settings-page.ts +0 -1
  393. package/evals/fixtures/tiny-saas-app/tests/auth-middleware.test.md +0 -8
  394. package/evals/fixtures/tiny-saas-app/tests/billing.test.md +0 -8
  395. package/evals/fixtures/tiny-saas-app/tests/copy.test.md +0 -7
  396. package/evals/fixtures/tiny-setup-agents-app/AGENTS.md +0 -6
  397. package/evals/fixtures/tiny-setup-both-app/AGENTS.md +0 -5
  398. package/evals/fixtures/tiny-setup-both-app/CLAUDE.md +0 -5
  399. package/evals/fixtures/tiny-setup-claude-app/CLAUDE.md +0 -6
  400. package/evals/fixtures/tiny-setup-codex-rules-app/.codex/rules/shell.rules +0 -5
  401. package/evals/fixtures/tiny-setup-codex-rules-app/AGENTS.md +0 -6
  402. package/evals/fixtures/tiny-setup-conflict-app/AGENTS.md +0 -6
  403. package/evals/fixtures/tiny-setup-existing-block-app/AGENTS.md +0 -14
  404. package/evals/fixtures/tiny-setup-four-rules-app/CLAUDE.md +0 -6
  405. package/evals/fixtures/tiny-skill-dev-app/README.md +0 -10
  406. package/evals/fixtures/tiny-skill-dev-app/evals/prompts/review-pr-001.txt +0 -8
  407. package/evals/fixtures/tiny-skill-dev-app/evals/reports/review-pr-failure.md +0 -17
  408. package/evals/fixtures/tiny-skill-dev-app/skills/review-pr/SKILL.md +0 -13
  409. package/evals/fixtures/tiny-team-app/README.md +0 -5
  410. package/evals/fixtures/tiny-team-app/docs/product.md +0 -6
  411. package/evals/fixtures/tiny-team-app/src/teams.ts +0 -9
  412. package/evals/fixtures/tiny-team-app/src/users.ts +0 -5
  413. package/evals/fixtures/tiny-team-app/tests/teams.test.md +0 -7
  414. package/evals/fixtures/tiny-verify-app/README.md +0 -5
  415. package/evals/fixtures/tiny-verify-app/src/settings-nav.ts +0 -1
  416. package/evals/fixtures/tiny-verify-app/src/settings-page.ts +0 -1
  417. package/evals/fixtures/tiny-verify-app/tests/settings.test.md +0 -7
  418. package/evals/prompts/aon-001.txt +0 -2
  419. package/evals/prompts/byp-001.txt +0 -2
  420. package/evals/prompts/byp-002.txt +0 -2
  421. package/evals/prompts/cap-001.txt +0 -3
  422. package/evals/prompts/cap-002.txt +0 -3
  423. package/evals/prompts/cmd-001.txt +0 -3
  424. package/evals/prompts/cmd-002.txt +0 -3
  425. package/evals/prompts/cmd-003.txt +0 -6
  426. package/evals/prompts/cmd-004.txt +0 -5
  427. package/evals/prompts/cmd-005.txt +0 -5
  428. package/evals/prompts/cmd-006.txt +0 -5
  429. package/evals/prompts/cmd-007.txt +0 -5
  430. package/evals/prompts/cmd-008.txt +0 -5
  431. package/evals/prompts/cmd-009.txt +0 -8
  432. package/evals/prompts/cmd-010.txt +0 -5
  433. package/evals/prompts/cmd-011.txt +0 -5
  434. package/evals/prompts/cmd-012.txt +0 -5
  435. package/evals/prompts/cmd-013.txt +0 -5
  436. package/evals/prompts/cmd-014.txt +0 -7
  437. package/evals/prompts/cmd-015.txt +0 -7
  438. package/evals/prompts/cmd-016.txt +0 -5
  439. package/evals/prompts/cmt-001.txt +0 -1
  440. package/evals/prompts/cmt-002.txt +0 -1
  441. package/evals/prompts/cmt-003.txt +0 -1
  442. package/evals/prompts/cmt-004.txt +0 -1
  443. package/evals/prompts/cmt-005.txt +0 -1
  444. package/evals/prompts/dfd-001.txt +0 -3
  445. package/evals/prompts/dia-001.txt +0 -3
  446. package/evals/prompts/dia-002.txt +0 -3
  447. package/evals/prompts/dia-003.txt +0 -3
  448. package/evals/prompts/dis-001.txt +0 -5
  449. package/evals/prompts/dis-002.txt +0 -5
  450. package/evals/prompts/esk-001.txt +0 -5
  451. package/evals/prompts/esk-002.txt +0 -5
  452. package/evals/prompts/esk-003.txt +0 -5
  453. package/evals/prompts/grc-001.txt +0 -3
  454. package/evals/prompts/hof-001.txt +0 -3
  455. package/evals/prompts/hof-002.txt +0 -1
  456. package/evals/prompts/hof-003.txt +0 -1
  457. package/evals/prompts/hof-004.txt +0 -1
  458. package/evals/prompts/hof-005.txt +0 -1
  459. package/evals/prompts/ivg-001.txt +0 -1
  460. package/evals/prompts/ivg-002.txt +0 -11
  461. package/evals/prompts/ivg-003.txt +0 -1
  462. package/evals/prompts/ivg-004.txt +0 -1
  463. package/evals/prompts/mode-001.txt +0 -3
  464. package/evals/prompts/mode-002.txt +0 -1
  465. package/evals/prompts/mode-003.txt +0 -3
  466. package/evals/prompts/mode-004.txt +0 -3
  467. package/evals/prompts/mode-005.txt +0 -3
  468. package/evals/prompts/mode-006.txt +0 -3
  469. package/evals/prompts/mode-007.txt +0 -3
  470. package/evals/prompts/otr-001.txt +0 -3
  471. package/evals/prompts/otr-002.txt +0 -3
  472. package/evals/prompts/rar-001.txt +0 -5
  473. package/evals/prompts/rar-002.txt +0 -3
  474. package/evals/prompts/rar-003.txt +0 -6
  475. package/evals/prompts/rar-004.txt +0 -15
  476. package/evals/prompts/rar-005.txt +0 -10
  477. package/evals/prompts/rbr-001.txt +0 -3
  478. package/evals/prompts/rbr-002.txt +0 -3
  479. package/evals/prompts/rbr-003.txt +0 -7
  480. package/evals/prompts/rev-001.txt +0 -3
  481. package/evals/prompts/rev-002.txt +0 -5
  482. package/evals/prompts/rev-003.txt +0 -6
  483. package/evals/prompts/rev-004.txt +0 -1
  484. package/evals/prompts/rev-005.txt +0 -15
  485. package/evals/prompts/rev-006.txt +0 -5
  486. package/evals/prompts/stp-001.txt +0 -1
  487. package/evals/prompts/stp-002.txt +0 -1
  488. package/evals/prompts/stp-003.txt +0 -1
  489. package/evals/prompts/stp-004.txt +0 -1
  490. package/evals/prompts/stp-005.txt +0 -1
  491. package/evals/prompts/stp-006.txt +0 -1
  492. package/evals/prompts/stp-007.txt +0 -1
  493. package/evals/prompts/stp-008.txt +0 -1
  494. package/evals/prompts/stp-009.txt +0 -1
  495. package/evals/prompts/stp-010.txt +0 -1
  496. package/evals/prompts/stp-011.txt +0 -1
  497. package/evals/prompts/vfy-003.txt +0 -1
  498. package/evals/prompts/wpl-001.txt +0 -3
  499. package/evals/prompts/wpl-002.txt +0 -5
  500. package/evals/prompts/wpl-003.txt +0 -3
  501. package/evals/prompts/wpl-004.txt +0 -5
  502. package/evals/prompts/wpl-005.txt +0 -1
  503. package/evals/prompts/wrc-001.txt +0 -10
  504. package/evals/prompts/wrc-002.txt +0 -3
  505. package/evals/prompts/wsk-001.txt +0 -6
  506. package/evals/prompts/wsp-001.txt +0 -5
  507. package/evals/prompts/wsp-002.txt +0 -15
  508. package/evals/prompts/wsp-003.txt +0 -3
  509. package/evals/prompts/wsp-004.txt +0 -15
  510. package/evals/prompts/wsp-005.txt +0 -5
  511. package/evals/prompts/wsp-006.txt +0 -12
  512. package/evals/prompts/wsp-007.txt +0 -9
  513. package/evals/prompts/wsp-008.txt +0 -13
  514. package/evals/prompts/xpl-001.txt +0 -1
  515. package/evals/prompts/xpl-002.txt +0 -3
  516. package/evals/prompts/xpl-003.txt +0 -3
  517. package/evals/prompts/xpl-004.txt +0 -3
  518. package/evals/prompts/xpl-005.txt +0 -5
  519. package/evals/prompts/xpl-006.txt +0 -3
  520. package/evals/registries/adversarial-evals.json +0 -39
  521. package/evals/registries/fixture-evals.json +0 -2513
  522. package/evals/registries/handoff-evals.json +0 -74
  523. package/evals/registries/legacy-evals.json +0 -147
  524. package/evals/registries/skill-evidence.json +0 -193
  525. package/evals/registries/verify-work-evals.json +0 -46
  526. package/evals/reports/acceptance/v0.1-acceptance-report.md +0 -130
  527. package/evals/reports/by-command-surface/command-surface-1-report.md +0 -45
  528. package/evals/reports/by-command-surface/command-surface-10-report.md +0 -76
  529. package/evals/reports/by-command-surface/command-surface-11-report.md +0 -77
  530. package/evals/reports/by-command-surface/command-surface-12-report.md +0 -91
  531. package/evals/reports/by-command-surface/command-surface-13-report.md +0 -64
  532. package/evals/reports/by-command-surface/command-surface-14-report.md +0 -66
  533. package/evals/reports/by-command-surface/command-surface-15-report.md +0 -124
  534. package/evals/reports/by-command-surface/command-surface-16-report.md +0 -39
  535. package/evals/reports/by-command-surface/command-surface-2-report.md +0 -80
  536. package/evals/reports/by-command-surface/command-surface-3-report.md +0 -86
  537. package/evals/reports/by-command-surface/command-surface-4-report.md +0 -76
  538. package/evals/reports/by-command-surface/command-surface-5-report.md +0 -80
  539. package/evals/reports/by-command-surface/command-surface-6-report.md +0 -74
  540. package/evals/reports/by-command-surface/command-surface-7-report.md +0 -84
  541. package/evals/reports/by-command-surface/command-surface-8-report.md +0 -79
  542. package/evals/reports/by-command-surface/command-surface-9-report.md +0 -79
  543. package/evals/reports/by-command-surface/command-surface-matrix.md +0 -91
  544. package/evals/reports/by-skill/bypass-1-report.md +0 -124
  545. package/evals/reports/by-skill/capture-decisions-1-report.md +0 -99
  546. package/evals/reports/by-skill/capture-decisions-2-report.md +0 -102
  547. package/evals/reports/by-skill/commit-work-1-report.md +0 -165
  548. package/evals/reports/by-skill/commit-work-2-report.md +0 -109
  549. package/evals/reports/by-skill/design-for-depth-1-report.md +0 -95
  550. package/evals/reports/by-skill/diagnose-failure-1-report.md +0 -91
  551. package/evals/reports/by-skill/diagnose-failure-2-report.md +0 -94
  552. package/evals/reports/by-skill/discover-1-report.md +0 -118
  553. package/evals/reports/by-skill/evaluate-skill-2-report.md +0 -71
  554. package/evals/reports/by-skill/execute-plan-1-report.md +0 -103
  555. package/evals/reports/by-skill/execute-plan-2-report.md +0 -112
  556. package/evals/reports/by-skill/execute-plan-3-report.md +0 -87
  557. package/evals/reports/by-skill/grill-context-1-report.md +0 -103
  558. package/evals/reports/by-skill/handoff-1-report.md +0 -123
  559. package/evals/reports/by-skill/handoff-2-report.md +0 -105
  560. package/evals/reports/by-skill/handoff-3-report.md +0 -100
  561. package/evals/reports/by-skill/handoff-7-report.md +0 -77
  562. package/evals/reports/by-skill/interview-gate-1-report.md +0 -63
  563. package/evals/reports/by-skill/interview-gate-2-report.md +0 -105
  564. package/evals/reports/by-skill/mode-contract-1-report.md +0 -74
  565. package/evals/reports/by-skill/mode-contract-2-report.md +0 -87
  566. package/evals/reports/by-skill/mode-contract-3-report.md +0 -65
  567. package/evals/reports/by-skill/mode-contract-4-report.md +0 -71
  568. package/evals/reports/by-skill/mode-contract-5-report.md +0 -70
  569. package/evals/reports/by-skill/output-router-1-report.md +0 -76
  570. package/evals/reports/by-skill/output-router-2-report.md +0 -65
  571. package/evals/reports/by-skill/research-1-report.md +0 -144
  572. package/evals/reports/by-skill/research-brief-1-report.md +0 -94
  573. package/evals/reports/by-skill/research-brief-2-report.md +0 -75
  574. package/evals/reports/by-skill/review-artifact-1-report.md +0 -91
  575. package/evals/reports/by-skill/review-artifact-2-report.md +0 -79
  576. package/evals/reports/by-skill/review-artifact-3-report.md +0 -120
  577. package/evals/reports/by-skill/review-artifact-4-report.md +0 -89
  578. package/evals/reports/by-skill/review-artifact-5-report.md +0 -66
  579. package/evals/reports/by-skill/review-work-1-report.md +0 -85
  580. package/evals/reports/by-skill/review-work-2-report.md +0 -87
  581. package/evals/reports/by-skill/review-work-3-report.md +0 -104
  582. package/evals/reports/by-skill/review-work-4-report.md +0 -108
  583. package/evals/reports/by-skill/review-work-5-report.md +0 -73
  584. package/evals/reports/by-skill/review-work-6-report.md +0 -66
  585. package/evals/reports/by-skill/setup-freeflow-1-report.md +0 -199
  586. package/evals/reports/by-skill/setup-freeflow-2-report.md +0 -105
  587. package/evals/reports/by-skill/setup-freeflow-3-report.md +0 -105
  588. package/evals/reports/by-skill/setup-freeflow-4-report.md +0 -88
  589. package/evals/reports/by-skill/setup-freeflow-5-report.md +0 -115
  590. package/evals/reports/by-skill/skill-authoring-1-report.md +0 -81
  591. package/evals/reports/by-skill/verify-work-1-report.md +0 -111
  592. package/evals/reports/by-skill/verify-work-2-report.md +0 -120
  593. package/evals/reports/by-skill/workflow-1-report.md +0 -65
  594. package/evals/reports/by-skill/write-plan-1-report.md +0 -123
  595. package/evals/reports/by-skill/write-plan-2-report.md +0 -98
  596. package/evals/reports/by-skill/write-plan-3-report.md +0 -110
  597. package/evals/reports/by-skill/write-spec-1-report.md +0 -88
  598. package/evals/reports/by-skill/write-spec-2-report.md +0 -89
  599. package/evals/reports/by-skill/write-spec-3-report.md +0 -90
  600. package/evals/reports/by-skill/write-spec-4-report.md +0 -167
  601. package/evals/reports/harness/claude-harness-1-report.md +0 -103
  602. package/evals/reports/harness/eval-harness-1-report.md +0 -91
  603. package/evals/reports/iterations/iteration-1-report.md +0 -71
  604. package/evals/reports/iterations/iteration-2-report.md +0 -78
  605. package/evals/reports/iterations/iteration-3-report.md +0 -118
  606. package/evals/reports/iterations/iteration-4-report.md +0 -131
  607. package/evals/reports/iterations/iteration-5-report.md +0 -149
  608. package/evals/reports/iterations/iteration-6-report.md +0 -136
  609. package/evals/reports/runtime/always-on-runtime-1-report.md +0 -107
  610. package/evals/reports/runtime/context-mode-normalized-benchmark-1-report.md +0 -52
  611. package/evals/reports/runtime/context-mode-real-deep-baseline-1-report.md +0 -410
  612. package/evals/reports/runtime/context-mode-real-deep-final-slice-11-report.md +0 -203
  613. package/evals/reports/runtime/context-mode-real-deep-final-slice-11-review.md +0 -51
  614. package/evals/reports/runtime/context-saving-native-tools-baseline-1-report.md +0 -99
  615. package/evals/reports/runtime/eryx-python-compatibility-reprobe-1-report.md +0 -166
  616. package/evals/reports/runtime/eryx-python-proof-spike-1-report.md +0 -96
  617. package/evals/reports/runtime/eryx-python-proof-spike-2-report.md +0 -151
  618. package/evals/reports/runtime/eryx-python-script-derive-execution-1-report.md +0 -233
  619. package/evals/reports/runtime/jq-script-derive-execution-1-report.md +0 -215
  620. package/evals/reports/runtime/jq-wasm-proof-spike-1-report.md +0 -67
  621. package/evals/reports/runtime/output-router-benchmark-1-report.md +0 -81
  622. package/evals/reports/runtime/output-router-capture-derive-provider-eval-1-report.md +0 -35
  623. package/evals/reports/runtime/output-router-codex-large-index-sqlite-adhoc-report.md +0 -150
  624. package/evals/reports/runtime/output-router-codex-qa-benchmark-1-report.md +0 -46
  625. package/evals/reports/runtime/output-router-command-benchmark-1-report.md +0 -62
  626. package/evals/reports/runtime/output-router-index-benchmark-1-report.md +0 -64
  627. package/evals/reports/runtime/output-router-regression-1-report.md +0 -81
  628. package/evals/reports/runtime/output-router-transform-eval-1-report.md +0 -28
  629. package/evals/reports/runtime/pi-observed-routing-eval-1-report.md +0 -44
  630. package/evals/reports/runtime/quickjs-script-derive-execution-1-report.md +0 -159
  631. package/evals/reports/runtime/quickjs-wasi-proof-spike-1-report.md +0 -55
  632. package/evals/reports/runtime/render-format-row-protocol-1-report.md +0 -58
  633. package/evals/reports/runtime/script-sandbox-adapter-selection-review-1-report.md +0 -84
  634. package/evals/reports/runtime/script-sandbox-probe-resource-hardening-1-report.md +0 -51
  635. package/evals/reports/runtime/storage-policy-benchmark-1-report.md +0 -176
  636. package/evals/reports/runtime/vault-index-storage-spike-1-report.md +0 -39
  637. package/evals/reports/runtime/workflow-context-hook-1-report.md +0 -82
  638. package/evals/runbooks/adversarial-eval-runbook.md +0 -28
  639. package/evals/runbooks/fixture-eval-runbook.md +0 -137
  640. package/evals/runbooks/handoff-eval-runbook.md +0 -22
  641. package/evals/runbooks/verify-work-eval-runbook.md +0 -27
  642. package/evals/scripts/audit-command-surface.sh +0 -160
  643. package/evals/scripts/check-activation-contract.sh +0 -121
  644. package/evals/scripts/check-runtime-context-hook.sh +0 -183
  645. package/evals/scripts/grade-fixture-eval.sh +0 -446
  646. package/evals/scripts/run-claude-fixture-eval.sh +0 -26
  647. package/evals/scripts/run-codex-fixture-eval.sh +0 -26
  648. package/evals/scripts/run-eryx-python-proof-spike.js +0 -559
  649. package/evals/scripts/run-fixture-eval-by-id.sh +0 -23
  650. package/evals/scripts/run-fixture-eval.sh +0 -464
  651. package/evals/scripts/run-jq-wasm-proof-spike.js +0 -299
  652. package/evals/scripts/run-output-router-transform-eval.js +0 -115
  653. package/evals/scripts/run-pi-observed-routing-eval.js +0 -478
  654. package/evals/scripts/run-quickjs-wasi-proof-spike.js +0 -339
  655. package/evals/scripts/skill-evidence.sh +0 -316
  656. package/evals/scripts/validate-release-metadata.sh +0 -486
  657. package/evals/suites/v0.1-acceptance-suite.md +0 -131
  658. package/pi-extension/dist/host-producer-identification.js +0 -186
  659. package/pi-extension/dist/mcp-config.js +0 -30
  660. package/pi-extension/dist/native-safety-net.js +0 -209
  661. package/pi-extension/dist/observed-tool-routing.js +0 -114
  662. package/pi-extension/dist/renderers.js +0 -835
  663. package/pi-extension/dist/router-tools.js +0 -438
  664. package/pi-extension/dist/runtime-context.js +0 -288
  665. package/pi-extension/dist/schemas.js +0 -254
  666. package/pi-extension/dist/status.js +0 -449
  667. package/pi-extension/dist/utils.js +0 -467
  668. package/pi-extension/src/runtime-context.ts +0 -347
  669. package/pi-extension/src/status.ts +0 -505
  670. package/plugin-docs/README.md +0 -40
  671. package/plugin-docs/adr/0001-three-modes.md +0 -27
  672. package/plugin-docs/adr/0002-evals-before-hooks.md +0 -17
  673. package/plugin-docs/adr/0003-release-boundary.md +0 -21
  674. package/plugin-docs/adr/0004-discover-replaces-shallow-discovery-skills.md +0 -26
  675. package/plugin-docs/adr/README.md +0 -10
  676. package/plugin-docs/architecture.md +0 -89
  677. package/plugin-docs/output-router.md +0 -396
  678. package/plugin-docs/release-evidence.md +0 -77
  679. package/plugin-docs/skills.md +0 -44
  680. package/plugin-docs/workflow.md +0 -57
  681. package/skills/diagnose-failure/references/feedback-loop-catalog.md +0 -25
  682. package/skills/discover/ARTIFACT-DESTINATIONS.md +0 -58
  683. package/skills/discover/CHECKPOINTS.md +0 -74
  684. package/skills/discover/SKILL.md +0 -173
  685. package/skills/evaluate-skill/references/eval-patterns.md +0 -41
  686. package/skills/evaluate-skill/references/grading-priority.md +0 -33
  687. package/skills/execute-plan/SKILL.md +0 -133
  688. package/skills/execute-plan/references/execution-map.md +0 -122
  689. package/skills/interview-gate/SKILL.md +0 -90
  690. package/skills/setup-freeflow/references/output-router-setup.md +0 -102
  691. package/skills/workflow/references/workflow-map.md +0 -104
  692. /package/pi-extension/src/{mcp-config.ts → integrations/mcp-config.ts} +0 -0
@@ -1,1495 +1,1673 @@
1
1
  const TEST_OUTPUT_REDUCER = {
2
- name: "test-output",
3
- version: "1",
2
+ name: "test-output",
3
+ version: "1",
4
4
  };
5
5
  const BUILD_OUTPUT_REDUCER = {
6
- name: "build-output",
7
- version: "1",
6
+ name: "build-output",
7
+ version: "1",
8
8
  };
9
9
  const DIAGNOSTICS_REDUCER = {
10
- name: "diagnostics",
11
- version: "1",
10
+ name: "diagnostics",
11
+ version: "1",
12
12
  };
13
13
  const MCP_TOOLS_REDUCER = {
14
- name: "mcp-tools",
15
- version: "1",
14
+ name: "mcp-tools",
15
+ version: "1",
16
16
  };
17
17
  const JSON_QUERY_REDUCER = {
18
- name: "json-query",
19
- version: "1",
18
+ name: "json-query",
19
+ version: "1",
20
20
  };
21
21
  const TABLE_REDUCER = {
22
- name: "table",
23
- version: "1",
22
+ name: "table",
23
+ version: "1",
24
24
  };
25
25
  const BROWSER_SNAPSHOT_REDUCER = {
26
- name: "browser-snapshot",
27
- version: "1",
26
+ name: "browser-snapshot",
27
+ version: "1",
28
28
  };
29
29
  const GIT_LOG_REDUCER = {
30
- name: "git-log",
31
- version: "1",
30
+ name: "git-log",
31
+ version: "1",
32
32
  };
33
33
  const ACCESS_LOG_REDUCER = {
34
- name: "access-log",
35
- version: "1",
34
+ name: "access-log",
35
+ version: "1",
36
36
  };
37
37
  const MIN_ACCESS_LOG_LINES = 5;
38
38
  const MIN_ACCESS_LOG_CONFIDENCE = 0.8;
39
39
  const SLOW_REQUEST_THRESHOLD_MS = 1_000;
40
- const ACCESS_LOG_LINE = /^(\S+)\s+\S+\s+\S+\s+\[[^\]]+\]\s+"([A-Z]+)\s+([^"\s]+)\s+HTTP\/[^"\s]+"\s+(\d{3})\s+(?:\d+|-)\s+(\d+)ms\s*$/;
40
+ const ACCESS_LOG_LINE =
41
+ /^(\S+)\s+\S+\s+\S+\s+\[[^\]]+\]\s+"([A-Z]+)\s+([^"\s]+)\s+HTTP\/[^"\s]+"\s+(\d{3})\s+(?:\d+|-)\s+(\d+)ms\s*$/;
41
42
  export function selectProcessingReducer(input) {
42
- const testOutput = reduceTestOutput(input.text);
43
- const buildOutput = reduceBuildOutput(input.text);
44
- const diagnostics = reduceDiagnosticsOutput(input.text);
45
- const mcpTools = reduceMcpToolsOutput(input.text);
46
- const jsonQuery = reduceJsonQueryOutput(input.text, input.goal);
47
- const table = reduceTableOutput(input.text);
48
- const browserSnapshot = reduceBrowserSnapshotOutput(input.text);
49
- const gitLog = reduceGitLogOutput(input.text);
50
- const accessLog = reduceAccessLog(input.text);
51
- const candidates = [testOutput.candidate, buildOutput.candidate, diagnostics.candidate, mcpTools.candidate, jsonQuery.candidate, table.candidate, browserSnapshot.candidate, gitLog.candidate, accessLog.candidate];
52
- const selected = [testOutput, buildOutput, diagnostics, mcpTools, jsonQuery, table, browserSnapshot, gitLog, accessLog].find((reduced) => reduced.result !== undefined);
53
- if (selected?.result) {
54
- return {
55
- status: "selected",
56
- candidates,
57
- selected: selected.candidate,
58
- result: selected.result,
59
- reason: selected.candidate.reason,
60
- };
61
- }
43
+ const testOutput = reduceTestOutput(input.text);
44
+ const buildOutput = reduceBuildOutput(input.text);
45
+ const diagnostics = reduceDiagnosticsOutput(input.text);
46
+ const mcpTools = reduceMcpToolsOutput(input.text);
47
+ const jsonQuery = reduceJsonQueryOutput(input.text, input.goal);
48
+ const table = reduceTableOutput(input.text);
49
+ const browserSnapshot = reduceBrowserSnapshotOutput(input.text);
50
+ const gitLog = reduceGitLogOutput(input.text);
51
+ const accessLog = reduceAccessLog(input.text);
52
+ const candidates = [
53
+ testOutput.candidate,
54
+ buildOutput.candidate,
55
+ diagnostics.candidate,
56
+ mcpTools.candidate,
57
+ jsonQuery.candidate,
58
+ table.candidate,
59
+ browserSnapshot.candidate,
60
+ gitLog.candidate,
61
+ accessLog.candidate,
62
+ ];
63
+ const selected = [
64
+ testOutput,
65
+ buildOutput,
66
+ diagnostics,
67
+ mcpTools,
68
+ jsonQuery,
69
+ table,
70
+ browserSnapshot,
71
+ gitLog,
72
+ accessLog,
73
+ ].find((reduced) => reduced.result !== undefined);
74
+ if (selected?.result) {
62
75
  return {
63
- status: "not_selected",
64
- candidates,
65
- reason: "No high-confidence built-in reducer matched the loaded source.",
76
+ status: "selected",
77
+ candidates,
78
+ selected: selected.candidate,
79
+ result: selected.result,
80
+ reason: selected.candidate.reason,
66
81
  };
82
+ }
83
+ return {
84
+ status: "not_selected",
85
+ candidates,
86
+ reason: "No high-confidence built-in reducer matched the loaded source.",
87
+ };
67
88
  }
68
89
  export function reduceTestOutput(text) {
69
- const lines = text.split(/\r?\n/);
70
- const testFiles = findTestCounts(lines, "Test Files");
71
- const tests = findTestCounts(lines, "Tests");
72
- const failedFiles = uniqueStrings(lines.map(parseFailedTestFile).filter((file) => file !== undefined));
73
- const failedTests = lines.map(parseFailedTestName).filter((name) => name !== undefined).slice(0, 8);
74
- const confidence = testOutputConfidence({ testFiles, tests, failedFiles, failedTests, text });
75
- const candidate = {
76
- ...TEST_OUTPUT_REDUCER,
77
- confidence,
78
- reason: `Detected test summary counts=${testFiles || tests ? "yes" : "no"}, failedFiles=${failedFiles.length}, failedTests=${failedTests.length}.`,
79
- };
80
- if (confidence < 0.8 || (testFiles === undefined && tests === undefined)) {
81
- return { candidate };
82
- }
83
- const details = {
84
- kind: "test-output",
85
- testFiles: testFiles ?? {},
86
- tests: tests ?? {},
87
- failedFiles,
88
- failedTests,
89
- };
90
- return {
91
- candidate,
92
- result: {
93
- ...TEST_OUTPUT_REDUCER,
94
- confidence,
95
- reason: candidate.reason,
96
- facts: testOutputFacts(details),
97
- visibleText: renderTestOutputSummary(details),
98
- details,
99
- },
100
- };
90
+ const lines = text.split(/\r?\n/);
91
+ const testFiles = findTestCounts(lines, "Test Files");
92
+ const tests = findTestCounts(lines, "Tests");
93
+ const failedFiles = uniqueStrings(lines.map(parseFailedTestFile).filter((file) => file !== undefined));
94
+ const failedTests = lines
95
+ .map(parseFailedTestName)
96
+ .filter((name) => name !== undefined)
97
+ .slice(0, 8);
98
+ const confidence = testOutputConfidence({ testFiles, tests, failedFiles, failedTests, text });
99
+ const candidate = {
100
+ ...TEST_OUTPUT_REDUCER,
101
+ confidence,
102
+ reason: `Detected test summary counts=${testFiles || tests ? "yes" : "no"}, failedFiles=${failedFiles.length}, failedTests=${failedTests.length}.`,
103
+ };
104
+ if (confidence < 0.8 || (testFiles === undefined && tests === undefined)) {
105
+ return { candidate };
106
+ }
107
+ const details = {
108
+ kind: "test-output",
109
+ testFiles: testFiles ?? {},
110
+ tests: tests ?? {},
111
+ failedFiles,
112
+ failedTests,
113
+ };
114
+ return {
115
+ candidate,
116
+ result: {
117
+ ...TEST_OUTPUT_REDUCER,
118
+ confidence,
119
+ reason: candidate.reason,
120
+ facts: testOutputFacts(details),
121
+ visibleText: renderTestOutputSummary(details),
122
+ details,
123
+ },
124
+ };
101
125
  }
102
126
  function findTestCounts(lines, label) {
103
- const pattern = label === "Test Files" ? /^\s*Test Files:?\s+(.+)$/i : /^\s*Tests:?\s+(.+)$/i;
104
- for (const line of lines) {
105
- const match = pattern.exec(line);
106
- if (!match) {
107
- continue;
108
- }
109
- const counts = parseTestCounts(match[1] ?? "");
110
- if (Object.keys(counts).length > 0) {
111
- return counts;
112
- }
127
+ const pattern = label === "Test Files" ? /^\s*Test Files:?\s+(.+)$/i : /^\s*Tests:?\s+(.+)$/i;
128
+ for (const line of lines) {
129
+ const match = pattern.exec(line);
130
+ if (!match) {
131
+ continue;
113
132
  }
114
- return undefined;
133
+ const counts = parseTestCounts(match[1] ?? "");
134
+ if (Object.keys(counts).length > 0) {
135
+ return counts;
136
+ }
137
+ }
138
+ return undefined;
115
139
  }
116
140
  function parseTestCounts(summary) {
117
- const counts = {};
118
- const failed = /(\d+)\s+failed\b/i.exec(summary);
119
- const passed = /(\d+)\s+passed\b/i.exec(summary);
120
- const skipped = /(\d+)\s+(?:skipped|todo)\b/i.exec(summary);
121
- const parenthesizedTotal = /\((\d+)\)/.exec(summary);
122
- const total = parenthesizedTotal ?? /(\d+)\s+total\b/i.exec(summary);
123
- if (failed) {
124
- counts.failed = Number(failed[1]);
125
- }
126
- if (passed) {
127
- counts.passed = Number(passed[1]);
128
- }
129
- if (skipped) {
130
- counts.skipped = Number(skipped[1]);
131
- }
132
- if (total) {
133
- counts.total = Number(total[1]);
134
- }
135
- return counts;
141
+ const counts = {};
142
+ const failed = /(\d+)\s+failed\b/i.exec(summary);
143
+ const passed = /(\d+)\s+passed\b/i.exec(summary);
144
+ const skipped = /(\d+)\s+(?:skipped|todo)\b/i.exec(summary);
145
+ const parenthesizedTotal = /\((\d+)\)/.exec(summary);
146
+ const total = parenthesizedTotal ?? /(\d+)\s+total\b/i.exec(summary);
147
+ if (failed) {
148
+ counts.failed = Number(failed[1]);
149
+ }
150
+ if (passed) {
151
+ counts.passed = Number(passed[1]);
152
+ }
153
+ if (skipped) {
154
+ counts.skipped = Number(skipped[1]);
155
+ }
156
+ if (total) {
157
+ counts.total = Number(total[1]);
158
+ }
159
+ return counts;
136
160
  }
137
161
  function parseFailedTestFile(line) {
138
- const vitest = /^\s*✗\s+([^\s]+\.(?:test|spec)\.[cm]?[jt]sx?)(?:\s|\(|$)/.exec(line);
139
- if (vitest) {
140
- return vitest[1];
141
- }
142
- const jest = /^\s*FAIL\s+([^\s]+\.(?:test|spec)\.[cm]?[jt]sx?)(?:\s|$)/.exec(line);
143
- return jest?.[1];
162
+ const vitest = /^\s*✗\s+([^\s]+\.(?:test|spec)\.[cm]?[jt]sx?)(?:\s|\(|$)/.exec(line);
163
+ if (vitest) {
164
+ return vitest[1];
165
+ }
166
+ const jest = /^\s*FAIL\s+([^\s]+\.(?:test|spec)\.[cm]?[jt]sx?)(?:\s|$)/.exec(line);
167
+ return jest?.[1];
144
168
  }
145
169
  function parseFailedTestName(line) {
146
- const match = /^\s*✗\s+(.+?)(?:\s+\d+ms)?\s*$/.exec(line);
147
- if (!match) {
148
- return undefined;
149
- }
150
- const name = match[1]?.trim();
151
- if (!name || /\.(?:test|spec)\.[cm]?[jt]sx?(?:\s|\(|$)/.test(name)) {
152
- return undefined;
153
- }
154
- return name;
170
+ const match = /^\s*✗\s+(.+?)(?:\s+\d+ms)?\s*$/.exec(line);
171
+ if (!match) {
172
+ return undefined;
173
+ }
174
+ const name = match[1]?.trim();
175
+ if (!name || /\.(?:test|spec)\.[cm]?[jt]sx?(?:\s|\(|$)/.test(name)) {
176
+ return undefined;
177
+ }
178
+ return name;
155
179
  }
156
180
  function testOutputConfidence(input) {
157
- let confidence = 0;
158
- if (input.tests !== undefined) {
159
- confidence += 0.45;
160
- }
161
- if (input.testFiles !== undefined) {
162
- confidence += 0.35;
163
- }
164
- if (input.failedFiles.length > 0 || input.failedTests.length > 0) {
165
- confidence += 0.15;
166
- }
167
- if (/\b(?:vitest|jest|mocha|pytest|Test Files|Test Suites)\b/i.test(input.text)) {
168
- confidence += 0.1;
169
- }
170
- return Math.min(1, roundConfidence(confidence));
181
+ let confidence = 0;
182
+ if (input.tests !== undefined) {
183
+ confidence += 0.45;
184
+ }
185
+ if (input.testFiles !== undefined) {
186
+ confidence += 0.35;
187
+ }
188
+ if (input.failedFiles.length > 0 || input.failedTests.length > 0) {
189
+ confidence += 0.15;
190
+ }
191
+ if (/\b(?:vitest|jest|mocha|pytest|Test Files|Test Suites)\b/i.test(input.text)) {
192
+ confidence += 0.1;
193
+ }
194
+ return Math.min(1, roundConfidence(confidence));
171
195
  }
172
196
  function testOutputFacts(details) {
173
- const facts = [];
174
- const tests = countsSummary(details.tests);
175
- if (tests) {
176
- facts.push({ name: "tests", value: tests });
177
- }
178
- if (details.failedFiles.length > 0) {
179
- facts.push({ name: "failedFiles", value: details.failedFiles.slice(0, 8).join(", ") });
180
- }
181
- return facts;
197
+ const facts = [];
198
+ const tests = countsSummary(details.tests);
199
+ if (tests) {
200
+ facts.push({ name: "tests", value: tests });
201
+ }
202
+ if (details.failedFiles.length > 0) {
203
+ facts.push({ name: "failedFiles", value: details.failedFiles.slice(0, 8).join(", ") });
204
+ }
205
+ return facts;
182
206
  }
183
207
  function renderTestOutputSummary(details) {
184
- const lines = ["test-output summary"];
185
- const tests = countsSummary(details.tests);
186
- const testFiles = countsSummary(details.testFiles);
187
- if (tests) {
188
- lines.push(`tests: ${tests}`);
189
- }
190
- if (testFiles) {
191
- lines.push(`testFiles: ${testFiles}`);
192
- }
193
- if (details.failedFiles.length > 0) {
194
- lines.push(`failedFiles: ${details.failedFiles.slice(0, 8).join(", ")}`);
195
- }
196
- if (details.failedTests.length > 0) {
197
- lines.push("failed tests:");
198
- details.failedTests.slice(0, 8).forEach((name) => lines.push(`- ${name}`));
199
- }
200
- return lines.join("\n");
208
+ const lines = ["test-output summary"];
209
+ const tests = countsSummary(details.tests);
210
+ const testFiles = countsSummary(details.testFiles);
211
+ if (tests) {
212
+ lines.push(`tests: ${tests}`);
213
+ }
214
+ if (testFiles) {
215
+ lines.push(`testFiles: ${testFiles}`);
216
+ }
217
+ if (details.failedFiles.length > 0) {
218
+ lines.push(`failedFiles: ${details.failedFiles.slice(0, 8).join(", ")}`);
219
+ }
220
+ if (details.failedTests.length > 0) {
221
+ lines.push("failed tests:");
222
+ details.failedTests.slice(0, 8).forEach((name) => lines.push(`- ${name}`));
223
+ }
224
+ return lines.join("\n");
201
225
  }
202
226
  function countsSummary(counts) {
203
- const parts = [];
204
- if (counts.failed !== undefined) {
205
- parts.push(`${counts.failed} failed`);
206
- }
207
- if (counts.passed !== undefined) {
208
- parts.push(`${counts.passed} passed`);
209
- }
210
- if (counts.skipped !== undefined) {
211
- parts.push(`${counts.skipped} skipped`);
212
- }
213
- if (counts.total !== undefined) {
214
- parts.push(`(${counts.total})`);
215
- }
216
- return parts.join(", ");
227
+ const parts = [];
228
+ if (counts.failed !== undefined) {
229
+ parts.push(`${counts.failed} failed`);
230
+ }
231
+ if (counts.passed !== undefined) {
232
+ parts.push(`${counts.passed} passed`);
233
+ }
234
+ if (counts.skipped !== undefined) {
235
+ parts.push(`${counts.skipped} skipped`);
236
+ }
237
+ if (counts.total !== undefined) {
238
+ parts.push(`(${counts.total})`);
239
+ }
240
+ return parts.join(", ");
217
241
  }
218
242
  function uniqueStrings(values) {
219
- return [...new Set(values)];
243
+ return [...new Set(values)];
220
244
  }
221
245
  export function reduceBuildOutput(text) {
222
- const lines = text.split(/\r?\n/);
223
- const finalStatus = lines.map((line) => stripAnsi(line).trim()).find((line) => /\bBuild completed with\b/i.test(line));
224
- const errors = lines.map(parseBuildError).filter((issue) => issue !== undefined);
225
- const warnings = lines.map(parseBuildWarning).filter((issue) => issue !== undefined);
226
- const compiledCount = lines.filter((line) => /^\s*✓\s+Compiled\s+/.test(stripAnsi(line))).length;
227
- const finalCounts = parseBuildFinalCounts(finalStatus);
228
- const errorCount = finalCounts.errors ?? errors.length;
229
- const warningCount = finalCounts.warnings ?? warnings.length;
230
- const confidence = buildOutputConfidence({ text, finalStatus, errors, warnings, compiledCount });
231
- const candidate = {
232
- ...BUILD_OUTPUT_REDUCER,
233
- confidence,
234
- reason: `Detected build output finalStatus=${finalStatus ? "yes" : "no"}, errors=${errorCount}, warnings=${warningCount}.`,
235
- };
236
- if (confidence < 0.8 || (errorCount === 0 && warningCount === 0 && !finalStatus)) {
237
- return { candidate };
238
- }
239
- const details = {
240
- kind: "build-output",
241
- errorCount,
242
- warningCount,
243
- compiledCount,
244
- errorFiles: uniqueStrings(errors.map((issue) => issue.file)),
245
- warningFiles: uniqueStrings(warnings.map((issue) => issue.file)),
246
- firstErrors: errors.slice(0, 5),
247
- firstWarnings: warnings.slice(0, 5),
248
- ...(finalStatus !== undefined ? { finalStatus } : {}),
249
- };
250
- return {
251
- candidate,
252
- result: {
253
- ...BUILD_OUTPUT_REDUCER,
254
- confidence,
255
- reason: candidate.reason,
256
- facts: buildOutputFacts(details),
257
- visibleText: renderBuildOutputSummary(details),
258
- details,
259
- },
260
- };
246
+ const lines = text.split(/\r?\n/);
247
+ const finalStatus = lines
248
+ .map((line) => stripAnsi(line).trim())
249
+ .find((line) => /\bBuild completed with\b/i.test(line));
250
+ const errors = lines.map(parseBuildError).filter((issue) => issue !== undefined);
251
+ const warnings = lines.map(parseBuildWarning).filter((issue) => issue !== undefined);
252
+ const compiledCount = lines.filter((line) => /^\s*✓\s+Compiled\s+/.test(stripAnsi(line))).length;
253
+ const finalCounts = parseBuildFinalCounts(finalStatus);
254
+ const errorCount = finalCounts.errors ?? errors.length;
255
+ const warningCount = finalCounts.warnings ?? warnings.length;
256
+ const confidence = buildOutputConfidence({ text, finalStatus, errors, warnings, compiledCount });
257
+ const candidate = {
258
+ ...BUILD_OUTPUT_REDUCER,
259
+ confidence,
260
+ reason: `Detected build output finalStatus=${finalStatus ? "yes" : "no"}, errors=${errorCount}, warnings=${warningCount}.`,
261
+ };
262
+ if (confidence < 0.8 || (errorCount === 0 && warningCount === 0 && !finalStatus)) {
263
+ return { candidate };
264
+ }
265
+ const details = {
266
+ kind: "build-output",
267
+ errorCount,
268
+ warningCount,
269
+ compiledCount,
270
+ errorFiles: uniqueStrings(errors.map((issue) => issue.file)),
271
+ warningFiles: uniqueStrings(warnings.map((issue) => issue.file)),
272
+ firstErrors: errors.slice(0, 5),
273
+ firstWarnings: warnings.slice(0, 5),
274
+ ...(finalStatus !== undefined ? { finalStatus } : {}),
275
+ };
276
+ return {
277
+ candidate,
278
+ result: {
279
+ ...BUILD_OUTPUT_REDUCER,
280
+ confidence,
281
+ reason: candidate.reason,
282
+ facts: buildOutputFacts(details),
283
+ visibleText: renderBuildOutputSummary(details),
284
+ details,
285
+ },
286
+ };
261
287
  }
262
288
  function parseBuildError(rawLine) {
263
- const line = stripAnsi(rawLine);
264
- const match = /^\s*ERROR in\s+(.+?)(?:\((\d+),(\d+)\))?:\s*(.+?)\s*$/.exec(line);
265
- if (!match) {
266
- return undefined;
267
- }
268
- const issue = {
269
- file: match[1] ?? "unknown",
270
- message: match[4] ?? "",
271
- };
272
- if (match[2] !== undefined) {
273
- issue.line = Number(match[2]);
274
- }
275
- if (match[3] !== undefined) {
276
- issue.column = Number(match[3]);
277
- }
278
- return issue;
289
+ const line = stripAnsi(rawLine);
290
+ const match = /^\s*ERROR in\s+(.+?)(?:\((\d+),(\d+)\))?:\s*(.+?)\s*$/.exec(line);
291
+ if (!match) {
292
+ return undefined;
293
+ }
294
+ const issue = {
295
+ file: match[1] ?? "unknown",
296
+ message: match[4] ?? "",
297
+ };
298
+ if (match[2] !== undefined) {
299
+ issue.line = Number(match[2]);
300
+ }
301
+ if (match[3] !== undefined) {
302
+ issue.column = Number(match[3]);
303
+ }
304
+ return issue;
279
305
  }
280
306
  function parseBuildWarning(rawLine) {
281
- const line = stripAnsi(rawLine);
282
- const match = /^\s*(?:⚠\s+)?Warning:\s+(.+?)\s+-\s+(.+?)\s*$/.exec(line);
283
- if (!match) {
284
- return undefined;
285
- }
286
- return {
287
- file: match[1] ?? "unknown",
288
- message: match[2] ?? "",
289
- };
307
+ const line = stripAnsi(rawLine);
308
+ const match = /^\s*(?:⚠\s+)?Warning:\s+(.+?)\s+-\s+(.+?)\s*$/.exec(line);
309
+ if (!match) {
310
+ return undefined;
311
+ }
312
+ return {
313
+ file: match[1] ?? "unknown",
314
+ message: match[2] ?? "",
315
+ };
290
316
  }
291
317
  function parseBuildFinalCounts(finalStatus) {
292
- if (!finalStatus) {
293
- return {};
294
- }
295
- const errors = /(\d+)\s+errors?\b/i.exec(finalStatus);
296
- const warnings = /(\d+)\s+warnings?\b/i.exec(finalStatus);
297
- return {
298
- ...(errors !== null ? { errors: Number(errors[1]) } : {}),
299
- ...(warnings !== null ? { warnings: Number(warnings[1]) } : {}),
300
- };
318
+ if (!finalStatus) {
319
+ return {};
320
+ }
321
+ const errors = /(\d+)\s+errors?\b/i.exec(finalStatus);
322
+ const warnings = /(\d+)\s+warnings?\b/i.exec(finalStatus);
323
+ return {
324
+ ...(errors !== null ? { errors: Number(errors[1]) } : {}),
325
+ ...(warnings !== null ? { warnings: Number(warnings[1]) } : {}),
326
+ };
301
327
  }
302
328
  function buildOutputConfidence(input) {
303
- let confidence = 0;
304
- if (input.finalStatus !== undefined) {
305
- confidence += 0.55;
306
- }
307
- if (input.errors.length > 0 || input.warnings.length > 0) {
308
- confidence += 0.25;
309
- }
310
- if (input.compiledCount > 0) {
311
- confidence += 0.1;
312
- }
313
- if (/\b(?:Creating an optimized production build|Route \(app\)|First Load JS|next\.js|webpack|compiled)\b/i.test(input.text)) {
314
- confidence += 0.15;
315
- }
316
- return Math.min(1, roundConfidence(confidence));
329
+ let confidence = 0;
330
+ if (input.finalStatus !== undefined) {
331
+ confidence += 0.55;
332
+ }
333
+ if (input.errors.length > 0 || input.warnings.length > 0) {
334
+ confidence += 0.25;
335
+ }
336
+ if (input.compiledCount > 0) {
337
+ confidence += 0.1;
338
+ }
339
+ if (
340
+ /\b(?:Creating an optimized production build|Route \(app\)|First Load JS|next\.js|webpack|compiled)\b/i.test(
341
+ input.text,
342
+ )
343
+ ) {
344
+ confidence += 0.15;
345
+ }
346
+ return Math.min(1, roundConfidence(confidence));
317
347
  }
318
348
  function buildOutputFacts(details) {
319
- const facts = [
320
- { name: "build", value: `${details.errorCount} errors, ${details.warningCount} warnings` },
321
- ];
322
- const files = uniqueStrings([...details.errorFiles, ...details.warningFiles].map(basename));
323
- if (files.length > 0) {
324
- facts.push({ name: "files", value: files.slice(0, 6).join(", ") });
325
- }
326
- return facts;
349
+ const facts = [{ name: "build", value: `${details.errorCount} errors, ${details.warningCount} warnings` }];
350
+ const files = uniqueStrings([...details.errorFiles, ...details.warningFiles].map(basename));
351
+ if (files.length > 0) {
352
+ facts.push({ name: "files", value: files.slice(0, 6).join(", ") });
353
+ }
354
+ return facts;
327
355
  }
328
356
  function renderBuildOutputSummary(details) {
329
- const lines = [
330
- "build summary",
331
- `build: ${details.errorCount} errors, ${details.warningCount} warnings`,
332
- ];
333
- const files = uniqueStrings([...details.errorFiles, ...details.warningFiles].map(basename));
334
- if (files.length > 0) {
335
- lines.push(`files: ${files.slice(0, 6).join(", ")}`);
336
- }
337
- if (details.finalStatus) {
338
- lines.push(`final: ${details.finalStatus}`);
339
- }
340
- return lines.join("\n");
357
+ const lines = ["build summary", `build: ${details.errorCount} errors, ${details.warningCount} warnings`];
358
+ const files = uniqueStrings([...details.errorFiles, ...details.warningFiles].map(basename));
359
+ if (files.length > 0) {
360
+ lines.push(`files: ${files.slice(0, 6).join(", ")}`);
361
+ }
362
+ if (details.finalStatus) {
363
+ lines.push(`final: ${details.finalStatus}`);
364
+ }
365
+ return lines.join("\n");
341
366
  }
342
367
  export function reduceDiagnosticsOutput(text) {
343
- const diagnostics = parseDiagnostics(text);
344
- const confidence = diagnosticsConfidence(diagnostics, text);
345
- const candidate = {
346
- ...DIAGNOSTICS_REDUCER,
347
- confidence,
348
- reason: `Detected ${diagnostics.length} TypeScript/lint diagnostic line(s).`,
349
- };
350
- if (confidence < 0.8 || diagnostics.length === 0) {
351
- return { candidate };
352
- }
353
- const details = summarizeDiagnostics(diagnostics);
354
- return {
355
- candidate,
356
- result: {
357
- ...DIAGNOSTICS_REDUCER,
358
- confidence,
359
- reason: candidate.reason,
360
- facts: diagnosticsFacts(details),
361
- visibleText: renderDiagnosticsSummary(details),
362
- details,
363
- },
364
- };
368
+ const diagnostics = parseDiagnostics(text);
369
+ const confidence = diagnosticsConfidence(diagnostics, text);
370
+ const candidate = {
371
+ ...DIAGNOSTICS_REDUCER,
372
+ confidence,
373
+ reason: `Detected ${diagnostics.length} TypeScript/lint diagnostic line(s).`,
374
+ };
375
+ if (confidence < 0.8 || diagnostics.length === 0) {
376
+ return { candidate };
377
+ }
378
+ const details = summarizeDiagnostics(diagnostics);
379
+ return {
380
+ candidate,
381
+ result: {
382
+ ...DIAGNOSTICS_REDUCER,
383
+ confidence,
384
+ reason: candidate.reason,
385
+ facts: diagnosticsFacts(details),
386
+ visibleText: renderDiagnosticsSummary(details),
387
+ details,
388
+ },
389
+ };
365
390
  }
366
391
  function parseDiagnostics(text) {
367
- const diagnostics = [];
368
- let currentLintFile;
369
- for (const rawLine of text.split(/\r?\n/)) {
370
- const line = stripAnsi(rawLine);
371
- const ts = /^(.+?)\((\d+),(\d+)\):\s+(error|warning)\s+(TS\d+):\s+(.+)$/.exec(line);
372
- if (ts) {
373
- diagnostics.push({
374
- file: ts[1] ?? "unknown",
375
- line: Number(ts[2]),
376
- column: Number(ts[3]),
377
- severity: (ts[4] ?? "error"),
378
- code: ts[5] ?? "TS????",
379
- message: ts[6] ?? "",
380
- });
381
- continue;
382
- }
383
- const compactLint = /^(.+?\.[cm]?[jt]sx?):(\d+):(\d+):\s+(error|warning)\s+(.+?)(?:\s+([@A-Za-z0-9_/-]+))?\s*$/.exec(line);
384
- if (compactLint) {
385
- diagnostics.push({
386
- file: compactLint[1] ?? "unknown",
387
- line: Number(compactLint[2]),
388
- column: Number(compactLint[3]),
389
- severity: (compactLint[4] ?? "error"),
390
- ...(compactLint[6] !== undefined ? { code: compactLint[6] } : {}),
391
- message: compactLint[5] ?? "",
392
- });
393
- continue;
394
- }
395
- const lintFile = /^\s*([^\s].+\.[cm]?[jt]sx?)\s*$/.exec(line);
396
- if (lintFile) {
397
- currentLintFile = lintFile[1];
398
- continue;
399
- }
400
- const stylishLint = /^\s*(\d+):(\d+)\s+(error|warning)\s+(.+?)\s+([@A-Za-z0-9_/-]+)\s*$/.exec(line);
401
- if (stylishLint && currentLintFile) {
402
- diagnostics.push({
403
- file: currentLintFile,
404
- line: Number(stylishLint[1]),
405
- column: Number(stylishLint[2]),
406
- severity: (stylishLint[3] ?? "error"),
407
- message: stylishLint[4] ?? "",
408
- ...(stylishLint[5] !== undefined ? { code: stylishLint[5] } : {}),
409
- });
410
- }
411
- }
412
- return diagnostics;
392
+ const diagnostics = [];
393
+ let currentLintFile;
394
+ for (const rawLine of text.split(/\r?\n/)) {
395
+ const line = stripAnsi(rawLine);
396
+ const ts = /^(.+?)\((\d+),(\d+)\):\s+(error|warning)\s+(TS\d+):\s+(.+)$/.exec(line);
397
+ if (ts) {
398
+ diagnostics.push({
399
+ file: ts[1] ?? "unknown",
400
+ line: Number(ts[2]),
401
+ column: Number(ts[3]),
402
+ severity: ts[4] ?? "error",
403
+ code: ts[5] ?? "TS????",
404
+ message: ts[6] ?? "",
405
+ });
406
+ continue;
407
+ }
408
+ const compactLint =
409
+ /^(.+?\.[cm]?[jt]sx?):(\d+):(\d+):\s+(error|warning)\s+(.+?)(?:\s+([@A-Za-z0-9_/-]+))?\s*$/.exec(line);
410
+ if (compactLint) {
411
+ diagnostics.push({
412
+ file: compactLint[1] ?? "unknown",
413
+ line: Number(compactLint[2]),
414
+ column: Number(compactLint[3]),
415
+ severity: compactLint[4] ?? "error",
416
+ ...(compactLint[6] !== undefined ? { code: compactLint[6] } : {}),
417
+ message: compactLint[5] ?? "",
418
+ });
419
+ continue;
420
+ }
421
+ const lintFile = /^\s*([^\s].+\.[cm]?[jt]sx?)\s*$/.exec(line);
422
+ if (lintFile) {
423
+ currentLintFile = lintFile[1];
424
+ continue;
425
+ }
426
+ const stylishLint = /^\s*(\d+):(\d+)\s+(error|warning)\s+(.+?)\s+([@A-Za-z0-9_/-]+)\s*$/.exec(line);
427
+ if (stylishLint && currentLintFile) {
428
+ diagnostics.push({
429
+ file: currentLintFile,
430
+ line: Number(stylishLint[1]),
431
+ column: Number(stylishLint[2]),
432
+ severity: stylishLint[3] ?? "error",
433
+ message: stylishLint[4] ?? "",
434
+ ...(stylishLint[5] !== undefined ? { code: stylishLint[5] } : {}),
435
+ });
436
+ }
437
+ }
438
+ return diagnostics;
413
439
  }
414
440
  function diagnosticsConfidence(diagnostics, text) {
415
- const hasTsCodes = diagnostics.some((diagnostic) => diagnostic.code?.startsWith("TS"));
416
- const hasLintRules = diagnostics.some((diagnostic) => diagnostic.code !== undefined && !diagnostic.code.startsWith("TS"));
417
- const hasDiagnosticSummary = /\b(?:error TS\d+|problems?\s+\(|eslint|\d+\s+errors?)\b/i.test(text);
418
- if (diagnostics.length >= 3 && hasTsCodes) {
419
- return 1;
420
- }
421
- if (diagnostics.length >= 2 && hasLintRules) {
422
- return 0.95;
423
- }
424
- if (diagnostics.length >= 2 && hasDiagnosticSummary) {
425
- return 0.85;
426
- }
427
- if (diagnostics.length === 1 && (hasTsCodes || hasLintRules) && hasDiagnosticSummary) {
428
- return 0.75;
429
- }
430
- return roundConfidence(Math.min(0.7, diagnostics.length * 0.2));
441
+ const hasTsCodes = diagnostics.some((diagnostic) => diagnostic.code?.startsWith("TS"));
442
+ const hasLintRules = diagnostics.some(
443
+ (diagnostic) => diagnostic.code !== undefined && !diagnostic.code.startsWith("TS"),
444
+ );
445
+ const hasDiagnosticSummary = /\b(?:error TS\d+|problems?\s+\(|eslint|\d+\s+errors?)\b/i.test(text);
446
+ if (diagnostics.length >= 3 && hasTsCodes) {
447
+ return 1;
448
+ }
449
+ if (diagnostics.length >= 2 && hasLintRules) {
450
+ return 0.95;
451
+ }
452
+ if (diagnostics.length >= 2 && hasDiagnosticSummary) {
453
+ return 0.85;
454
+ }
455
+ if (diagnostics.length === 1 && (hasTsCodes || hasLintRules) && hasDiagnosticSummary) {
456
+ return 0.75;
457
+ }
458
+ return roundConfidence(Math.min(0.7, diagnostics.length * 0.2));
431
459
  }
432
460
  function summarizeDiagnostics(diagnostics) {
433
- const fileCounts = new Map();
434
- const codeCounts = new Map();
435
- let errorCount = 0;
436
- let warningCount = 0;
437
- for (const diagnostic of diagnostics) {
438
- fileCounts.set(diagnostic.file, (fileCounts.get(diagnostic.file) ?? 0) + 1);
439
- if (diagnostic.code) {
440
- codeCounts.set(diagnostic.code, (codeCounts.get(diagnostic.code) ?? 0) + 1);
441
- }
442
- if (diagnostic.severity === "warning") {
443
- warningCount += 1;
444
- }
445
- else {
446
- errorCount += 1;
447
- }
448
- }
449
- return {
450
- kind: "diagnostics",
451
- total: diagnostics.length,
452
- errorCount,
453
- warningCount,
454
- fileCount: fileCounts.size,
455
- topFiles: topCounts(fileCounts, "file").map(({ key, count }) => ({ file: key, count })),
456
- topCodes: topCounts(codeCounts, "code").map(({ key, count }) => ({ code: key, count })),
457
- firstDiagnostics: diagnostics.slice(0, 5).map((diagnostic) => ({ ...diagnostic })),
458
- };
461
+ const fileCounts = new Map();
462
+ const codeCounts = new Map();
463
+ let errorCount = 0;
464
+ let warningCount = 0;
465
+ for (const diagnostic of diagnostics) {
466
+ fileCounts.set(diagnostic.file, (fileCounts.get(diagnostic.file) ?? 0) + 1);
467
+ if (diagnostic.code) {
468
+ codeCounts.set(diagnostic.code, (codeCounts.get(diagnostic.code) ?? 0) + 1);
469
+ }
470
+ if (diagnostic.severity === "warning") {
471
+ warningCount += 1;
472
+ } else {
473
+ errorCount += 1;
474
+ }
475
+ }
476
+ return {
477
+ kind: "diagnostics",
478
+ total: diagnostics.length,
479
+ errorCount,
480
+ warningCount,
481
+ fileCount: fileCounts.size,
482
+ topFiles: topCounts(fileCounts, "file").map(({ key, count }) => ({ file: key, count })),
483
+ topCodes: topCounts(codeCounts, "code").map(({ key, count }) => ({ code: key, count })),
484
+ firstDiagnostics: diagnostics.slice(0, 5).map((diagnostic) => ({ ...diagnostic })),
485
+ };
459
486
  }
460
487
  function diagnosticsFacts(details) {
461
- const facts = [
462
- { name: "diagnostics", value: details.warningCount > 0 ? `${details.total} total` : `${details.total} errors` },
463
- { name: "files", value: details.fileCount },
464
- ];
465
- if (details.warningCount > 0) {
466
- facts.push({ name: "errors", value: details.errorCount });
467
- facts.push({ name: "warnings", value: details.warningCount });
468
- }
469
- if (details.topFiles.length > 0) {
470
- facts.push({ name: "topFiles", value: details.topFiles.slice(0, 3).map(({ file, count }) => `${basename(file)}:${count}`).join(", ") });
471
- }
472
- if (details.topCodes.length > 0) {
473
- facts.push({ name: "topCodes", value: details.topCodes.slice(0, 3).map(({ code, count }) => `${code}:${count}`).join(", ") });
474
- }
475
- return facts;
488
+ const facts = [
489
+ { name: "diagnostics", value: details.warningCount > 0 ? `${details.total} total` : `${details.total} errors` },
490
+ { name: "files", value: details.fileCount },
491
+ ];
492
+ if (details.warningCount > 0) {
493
+ facts.push({ name: "errors", value: details.errorCount });
494
+ facts.push({ name: "warnings", value: details.warningCount });
495
+ }
496
+ if (details.topFiles.length > 0) {
497
+ facts.push({
498
+ name: "topFiles",
499
+ value: details.topFiles
500
+ .slice(0, 3)
501
+ .map(({ file, count }) => `${basename(file)}:${count}`)
502
+ .join(", "),
503
+ });
504
+ }
505
+ if (details.topCodes.length > 0) {
506
+ facts.push({
507
+ name: "topCodes",
508
+ value: details.topCodes
509
+ .slice(0, 3)
510
+ .map(({ code, count }) => `${code}:${count}`)
511
+ .join(", "),
512
+ });
513
+ }
514
+ return facts;
476
515
  }
477
516
  function renderDiagnosticsSummary(details) {
478
- const lines = [
479
- "diagnostics summary",
480
- `diagnostics: ${details.total}`,
481
- `files: ${details.fileCount}`,
482
- `errors: ${details.errorCount}`,
483
- ];
484
- if (details.warningCount > 0) {
485
- lines.push(`warnings: ${details.warningCount}`);
486
- }
487
- if (details.topFiles.length > 0) {
488
- lines.push(`topFiles: ${details.topFiles.map(({ file, count }) => `${file}:${count}`).join(", ")}`);
489
- }
490
- if (details.topCodes.length > 0) {
491
- lines.push(`topCodes: ${details.topCodes.map(({ code, count }) => `${code}:${count}`).join(", ")}`);
492
- }
493
- return lines.join("\n");
517
+ const lines = [
518
+ "diagnostics summary",
519
+ `diagnostics: ${details.total}`,
520
+ `files: ${details.fileCount}`,
521
+ `errors: ${details.errorCount}`,
522
+ ];
523
+ if (details.warningCount > 0) {
524
+ lines.push(`warnings: ${details.warningCount}`);
525
+ }
526
+ if (details.topFiles.length > 0) {
527
+ lines.push(`topFiles: ${details.topFiles.map(({ file, count }) => `${file}:${count}`).join(", ")}`);
528
+ }
529
+ if (details.topCodes.length > 0) {
530
+ lines.push(`topCodes: ${details.topCodes.map(({ code, count }) => `${code}:${count}`).join(", ")}`);
531
+ }
532
+ return lines.join("\n");
494
533
  }
495
534
  function topCounts(map, tieBreaker) {
496
- return [...map.entries()]
497
- .sort(([leftKey, leftCount], [rightKey, rightCount]) => {
498
- if (rightCount !== leftCount) {
499
- return rightCount - leftCount;
500
- }
501
- return tieBreaker === "file" ? leftKey.localeCompare(rightKey) : leftKey.localeCompare(rightKey, undefined, { numeric: true });
535
+ return [...map.entries()]
536
+ .sort(([leftKey, leftCount], [rightKey, rightCount]) => {
537
+ if (rightCount !== leftCount) {
538
+ return rightCount - leftCount;
539
+ }
540
+ return tieBreaker === "file"
541
+ ? leftKey.localeCompare(rightKey)
542
+ : leftKey.localeCompare(rightKey, undefined, { numeric: true });
502
543
  })
503
- .slice(0, 5)
504
- .map(([key, count]) => ({ key, count }));
544
+ .slice(0, 5)
545
+ .map(([key, count]) => ({ key, count }));
505
546
  }
506
547
  function stripAnsi(text) {
507
- return text.replace(/\u001b\[[0-9;]*m/g, "");
548
+ return text.replace(/\u001b\[[0-9;]*m/g, "");
508
549
  }
509
550
  function basename(path) {
510
- return path.split(/[\\/]/).pop() || path;
551
+ return path.split(/[\\/]/).pop() || path;
511
552
  }
512
553
  export function reduceMcpToolsOutput(text) {
513
- const tools = parseMcpToolsList(text);
514
- const confidence = mcpToolsConfidence(tools);
515
- const candidate = {
516
- ...MCP_TOOLS_REDUCER,
517
- confidence,
518
- reason: tools === undefined
519
- ? "No MCP tools/list JSON shape detected."
520
- : `Detected MCP tools/list JSON with ${tools.length} tool(s).`,
521
- };
522
- if (tools === undefined || confidence < 0.8) {
523
- return { candidate };
524
- }
525
- const details = summarizeMcpTools(tools);
526
- return {
527
- candidate,
528
- result: {
529
- ...MCP_TOOLS_REDUCER,
530
- confidence,
531
- reason: candidate.reason,
532
- facts: mcpToolsFacts(details),
533
- visibleText: renderMcpToolsSummary(details),
534
- details,
535
- },
536
- };
554
+ const tools = parseMcpToolsList(text);
555
+ const confidence = mcpToolsConfidence(tools);
556
+ const candidate = {
557
+ ...MCP_TOOLS_REDUCER,
558
+ confidence,
559
+ reason:
560
+ tools === undefined
561
+ ? "No MCP tools/list JSON shape detected."
562
+ : `Detected MCP tools/list JSON with ${tools.length} tool(s).`,
563
+ };
564
+ if (tools === undefined || confidence < 0.8) {
565
+ return { candidate };
566
+ }
567
+ const details = summarizeMcpTools(tools);
568
+ return {
569
+ candidate,
570
+ result: {
571
+ ...MCP_TOOLS_REDUCER,
572
+ confidence,
573
+ reason: candidate.reason,
574
+ facts: mcpToolsFacts(details),
575
+ visibleText: renderMcpToolsSummary(details),
576
+ details,
577
+ },
578
+ };
537
579
  }
538
580
  function parseMcpToolsList(text) {
539
- let value;
540
- try {
541
- value = JSON.parse(text);
542
- }
543
- catch {
544
- return undefined;
545
- }
546
- const rawTools = Array.isArray(value)
547
- ? value
548
- : isJsonObject(value) && Array.isArray(value.tools)
549
- ? value.tools
550
- : undefined;
551
- if (rawTools === undefined || rawTools.length < 1 || !rawTools.every(isJsonObject)) {
552
- return undefined;
553
- }
554
- const tools = [];
555
- for (const tool of rawTools) {
556
- const name = typeof tool.name === "string" ? tool.name.trim() : "";
557
- if (!isMcpToolName(name) || !isJsonObject(tool.inputSchema)) {
558
- return undefined;
559
- }
560
- const description = typeof tool.description === "string" && tool.description.trim().length > 0 ? oneLine(tool.description, 160) : undefined;
561
- tools.push({
562
- name,
563
- ...(description !== undefined ? { description } : {}),
564
- inputSchema: tool.inputSchema,
565
- });
566
- }
567
- return uniqueStrings(tools.map((tool) => tool.name)).length === tools.length ? tools : undefined;
581
+ let value;
582
+ try {
583
+ value = JSON.parse(text);
584
+ } catch {
585
+ return undefined;
586
+ }
587
+ const rawTools = Array.isArray(value)
588
+ ? value
589
+ : isJsonObject(value) && Array.isArray(value.tools)
590
+ ? value.tools
591
+ : undefined;
592
+ if (rawTools === undefined || rawTools.length < 1 || !rawTools.every(isJsonObject)) {
593
+ return undefined;
594
+ }
595
+ const tools = [];
596
+ for (const tool of rawTools) {
597
+ const name = typeof tool.name === "string" ? tool.name.trim() : "";
598
+ if (!isMcpToolName(name) || !isJsonObject(tool.inputSchema)) {
599
+ return undefined;
600
+ }
601
+ const description =
602
+ typeof tool.description === "string" && tool.description.trim().length > 0
603
+ ? oneLine(tool.description, 160)
604
+ : undefined;
605
+ tools.push({
606
+ name,
607
+ ...(description !== undefined ? { description } : {}),
608
+ inputSchema: tool.inputSchema,
609
+ });
610
+ }
611
+ return uniqueStrings(tools.map((tool) => tool.name)).length === tools.length ? tools : undefined;
568
612
  }
569
613
  function mcpToolsConfidence(tools) {
570
- if (tools === undefined) {
571
- return 0;
572
- }
573
- if (tools.length >= 10) {
574
- return 1;
575
- }
576
- if (tools.length >= 2) {
577
- return 0.9;
578
- }
579
- return 0.85;
614
+ if (tools === undefined) {
615
+ return 0;
616
+ }
617
+ if (tools.length >= 10) {
618
+ return 1;
619
+ }
620
+ if (tools.length >= 2) {
621
+ return 0.9;
622
+ }
623
+ return 0.85;
580
624
  }
581
625
  function summarizeMcpTools(tools) {
582
- const signatures = tools.map((tool) => {
583
- const required = schemaRequiredProperties(tool.inputSchema);
584
- const parameters = schemaPropertyNames(tool.inputSchema).map((property) => required.includes(property) ? property : `${property}?`);
585
- return {
586
- name: tool.name,
587
- category: mcpToolCategory(tool.name),
588
- parameters,
589
- required,
590
- ...(tool.description !== undefined ? { description: tool.description } : {}),
591
- };
592
- });
626
+ const signatures = tools.map((tool) => {
627
+ const required = schemaRequiredProperties(tool.inputSchema);
628
+ const parameters = schemaPropertyNames(tool.inputSchema).map((property) =>
629
+ required.includes(property) ? property : `${property}?`,
630
+ );
593
631
  return {
594
- kind: "mcp-tools",
595
- toolCount: tools.length,
596
- categories: countValues(signatures.map((signature) => signature.category)).map(({ value, count }) => ({ category: value, count })),
597
- signatures,
632
+ name: tool.name,
633
+ category: mcpToolCategory(tool.name),
634
+ parameters,
635
+ required,
636
+ ...(tool.description !== undefined ? { description: tool.description } : {}),
598
637
  };
638
+ });
639
+ return {
640
+ kind: "mcp-tools",
641
+ toolCount: tools.length,
642
+ categories: countValues(signatures.map((signature) => signature.category)).map(({ value, count }) => ({
643
+ category: value,
644
+ count,
645
+ })),
646
+ signatures,
647
+ };
599
648
  }
600
649
  function mcpToolsFacts(details) {
601
- return [
602
- { name: "tools", value: details.toolCount },
603
- { name: "categories", value: details.categories.map(({ category, count }) => `${category}:${count}`).join(", ") },
604
- { name: "signatures", value: details.signatures.slice(0, 10).map(renderMcpToolSignature).join(", ") },
605
- ];
650
+ return [
651
+ { name: "tools", value: details.toolCount },
652
+ { name: "categories", value: details.categories.map(({ category, count }) => `${category}:${count}`).join(", ") },
653
+ { name: "signatures", value: details.signatures.slice(0, 10).map(renderMcpToolSignature).join(", ") },
654
+ ];
606
655
  }
607
656
  function renderMcpToolsSummary(details) {
608
- return [
609
- "mcp tools summary",
610
- `tools: ${details.toolCount}`,
611
- `categories: ${details.categories.map(({ category, count }) => `${category}:${count}`).join(", ")}`,
612
- `signatures: ${details.signatures.slice(0, 10).map(renderMcpToolSignature).join(", ")}`,
613
- ].join("\n");
657
+ return [
658
+ "mcp tools summary",
659
+ `tools: ${details.toolCount}`,
660
+ `categories: ${details.categories.map(({ category, count }) => `${category}:${count}`).join(", ")}`,
661
+ `signatures: ${details.signatures.slice(0, 10).map(renderMcpToolSignature).join(", ")}`,
662
+ ].join("\n");
614
663
  }
615
664
  function renderMcpToolSignature(signature) {
616
- return `${signature.name}(${signature.parameters.join(", ")})`;
665
+ return `${signature.name}(${signature.parameters.join(", ")})`;
617
666
  }
618
667
  function schemaPropertyNames(schema) {
619
- return isJsonObject(schema.properties) ? Object.keys(schema.properties).filter(isMcpParameterName) : [];
668
+ return isJsonObject(schema.properties) ? Object.keys(schema.properties).filter(isMcpParameterName) : [];
620
669
  }
621
670
  function schemaRequiredProperties(schema) {
622
- return Array.isArray(schema.required) ? schema.required.filter((property) => typeof property === "string" && isMcpParameterName(property)) : [];
671
+ return Array.isArray(schema.required)
672
+ ? schema.required.filter((property) => typeof property === "string" && isMcpParameterName(property))
673
+ : [];
623
674
  }
624
675
  function mcpToolCategory(name) {
625
- return name.split("_")[0] || "other";
676
+ return name.split("_")[0] || "other";
626
677
  }
627
678
  function isMcpToolName(value) {
628
- return /^[A-Za-z][A-Za-z0-9_-]*$/.test(value);
679
+ return /^[A-Za-z][A-Za-z0-9_-]*$/.test(value);
629
680
  }
630
681
  function isMcpParameterName(value) {
631
- return /^[A-Za-z_$][A-Za-z0-9_$.-]*$/.test(value);
682
+ return /^[A-Za-z_$][A-Za-z0-9_$.-]*$/.test(value);
632
683
  }
633
684
  function oneLine(text, maxChars) {
634
- const compact = text.replace(/\s+/g, " ").trim();
635
- return compact.length <= maxChars ? compact : `${compact.slice(0, maxChars - 1)}…`;
685
+ const compact = text.replace(/\s+/g, " ").trim();
686
+ return compact.length <= maxChars ? compact : `${compact.slice(0, maxChars - 1)}…`;
636
687
  }
637
688
  export function reduceJsonQueryOutput(text, goal) {
638
- const indexed = indexJsonForQuery(text);
639
- const goalTokens = tokenizeGoal(goal ?? "");
640
- const details = indexed ? summarizeJsonQuery(indexed, goalTokens) : undefined;
641
- const confidence = jsonQueryConfidence(indexed, details, goalTokens);
642
- const candidate = {
643
- ...JSON_QUERY_REDUCER,
644
- confidence,
645
- reason: indexed === undefined
646
- ? "No JSON object/array shape detected."
647
- : `Indexed ${indexed.rootKind} JSON with ${indexed.entries.length} primitive value(s), ${details?.matchedPaths.length ?? 0} matched path(s), and ${goalTokens.size} goal token(s).`,
648
- };
649
- if (indexed === undefined || details === undefined || confidence < 0.8) {
650
- return { candidate };
651
- }
652
- return {
653
- candidate,
654
- result: {
655
- ...JSON_QUERY_REDUCER,
656
- confidence,
657
- reason: candidate.reason,
658
- facts: jsonQueryFacts(details),
659
- visibleText: renderJsonQuerySummary(details),
660
- details,
661
- },
662
- };
689
+ const indexed = indexJsonForQuery(text);
690
+ const goalTokens = tokenizeGoal(goal ?? "");
691
+ const details = indexed ? summarizeJsonQuery(indexed, goalTokens) : undefined;
692
+ const confidence = jsonQueryConfidence(indexed, details, goalTokens);
693
+ const candidate = {
694
+ ...JSON_QUERY_REDUCER,
695
+ confidence,
696
+ reason:
697
+ indexed === undefined
698
+ ? "No JSON object/array shape detected."
699
+ : `Indexed ${indexed.rootKind} JSON with ${indexed.entries.length} primitive value(s), ${details?.matchedPaths.length ?? 0} matched path(s), and ${goalTokens.size} goal token(s).`,
700
+ };
701
+ if (indexed === undefined || details === undefined || confidence < 0.8) {
702
+ return { candidate };
703
+ }
704
+ return {
705
+ candidate,
706
+ result: {
707
+ ...JSON_QUERY_REDUCER,
708
+ confidence,
709
+ reason: candidate.reason,
710
+ facts: jsonQueryFacts(details),
711
+ visibleText: renderJsonQuerySummary(details),
712
+ details,
713
+ },
714
+ };
663
715
  }
664
716
  function indexJsonForQuery(text) {
665
- let value;
666
- try {
667
- value = JSON.parse(text);
668
- }
669
- catch {
670
- return undefined;
671
- }
672
- if (!Array.isArray(value) && !isJsonObject(value)) {
673
- return undefined;
674
- }
675
- const entries = [];
676
- if (Array.isArray(value)) {
677
- value.slice(0, 500).forEach((item, itemIndex) => flattenJsonValue(item, "", entries, itemIndex, 0));
678
- return { rootKind: "array", itemCount: value.length, entries, rootItems: value.slice(0, 20) };
679
- }
680
- flattenJsonValue(value, "", entries, undefined, 0);
681
- return { rootKind: "object", entries, rootItems: [value] };
717
+ let value;
718
+ try {
719
+ value = JSON.parse(text);
720
+ } catch {
721
+ return undefined;
722
+ }
723
+ if (!Array.isArray(value) && !isJsonObject(value)) {
724
+ return undefined;
725
+ }
726
+ const entries = [];
727
+ if (Array.isArray(value)) {
728
+ value.slice(0, 500).forEach((item, itemIndex) => flattenJsonValue(item, "", entries, itemIndex, 0));
729
+ return { rootKind: "array", itemCount: value.length, entries, rootItems: value.slice(0, 20) };
730
+ }
731
+ flattenJsonValue(value, "", entries, undefined, 0);
732
+ return { rootKind: "object", entries, rootItems: [value] };
682
733
  }
683
734
  function flattenJsonValue(value, path, entries, itemIndex, depth) {
684
- if (entries.length >= 5_000 || depth > 6) {
685
- return;
686
- }
687
- if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
688
- if (path.length > 0) {
689
- entries.push({
690
- path,
691
- value: typeof value === "string" ? oneLine(value, 2_000) : value,
692
- ...(itemIndex !== undefined ? { itemIndex } : {}),
693
- });
694
- }
695
- return;
696
- }
697
- if (Array.isArray(value)) {
698
- const nextPath = path.length > 0 ? `${path}[]` : "[]";
699
- for (const item of value.slice(0, 100)) {
700
- flattenJsonValue(item, nextPath, entries, itemIndex, depth + 1);
701
- }
702
- return;
703
- }
704
- if (isJsonObject(value)) {
705
- for (const [key, nested] of Object.entries(value)) {
706
- const safeKey = key.replace(/\s+/g, "_");
707
- const nextPath = path.length > 0 ? `${path}.${safeKey}` : safeKey;
708
- flattenJsonValue(nested, nextPath, entries, itemIndex, depth + 1);
709
- }
710
- }
735
+ if (entries.length >= 5_000 || depth > 6) {
736
+ return;
737
+ }
738
+ if (value === null || typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
739
+ if (path.length > 0) {
740
+ entries.push({
741
+ path,
742
+ value: typeof value === "string" ? oneLine(value, 2_000) : value,
743
+ ...(itemIndex !== undefined ? { itemIndex } : {}),
744
+ });
745
+ }
746
+ return;
747
+ }
748
+ if (Array.isArray(value)) {
749
+ const nextPath = path.length > 0 ? `${path}[]` : "[]";
750
+ for (const item of value.slice(0, 100)) {
751
+ flattenJsonValue(item, nextPath, entries, itemIndex, depth + 1);
752
+ }
753
+ return;
754
+ }
755
+ if (isJsonObject(value)) {
756
+ for (const [key, nested] of Object.entries(value)) {
757
+ const safeKey = key.replace(/\s+/g, "_");
758
+ const nextPath = path.length > 0 ? `${path}.${safeKey}` : safeKey;
759
+ flattenJsonValue(nested, nextPath, entries, itemIndex, depth + 1);
760
+ }
761
+ }
711
762
  }
712
763
  function summarizeJsonQuery(index, goalTokens) {
713
- const categorical = summarizeJsonCategorical(index.entries, goalTokens);
714
- const numeric = summarizeJsonNumeric(index.entries, goalTokens);
715
- const mentions = summarizeJsonMentions(index.entries);
716
- const samples = summarizeJsonSamples(index.rootItems);
717
- const matchedPaths = [
718
- ...categorical.slice(0, 5).map((summary) => ({ path: summary.path, score: summary.score, reason: "categorical path/value overlap" })),
719
- ...numeric.slice(0, 3).map((summary) => ({ path: summary.path, score: summary.score, reason: "numeric path/value overlap" })),
720
- ...mentions.slice(0, 3).map((mention) => ({ path: `mentions.${mention.kind}`, score: 10, reason: "structured mention extracted from JSON string values" })),
721
- ].sort((left, right) => right.score - left.score || left.path.localeCompare(right.path));
722
- return {
723
- kind: "json-query",
724
- rootKind: index.rootKind,
725
- ...(index.itemCount !== undefined ? { itemCount: index.itemCount } : {}),
726
- matchedPaths,
727
- categorical,
728
- numeric,
729
- mentions,
730
- samples,
731
- };
764
+ const categorical = summarizeJsonCategorical(index.entries, goalTokens);
765
+ const numeric = summarizeJsonNumeric(index.entries, goalTokens);
766
+ const mentions = summarizeJsonMentions(index.entries);
767
+ const samples = summarizeJsonSamples(index.rootItems);
768
+ const matchedPaths = [
769
+ ...categorical
770
+ .slice(0, 5)
771
+ .map((summary) => ({ path: summary.path, score: summary.score, reason: "categorical path/value overlap" })),
772
+ ...numeric
773
+ .slice(0, 3)
774
+ .map((summary) => ({ path: summary.path, score: summary.score, reason: "numeric path/value overlap" })),
775
+ ...mentions.slice(0, 3).map((mention) => ({
776
+ path: `mentions.${mention.kind}`,
777
+ score: 10,
778
+ reason: "structured mention extracted from JSON string values",
779
+ })),
780
+ ].sort((left, right) => right.score - left.score || left.path.localeCompare(right.path));
781
+ return {
782
+ kind: "json-query",
783
+ rootKind: index.rootKind,
784
+ ...(index.itemCount !== undefined ? { itemCount: index.itemCount } : {}),
785
+ matchedPaths,
786
+ categorical,
787
+ numeric,
788
+ mentions,
789
+ samples,
790
+ };
732
791
  }
733
792
  function summarizeJsonCategorical(entries, goalTokens) {
734
- const byPath = groupJsonEntriesByPath(entries.filter((entry) => typeof entry.value === "string" || typeof entry.value === "boolean" || entry.value === null));
735
- const summaries = [];
736
- for (const [path, pathEntries] of byPath) {
737
- const values = pathEntries.map((entry) => String(entry.value ?? "null")).filter((value) => value.length > 0);
738
- if (values.length === 0) {
739
- continue;
740
- }
741
- const averageLength = values.reduce((sum, value) => sum + value.length, 0) / values.length;
742
- const counts = countValues(values);
743
- if (counts.length > 50 || (counts.length === values.length && !jsonPathIsSampleField(path)) || averageLength > 160) {
744
- continue;
745
- }
746
- const score = jsonPathScore(path, counts.map((count) => count.value), goalTokens);
747
- if (score <= 0 && !isHighSignalJsonPath(path)) {
748
- continue;
749
- }
750
- summaries.push({ path, counts: counts.slice(0, 8), score });
751
- }
752
- return summaries.sort((left, right) => right.score - left.score || left.path.localeCompare(right.path)).slice(0, 8);
793
+ const byPath = groupJsonEntriesByPath(
794
+ entries.filter(
795
+ (entry) => typeof entry.value === "string" || typeof entry.value === "boolean" || entry.value === null,
796
+ ),
797
+ );
798
+ const summaries = [];
799
+ for (const [path, pathEntries] of byPath) {
800
+ const values = pathEntries.map((entry) => String(entry.value ?? "null")).filter((value) => value.length > 0);
801
+ if (values.length === 0) {
802
+ continue;
803
+ }
804
+ const averageLength = values.reduce((sum, value) => sum + value.length, 0) / values.length;
805
+ const counts = countValues(values);
806
+ if (
807
+ counts.length > 50 ||
808
+ (counts.length === values.length && !jsonPathIsSampleField(path)) ||
809
+ averageLength > 160
810
+ ) {
811
+ continue;
812
+ }
813
+ const score = jsonPathScore(
814
+ path,
815
+ counts.map((count) => count.value),
816
+ goalTokens,
817
+ );
818
+ if (score <= 0 && !isHighSignalJsonPath(path)) {
819
+ continue;
820
+ }
821
+ summaries.push({ path, counts: counts.slice(0, 8), score });
822
+ }
823
+ return summaries.sort((left, right) => right.score - left.score || left.path.localeCompare(right.path)).slice(0, 8);
753
824
  }
754
825
  function summarizeJsonNumeric(entries, goalTokens) {
755
- const byPath = groupJsonEntriesByPath(entries.filter((entry) => typeof entry.value === "number" && Number.isFinite(entry.value)));
756
- const summaries = [];
757
- for (const [path, pathEntries] of byPath) {
758
- const numbers = pathEntries.map((entry) => Number(entry.value)).filter((value) => Number.isFinite(value));
759
- if (numbers.length === 0) {
760
- continue;
761
- }
762
- const total = numbers.reduce((sum, value) => sum + value, 0);
763
- const score = jsonPathScore(path, [], goalTokens);
764
- summaries.push({
765
- path,
766
- min: Math.min(...numbers),
767
- max: Math.max(...numbers),
768
- average: Math.round((total / numbers.length) * 10) / 10,
769
- score,
770
- });
771
- }
772
- return summaries.sort((left, right) => right.score - left.score || right.max - left.max || left.path.localeCompare(right.path)).slice(0, 5);
826
+ const byPath = groupJsonEntriesByPath(
827
+ entries.filter((entry) => typeof entry.value === "number" && Number.isFinite(entry.value)),
828
+ );
829
+ const summaries = [];
830
+ for (const [path, pathEntries] of byPath) {
831
+ const numbers = pathEntries.map((entry) => Number(entry.value)).filter((value) => Number.isFinite(value));
832
+ if (numbers.length === 0) {
833
+ continue;
834
+ }
835
+ const total = numbers.reduce((sum, value) => sum + value, 0);
836
+ const score = jsonPathScore(path, [], goalTokens);
837
+ summaries.push({
838
+ path,
839
+ min: Math.min(...numbers),
840
+ max: Math.max(...numbers),
841
+ average: Math.round((total / numbers.length) * 10) / 10,
842
+ score,
843
+ });
844
+ }
845
+ return summaries
846
+ .sort((left, right) => right.score - left.score || right.max - left.max || left.path.localeCompare(right.path))
847
+ .slice(0, 5);
773
848
  }
774
849
  function summarizeJsonMentions(entries) {
775
- const repoMentions = new Map();
776
- for (const entry of entries) {
777
- if (typeof entry.value !== "string") {
778
- continue;
779
- }
780
- const text = entry.value;
781
- for (const match of text.matchAll(/(?:github\.com\/|repo:)([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)/g)) {
782
- const value = match[1];
783
- if (!value) {
784
- continue;
785
- }
786
- const current = repoMentions.get(value) ?? { count: 0, paths: new Set() };
787
- current.count += 1;
788
- current.paths.add(entry.path);
789
- repoMentions.set(value, current);
790
- }
791
- }
792
- return [...repoMentions.entries()]
793
- .map(([value, mention]) => ({ kind: "githubRepo", value, count: mention.count, paths: [...mention.paths].slice(0, 5) }))
794
- .sort((left, right) => right.count - left.count || left.value.localeCompare(right.value))
795
- .slice(0, 5);
850
+ const repoMentions = new Map();
851
+ for (const entry of entries) {
852
+ if (typeof entry.value !== "string") {
853
+ continue;
854
+ }
855
+ const text = entry.value;
856
+ for (const match of text.matchAll(/(?:github\.com\/|repo:)([A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+)/g)) {
857
+ const value = match[1];
858
+ if (!value) {
859
+ continue;
860
+ }
861
+ const current = repoMentions.get(value) ?? { count: 0, paths: new Set() };
862
+ current.count += 1;
863
+ current.paths.add(entry.path);
864
+ repoMentions.set(value, current);
865
+ }
866
+ }
867
+ return [...repoMentions.entries()]
868
+ .map(([value, mention]) => ({
869
+ kind: "githubRepo",
870
+ value,
871
+ count: mention.count,
872
+ paths: [...mention.paths].slice(0, 5),
873
+ }))
874
+ .sort((left, right) => right.count - left.count || left.value.localeCompare(right.value))
875
+ .slice(0, 5);
796
876
  }
797
877
  function summarizeJsonSamples(items) {
798
- const samples = [];
799
- for (const item of items.slice(0, 5)) {
800
- if (!isJsonObject(item)) {
801
- continue;
802
- }
803
- const number = primitiveSampleValue(item.number);
804
- const title = primitiveSampleValue(item.title) ?? primitiveSampleValue(item.name);
805
- const state = primitiveSampleValue(item.state) ?? primitiveSampleValue(item.status);
806
- if (title === undefined && number === undefined) {
807
- continue;
808
- }
809
- const label = `${number !== undefined ? `#${number} ` : ""}${title ?? "item"}`;
810
- const paths = {};
811
- if (number !== undefined) {
812
- paths.number = number;
813
- }
814
- if (title !== undefined) {
815
- paths.title = title;
816
- }
817
- if (state !== undefined) {
818
- paths.state = state;
819
- }
820
- samples.push({ label: oneLine(label, 180), paths });
878
+ const samples = [];
879
+ for (const item of items.slice(0, 5)) {
880
+ if (!isJsonObject(item)) {
881
+ continue;
821
882
  }
822
- return samples;
823
- }
824
- function jsonQueryConfidence(index, details, goalTokens) {
825
- if (index === undefined || details === undefined || goalTokens.size === 0) {
826
- return 0;
883
+ const number = primitiveSampleValue(item.number);
884
+ const title = primitiveSampleValue(item.title) ?? primitiveSampleValue(item.name);
885
+ const state = primitiveSampleValue(item.state) ?? primitiveSampleValue(item.status);
886
+ if (title === undefined && number === undefined) {
887
+ continue;
827
888
  }
828
- if (details.mentions.some((mention) => jsonMentionOverlapsGoal(mention, goalTokens))) {
829
- return 0.95;
889
+ const label = `${number !== undefined ? `#${number} ` : ""}${title ?? "item"}`;
890
+ const paths = {};
891
+ if (number !== undefined) {
892
+ paths.number = number;
830
893
  }
831
- if (details.categorical.some((summary) => jsonCategoricalSummaryOverlapsGoal(summary, goalTokens)) || details.numeric.some((summary) => jsonPathOverlapsGoal(summary.path, [], goalTokens))) {
832
- return 0.9;
894
+ if (title !== undefined) {
895
+ paths.title = title;
833
896
  }
834
- return 0.6;
897
+ if (state !== undefined) {
898
+ paths.state = state;
899
+ }
900
+ samples.push({ label: oneLine(label, 180), paths });
901
+ }
902
+ return samples;
903
+ }
904
+ function jsonQueryConfidence(index, details, goalTokens) {
905
+ if (index === undefined || details === undefined || goalTokens.size === 0) {
906
+ return 0;
907
+ }
908
+ if (details.mentions.some((mention) => jsonMentionOverlapsGoal(mention, goalTokens))) {
909
+ return 0.95;
910
+ }
911
+ if (
912
+ details.categorical.some((summary) => jsonCategoricalSummaryOverlapsGoal(summary, goalTokens)) ||
913
+ details.numeric.some((summary) => jsonPathOverlapsGoal(summary.path, [], goalTokens))
914
+ ) {
915
+ return 0.9;
916
+ }
917
+ return 0.6;
835
918
  }
836
919
  function jsonMentionOverlapsGoal(mention, goalTokens) {
837
- if (mention.kind === "githubRepo" && (goalTokens.has("github") || goalTokens.has("repo") || goalTokens.has("repository"))) {
838
- return true;
839
- }
840
- return overlapCount(tokenizeGoal(mention.value), goalTokens) > 0;
920
+ if (
921
+ mention.kind === "githubRepo" &&
922
+ (goalTokens.has("github") || goalTokens.has("repo") || goalTokens.has("repository"))
923
+ ) {
924
+ return true;
925
+ }
926
+ return overlapCount(tokenizeGoal(mention.value), goalTokens) > 0;
841
927
  }
842
928
  function jsonCategoricalSummaryOverlapsGoal(summary, goalTokens) {
843
- return jsonPathOverlapsGoal(summary.path, summary.counts.map((count) => count.value), goalTokens);
929
+ return jsonPathOverlapsGoal(
930
+ summary.path,
931
+ summary.counts.map((count) => count.value),
932
+ goalTokens,
933
+ );
844
934
  }
845
935
  function jsonPathOverlapsGoal(path, values, goalTokens) {
846
- const pathTokens = tokenizeGoal(path);
847
- const valueTokens = new Set(values.flatMap((value) => [...tokenizeGoal(value)].slice(0, 8)));
848
- return overlapCount(pathTokens, goalTokens) > 0 || overlapCount(valueTokens, goalTokens) > 0;
936
+ const pathTokens = tokenizeGoal(path);
937
+ const valueTokens = new Set(values.flatMap((value) => [...tokenizeGoal(value)].slice(0, 8)));
938
+ return overlapCount(pathTokens, goalTokens) > 0 || overlapCount(valueTokens, goalTokens) > 0;
849
939
  }
850
940
  function jsonQueryFacts(details) {
851
- const facts = [];
852
- if (details.itemCount !== undefined) {
853
- facts.push({ name: "items", value: details.itemCount });
854
- }
855
- else {
856
- facts.push({ name: "root", value: details.rootKind });
857
- }
858
- const repoMentions = details.mentions.filter((mention) => mention.kind === "githubRepo");
859
- if (repoMentions.length > 0) {
860
- facts.push({ name: "githubRepo", value: repoMentions.map((mention) => `${mention.value}:${mention.count}`).join(", ") });
861
- }
862
- const categorical = details.categorical[0];
863
- if (categorical) {
864
- facts.push({ name: categorical.path, value: categorical.counts.map(({ value, count }) => `${value}:${count}`).join(", ") });
865
- }
866
- const sample = details.samples[0];
867
- if (sample) {
868
- facts.push({ name: "sample", value: sample.label });
869
- }
870
- return facts;
941
+ const facts = [];
942
+ if (details.itemCount !== undefined) {
943
+ facts.push({ name: "items", value: details.itemCount });
944
+ } else {
945
+ facts.push({ name: "root", value: details.rootKind });
946
+ }
947
+ const repoMentions = details.mentions.filter((mention) => mention.kind === "githubRepo");
948
+ if (repoMentions.length > 0) {
949
+ facts.push({
950
+ name: "githubRepo",
951
+ value: repoMentions.map((mention) => `${mention.value}:${mention.count}`).join(", "),
952
+ });
953
+ }
954
+ const categorical = details.categorical[0];
955
+ if (categorical) {
956
+ facts.push({
957
+ name: categorical.path,
958
+ value: categorical.counts.map(({ value, count }) => `${value}:${count}`).join(", "),
959
+ });
960
+ }
961
+ const sample = details.samples[0];
962
+ if (sample) {
963
+ facts.push({ name: "sample", value: sample.label });
964
+ }
965
+ return facts;
871
966
  }
872
967
  function renderJsonQuerySummary(details) {
873
- const lines = ["json query summary"];
874
- if (details.itemCount !== undefined) {
875
- lines.push(`items: ${details.itemCount}`);
876
- }
877
- else {
878
- lines.push(`root: ${details.rootKind}`);
879
- }
880
- const repoMentions = details.mentions.filter((mention) => mention.kind === "githubRepo");
881
- if (repoMentions.length > 0) {
882
- lines.push(`githubRepo: ${repoMentions.map((mention) => `${mention.value}:${mention.count}`).join(", ")}`);
883
- }
884
- for (const summary of details.categorical.slice(0, 3)) {
885
- lines.push(`${summary.path}: ${summary.counts.map(({ value, count }) => `${value}:${count}`).join(", ")}`);
886
- }
887
- for (const summary of details.numeric.filter((entry) => entry.score > 0).slice(0, 2)) {
888
- lines.push(`${summary.path}: min=${summary.min}, max=${summary.max}, avg=${summary.average}`);
889
- }
890
- if (details.samples.length > 0) {
891
- lines.push(`samples: ${details.samples.slice(0, 3).map((sample) => sample.label).join(" | ")}`);
892
- }
893
- return lines.join("\n");
968
+ const lines = ["json query summary"];
969
+ if (details.itemCount !== undefined) {
970
+ lines.push(`items: ${details.itemCount}`);
971
+ } else {
972
+ lines.push(`root: ${details.rootKind}`);
973
+ }
974
+ const repoMentions = details.mentions.filter((mention) => mention.kind === "githubRepo");
975
+ if (repoMentions.length > 0) {
976
+ lines.push(`githubRepo: ${repoMentions.map((mention) => `${mention.value}:${mention.count}`).join(", ")}`);
977
+ }
978
+ for (const summary of details.categorical.slice(0, 3)) {
979
+ lines.push(`${summary.path}: ${summary.counts.map(({ value, count }) => `${value}:${count}`).join(", ")}`);
980
+ }
981
+ for (const summary of details.numeric.filter((entry) => entry.score > 0).slice(0, 2)) {
982
+ lines.push(`${summary.path}: min=${summary.min}, max=${summary.max}, avg=${summary.average}`);
983
+ }
984
+ if (details.samples.length > 0) {
985
+ lines.push(
986
+ `samples: ${details.samples
987
+ .slice(0, 3)
988
+ .map((sample) => sample.label)
989
+ .join(" | ")}`,
990
+ );
991
+ }
992
+ return lines.join("\n");
894
993
  }
895
994
  function groupJsonEntriesByPath(entries) {
896
- const byPath = new Map();
897
- for (const entry of entries) {
898
- const pathEntries = byPath.get(entry.path) ?? [];
899
- pathEntries.push(entry);
900
- byPath.set(entry.path, pathEntries);
901
- }
902
- return byPath;
995
+ const byPath = new Map();
996
+ for (const entry of entries) {
997
+ const pathEntries = byPath.get(entry.path) ?? [];
998
+ pathEntries.push(entry);
999
+ byPath.set(entry.path, pathEntries);
1000
+ }
1001
+ return byPath;
903
1002
  }
904
1003
  function jsonPathScore(path, values, goalTokens) {
905
- const pathTokens = tokenizeGoal(path);
906
- const valueTokens = new Set(values.flatMap((value) => [...tokenizeGoal(value)].slice(0, 8)));
907
- const pathOverlap = overlapCount(pathTokens, goalTokens);
908
- const valueOverlap = overlapCount(valueTokens, goalTokens);
909
- return pathOverlap * 5 + valueOverlap * 3 + highSignalJsonPathScore(path, values);
1004
+ const pathTokens = tokenizeGoal(path);
1005
+ const valueTokens = new Set(values.flatMap((value) => [...tokenizeGoal(value)].slice(0, 8)));
1006
+ const pathOverlap = overlapCount(pathTokens, goalTokens);
1007
+ const valueOverlap = overlapCount(valueTokens, goalTokens);
1008
+ return pathOverlap * 5 + valueOverlap * 3 + highSignalJsonPathScore(path, values);
910
1009
  }
911
1010
  function highSignalJsonPathScore(path, values = []) {
912
- const normalized = path.toLowerCase();
913
- if (/labels?\[\]\.name$/.test(normalized)) {
914
- return 12;
915
- }
916
- if (/(^|\.)(status|state|type|category|kind|severity|priority|role|result)$/.test(normalized)) {
917
- return 8;
918
- }
919
- if (/(^|\.)(repo|repository|full_name|owner|name|title)$/.test(normalized)) {
920
- return 5;
921
- }
922
- if (values.some((value) => /^(status|type|category|priority):/i.test(value))) {
923
- return 6;
924
- }
925
- return 0;
1011
+ const normalized = path.toLowerCase();
1012
+ if (/labels?\[\]\.name$/.test(normalized)) {
1013
+ return 12;
1014
+ }
1015
+ if (/(^|\.)(status|state|type|category|kind|severity|priority|role|result)$/.test(normalized)) {
1016
+ return 8;
1017
+ }
1018
+ if (/(^|\.)(repo|repository|full_name|owner|name|title)$/.test(normalized)) {
1019
+ return 5;
1020
+ }
1021
+ if (values.some((value) => /^(status|type|category|priority):/i.test(value))) {
1022
+ return 6;
1023
+ }
1024
+ return 0;
926
1025
  }
927
1026
  function isHighSignalJsonPath(path) {
928
- return highSignalJsonPathScore(path) > 0;
1027
+ return highSignalJsonPathScore(path) > 0;
929
1028
  }
930
1029
  function jsonPathIsSampleField(path) {
931
- return /(^|\.)(title|name)$/.test(path.toLowerCase());
1030
+ return /(^|\.)(title|name)$/.test(path.toLowerCase());
932
1031
  }
933
1032
  function tokenizeGoal(text) {
934
- return new Set(text.toLowerCase().split(/[^a-z0-9]+/).filter((token) => token.length > 1 && !JSON_STOP_TOKENS.has(token)));
1033
+ return new Set(
1034
+ text
1035
+ .toLowerCase()
1036
+ .split(/[^a-z0-9]+/)
1037
+ .filter((token) => token.length > 1 && !JSON_STOP_TOKENS.has(token)),
1038
+ );
935
1039
  }
936
1040
  const JSON_STOP_TOKENS = new Set(["json", "the", "and", "for", "with", "from"]);
937
1041
  function overlapCount(left, right) {
938
- let count = 0;
939
- for (const token of left) {
940
- if (right.has(token)) {
941
- count += 1;
942
- }
1042
+ let count = 0;
1043
+ for (const token of left) {
1044
+ if (right.has(token)) {
1045
+ count += 1;
943
1046
  }
944
- return count;
1047
+ }
1048
+ return count;
945
1049
  }
946
1050
  function primitiveSampleValue(value) {
947
- if (typeof value === "string" && value.trim().length > 0) {
948
- return oneLine(value, 120);
949
- }
950
- if (typeof value === "number" || typeof value === "boolean") {
951
- return value;
952
- }
953
- return undefined;
1051
+ if (typeof value === "string" && value.trim().length > 0) {
1052
+ return oneLine(value, 120);
1053
+ }
1054
+ if (typeof value === "number" || typeof value === "boolean") {
1055
+ return value;
1056
+ }
1057
+ return undefined;
954
1058
  }
955
1059
  export function reduceTableOutput(text) {
956
- const parsed = parseTable(text);
957
- const confidence = tableConfidence(parsed);
958
- const candidate = {
959
- ...TABLE_REDUCER,
960
- confidence,
961
- reason: parsed === undefined
962
- ? "No CSV table shape detected."
963
- : `Detected ${parsed.format} table with ${parsed.rows.length} row(s) and ${parsed.columns.length} column(s).`,
964
- };
965
- if (parsed === undefined || confidence < 0.8) {
966
- return { candidate };
967
- }
968
- const details = summarizeTable(parsed);
969
- return {
970
- candidate,
971
- result: {
972
- ...TABLE_REDUCER,
973
- confidence,
974
- reason: candidate.reason,
975
- facts: tableFacts(details),
976
- visibleText: renderTableSummary(details),
977
- details,
978
- },
979
- };
1060
+ const parsed = parseTable(text);
1061
+ const confidence = tableConfidence(parsed);
1062
+ const candidate = {
1063
+ ...TABLE_REDUCER,
1064
+ confidence,
1065
+ reason:
1066
+ parsed === undefined
1067
+ ? "No CSV table shape detected."
1068
+ : `Detected ${parsed.format} table with ${parsed.rows.length} row(s) and ${parsed.columns.length} column(s).`,
1069
+ };
1070
+ if (parsed === undefined || confidence < 0.8) {
1071
+ return { candidate };
1072
+ }
1073
+ const details = summarizeTable(parsed);
1074
+ return {
1075
+ candidate,
1076
+ result: {
1077
+ ...TABLE_REDUCER,
1078
+ confidence,
1079
+ reason: candidate.reason,
1080
+ facts: tableFacts(details),
1081
+ visibleText: renderTableSummary(details),
1082
+ details,
1083
+ },
1084
+ };
980
1085
  }
981
1086
  function parseTable(text) {
982
- return parseCsvTable(text);
1087
+ return parseCsvTable(text);
983
1088
  }
984
1089
  function parseCsvTable(text) {
985
- const lines = text.trim().split(/\r?\n/).filter((line) => line.trim().length > 0);
986
- if (lines.length < 3 || !lines[0]?.includes(",")) {
987
- return undefined;
988
- }
989
- const columns = parseCsvLine(lines[0]).map((column) => column.trim());
990
- if (columns.length < 2 || !columns.every(isTableColumnName)) {
991
- return undefined;
992
- }
993
- const rows = [];
994
- for (const line of lines.slice(1)) {
995
- const rawValues = parseCsvLine(line);
996
- if (rawValues.length < columns.length) {
997
- return undefined;
998
- }
999
- const values = rawValues.length === columns.length
1000
- ? rawValues
1001
- : [...rawValues.slice(0, columns.length - 1), rawValues.slice(columns.length - 1).join(",")];
1002
- rows.push(Object.fromEntries(columns.map((column, index) => [column, values[index] ?? ""])));
1003
- }
1004
- return rows.length >= 2 ? { format: "csv", columns, rows } : undefined;
1090
+ const lines = text
1091
+ .trim()
1092
+ .split(/\r?\n/)
1093
+ .filter((line) => line.trim().length > 0);
1094
+ if (lines.length < 3 || !lines[0]?.includes(",")) {
1095
+ return undefined;
1096
+ }
1097
+ const columns = parseCsvLine(lines[0]).map((column) => column.trim());
1098
+ if (columns.length < 2 || !columns.every(isTableColumnName)) {
1099
+ return undefined;
1100
+ }
1101
+ const rows = [];
1102
+ for (const line of lines.slice(1)) {
1103
+ const rawValues = parseCsvLine(line);
1104
+ if (rawValues.length < columns.length) {
1105
+ return undefined;
1106
+ }
1107
+ const values =
1108
+ rawValues.length === columns.length
1109
+ ? rawValues
1110
+ : [...rawValues.slice(0, columns.length - 1), rawValues.slice(columns.length - 1).join(",")];
1111
+ rows.push(Object.fromEntries(columns.map((column, index) => [column, values[index] ?? ""])));
1112
+ }
1113
+ return rows.length >= 2 ? { format: "csv", columns, rows } : undefined;
1005
1114
  }
1006
1115
  function tableConfidence(parsed) {
1007
- if (parsed === undefined) {
1008
- return 0;
1009
- }
1010
- if (parsed.rows.length >= 10 && parsed.columns.length >= 3) {
1011
- return 1;
1012
- }
1013
- if (parsed.rows.length >= 3 && parsed.columns.length >= 2) {
1014
- return 0.9;
1015
- }
1016
- return 0.75;
1116
+ if (parsed === undefined) {
1117
+ return 0;
1118
+ }
1119
+ if (parsed.rows.length >= 10 && parsed.columns.length >= 3) {
1120
+ return 1;
1121
+ }
1122
+ if (parsed.rows.length >= 3 && parsed.columns.length >= 2) {
1123
+ return 0.9;
1124
+ }
1125
+ return 0.75;
1017
1126
  }
1018
1127
  function summarizeTable(parsed) {
1019
- return {
1020
- kind: "table",
1021
- format: parsed.format,
1022
- rowCount: parsed.rows.length,
1023
- columns: parsed.columns,
1024
- categorical: summarizeCategoricalColumns(parsed),
1025
- numeric: summarizeNumericColumns(parsed),
1026
- };
1128
+ return {
1129
+ kind: "table",
1130
+ format: parsed.format,
1131
+ rowCount: parsed.rows.length,
1132
+ columns: parsed.columns,
1133
+ categorical: summarizeCategoricalColumns(parsed),
1134
+ numeric: summarizeNumericColumns(parsed),
1135
+ };
1027
1136
  }
1028
1137
  function summarizeCategoricalColumns(table) {
1029
- const summaries = [];
1030
- for (const column of table.columns) {
1031
- const values = table.rows.map((row) => row[column] ?? "").filter((value) => value.length > 0);
1032
- if (values.length !== table.rows.length) {
1033
- continue;
1034
- }
1035
- const counts = countValues(values);
1036
- if (counts.length < 2 || counts.length > Math.min(20, Math.max(3, Math.floor(table.rows.length / 2)))) {
1037
- continue;
1038
- }
1039
- summaries.push({ column, counts: counts.slice(0, 8) });
1040
- }
1041
- return summaries.sort((left, right) => categoricalPriority(left.column) - categoricalPriority(right.column) || left.counts.length - right.counts.length || left.column.localeCompare(right.column));
1138
+ const summaries = [];
1139
+ for (const column of table.columns) {
1140
+ const values = table.rows.map((row) => row[column] ?? "").filter((value) => value.length > 0);
1141
+ if (values.length !== table.rows.length) {
1142
+ continue;
1143
+ }
1144
+ const counts = countValues(values);
1145
+ if (counts.length < 2 || counts.length > Math.min(20, Math.max(3, Math.floor(table.rows.length / 2)))) {
1146
+ continue;
1147
+ }
1148
+ summaries.push({ column, counts: counts.slice(0, 8) });
1149
+ }
1150
+ return summaries.sort(
1151
+ (left, right) =>
1152
+ categoricalPriority(left.column) - categoricalPriority(right.column) ||
1153
+ left.counts.length - right.counts.length ||
1154
+ left.column.localeCompare(right.column),
1155
+ );
1042
1156
  }
1043
1157
  function summarizeNumericColumns(table) {
1044
- const summaries = [];
1045
- for (const column of table.columns) {
1046
- const numbers = table.rows.map((row) => Number(row[column])).filter((value) => Number.isFinite(value));
1047
- if (numbers.length !== table.rows.length || numbers.length === 0) {
1048
- continue;
1049
- }
1050
- const total = numbers.reduce((sum, value) => sum + value, 0);
1051
- summaries.push({ column, min: Math.min(...numbers), max: Math.max(...numbers), average: Math.round((total / numbers.length) * 10) / 10 });
1052
- }
1053
- return summaries.sort((left, right) => numericPriority(left.column) - numericPriority(right.column) || right.max - left.max || left.column.localeCompare(right.column)).slice(0, 5);
1158
+ const summaries = [];
1159
+ for (const column of table.columns) {
1160
+ const numbers = table.rows.map((row) => Number(row[column])).filter((value) => Number.isFinite(value));
1161
+ if (numbers.length !== table.rows.length || numbers.length === 0) {
1162
+ continue;
1163
+ }
1164
+ const total = numbers.reduce((sum, value) => sum + value, 0);
1165
+ summaries.push({
1166
+ column,
1167
+ min: Math.min(...numbers),
1168
+ max: Math.max(...numbers),
1169
+ average: Math.round((total / numbers.length) * 10) / 10,
1170
+ });
1171
+ }
1172
+ return summaries
1173
+ .sort(
1174
+ (left, right) =>
1175
+ numericPriority(left.column) - numericPriority(right.column) ||
1176
+ right.max - left.max ||
1177
+ left.column.localeCompare(right.column),
1178
+ )
1179
+ .slice(0, 5);
1054
1180
  }
1055
1181
  function tableFacts(details) {
1056
- const facts = [{ name: "rows", value: details.rowCount }];
1057
- const categorical = details.categorical[0];
1058
- if (categorical) {
1059
- facts.push({ name: categorical.column, value: categorical.counts.map(({ value, count }) => `${value}:${count}`).join(", ") });
1060
- }
1061
- const numeric = details.numeric[0];
1062
- if (numeric) {
1063
- facts.push({ name: `${numeric.column}.max`, value: numeric.max });
1064
- }
1065
- return facts;
1182
+ const facts = [{ name: "rows", value: details.rowCount }];
1183
+ const categorical = details.categorical[0];
1184
+ if (categorical) {
1185
+ facts.push({
1186
+ name: categorical.column,
1187
+ value: categorical.counts.map(({ value, count }) => `${value}:${count}`).join(", "),
1188
+ });
1189
+ }
1190
+ const numeric = details.numeric[0];
1191
+ if (numeric) {
1192
+ facts.push({ name: `${numeric.column}.max`, value: numeric.max });
1193
+ }
1194
+ return facts;
1066
1195
  }
1067
1196
  function renderTableSummary(details) {
1068
- const lines = ["table summary", `rows: ${details.rowCount}`];
1069
- const categorical = details.categorical[0];
1070
- if (categorical) {
1071
- lines.push(`${categorical.column}: ${categorical.counts.map(({ value, count }) => `${value}:${count}`).join(", ")}`);
1072
- }
1073
- const numeric = details.numeric[0];
1074
- if (numeric) {
1075
- lines.push(`${numeric.column}.max: ${numeric.max}`);
1076
- }
1077
- return lines.join("\n");
1197
+ const lines = ["table summary", `rows: ${details.rowCount}`];
1198
+ const categorical = details.categorical[0];
1199
+ if (categorical) {
1200
+ lines.push(
1201
+ `${categorical.column}: ${categorical.counts.map(({ value, count }) => `${value}:${count}`).join(", ")}`,
1202
+ );
1203
+ }
1204
+ const numeric = details.numeric[0];
1205
+ if (numeric) {
1206
+ lines.push(`${numeric.column}.max: ${numeric.max}`);
1207
+ }
1208
+ return lines.join("\n");
1078
1209
  }
1079
1210
  function parseCsvLine(line) {
1080
- const values = [];
1081
- let current = "";
1082
- let quoted = false;
1083
- for (let index = 0; index < line.length; index += 1) {
1084
- const char = line[index];
1085
- if (char === '"') {
1086
- if (quoted && line[index + 1] === '"') {
1087
- current += '"';
1088
- index += 1;
1089
- }
1090
- else {
1091
- quoted = !quoted;
1092
- }
1093
- continue;
1094
- }
1095
- if (char === "," && !quoted) {
1096
- values.push(current);
1097
- current = "";
1098
- continue;
1099
- }
1100
- current += char;
1101
- }
1102
- values.push(current);
1103
- return values;
1211
+ const values = [];
1212
+ let current = "";
1213
+ let quoted = false;
1214
+ for (let index = 0; index < line.length; index += 1) {
1215
+ const char = line[index];
1216
+ if (char === '"') {
1217
+ if (quoted && line[index + 1] === '"') {
1218
+ current += '"';
1219
+ index += 1;
1220
+ } else {
1221
+ quoted = !quoted;
1222
+ }
1223
+ continue;
1224
+ }
1225
+ if (char === "," && !quoted) {
1226
+ values.push(current);
1227
+ current = "";
1228
+ continue;
1229
+ }
1230
+ current += char;
1231
+ }
1232
+ values.push(current);
1233
+ return values;
1104
1234
  }
1105
1235
  function countValues(values) {
1106
- const counts = new Map();
1107
- for (const value of values) {
1108
- counts.set(value, (counts.get(value) ?? 0) + 1);
1109
- }
1110
- return [...counts.entries()]
1111
- .sort(([leftValue, leftCount], [rightValue, rightCount]) => rightCount - leftCount || leftValue.localeCompare(rightValue))
1112
- .map(([value, count]) => ({ value, count }));
1236
+ const counts = new Map();
1237
+ for (const value of values) {
1238
+ counts.set(value, (counts.get(value) ?? 0) + 1);
1239
+ }
1240
+ return [...counts.entries()]
1241
+ .sort(
1242
+ ([leftValue, leftCount], [rightValue, rightCount]) =>
1243
+ rightCount - leftCount || leftValue.localeCompare(rightValue),
1244
+ )
1245
+ .map(([value, count]) => ({ value, count }));
1113
1246
  }
1114
1247
  function categoricalPriority(column) {
1115
- const normalized = column.toLowerCase();
1116
- const priorities = ["status", "state", "result", "outcome", "role", "type", "category", "action", "resource"];
1117
- const index = priorities.indexOf(normalized);
1118
- return index === -1 ? priorities.length : index;
1248
+ const normalized = column.toLowerCase();
1249
+ const priorities = ["status", "state", "result", "outcome", "role", "type", "category", "action", "resource"];
1250
+ const index = priorities.indexOf(normalized);
1251
+ return index === -1 ? priorities.length : index;
1119
1252
  }
1120
1253
  function numericPriority(column) {
1121
- const normalized = column.toLowerCase();
1122
- if (normalized.includes("duration") || normalized.endsWith("_ms") || normalized.endsWith("ms")) {
1123
- return 0;
1124
- }
1125
- if (normalized.includes("latency") || normalized.includes("elapsed")) {
1126
- return 1;
1127
- }
1128
- return 2;
1254
+ const normalized = column.toLowerCase();
1255
+ if (normalized.includes("duration") || normalized.endsWith("_ms") || normalized.endsWith("ms")) {
1256
+ return 0;
1257
+ }
1258
+ if (normalized.includes("latency") || normalized.includes("elapsed")) {
1259
+ return 1;
1260
+ }
1261
+ return 2;
1129
1262
  }
1130
1263
  function isTableColumnName(value) {
1131
- return /^[A-Za-z_][A-Za-z0-9_.-]*$/.test(value.trim());
1264
+ return /^[A-Za-z_][A-Za-z0-9_.-]*$/.test(value.trim());
1132
1265
  }
1133
1266
  function isJsonObject(value) {
1134
- return typeof value === "object" && value !== null && !Array.isArray(value);
1267
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1135
1268
  }
1136
1269
  export function reduceBrowserSnapshotOutput(text) {
1137
- const parsed = parseBrowserSnapshot(text);
1138
- const confidence = browserSnapshotConfidence(parsed);
1139
- const candidate = {
1140
- ...BROWSER_SNAPSHOT_REDUCER,
1141
- confidence,
1142
- reason: parsed === undefined
1143
- ? "No Playwright/accessibility snapshot shape detected."
1144
- : `Detected browser snapshot with ${parsed.refCount} ref(s), ${parsed.namedLinks.length} named link(s), and ${parsed.lineCount} line(s).`,
1145
- };
1146
- if (parsed === undefined || confidence < 0.8) {
1147
- return { candidate };
1148
- }
1149
- const details = summarizeBrowserSnapshot(parsed);
1150
- return {
1151
- candidate,
1152
- result: {
1153
- ...BROWSER_SNAPSHOT_REDUCER,
1154
- confidence,
1155
- reason: candidate.reason,
1156
- facts: browserSnapshotFacts(details),
1157
- visibleText: renderBrowserSnapshotSummary(details),
1158
- details,
1159
- },
1160
- };
1270
+ const parsed = parseBrowserSnapshot(text);
1271
+ const confidence = browserSnapshotConfidence(parsed);
1272
+ const candidate = {
1273
+ ...BROWSER_SNAPSHOT_REDUCER,
1274
+ confidence,
1275
+ reason:
1276
+ parsed === undefined
1277
+ ? "No Playwright/accessibility snapshot shape detected."
1278
+ : `Detected browser snapshot with ${parsed.refCount} ref(s), ${parsed.namedLinks.length} named link(s), and ${parsed.lineCount} line(s).`,
1279
+ };
1280
+ if (parsed === undefined || confidence < 0.8) {
1281
+ return { candidate };
1282
+ }
1283
+ const details = summarizeBrowserSnapshot(parsed);
1284
+ return {
1285
+ candidate,
1286
+ result: {
1287
+ ...BROWSER_SNAPSHOT_REDUCER,
1288
+ confidence,
1289
+ reason: candidate.reason,
1290
+ facts: browserSnapshotFacts(details),
1291
+ visibleText: renderBrowserSnapshotSummary(details),
1292
+ details,
1293
+ },
1294
+ };
1161
1295
  }
1162
1296
  function parseBrowserSnapshot(text) {
1163
- const lines = text.split(/\r?\n/);
1164
- const snapshotStartIndex = lines.findIndex((line) => /^###\s+Snapshot\s*$/i.test(line.trim()) || /^```ya?ml\s*$/i.test(line.trim()));
1165
- const hasSnapshotMarker = snapshotStartIndex !== -1;
1166
- const pageTitle = firstMatch(text, /^- Page Title:\s*(.+)$/m);
1167
- const pageUrl = firstMatch(text, /^- Page URL:\s*(.+)$/m);
1168
- const refCount = [...text.matchAll(/\[ref=[^\]\s]+\]/g)].length;
1169
- const roleCounts = new Map();
1170
- const namedLinks = [];
1171
- const textNodes = [];
1172
- for (const line of hasSnapshotMarker ? lines.slice(snapshotStartIndex + 1) : lines) {
1173
- const role = parseSnapshotRole(line);
1174
- if (role !== undefined) {
1175
- roleCounts.set(role, (roleCounts.get(role) ?? 0) + 1);
1176
- }
1177
- const link = parseNamedSnapshotNode(line, "link");
1178
- if (link !== undefined) {
1179
- namedLinks.push(link);
1180
- }
1181
- const textNode = parseSnapshotTextNode(line);
1182
- if (textNode !== undefined) {
1183
- textNodes.push(textNode);
1184
- }
1185
- }
1186
- if (!hasSnapshotMarker || refCount < 3 || roleCounts.size === 0 || namedLinks.length === 0) {
1187
- return undefined;
1188
- }
1189
- return {
1190
- lineCount: lines.length,
1191
- ...(pageTitle !== undefined ? { pageTitle } : {}),
1192
- ...(pageUrl !== undefined ? { pageUrl } : {}),
1193
- refCount,
1194
- roleCounts,
1195
- namedLinks,
1196
- textNodes,
1197
- };
1297
+ const lines = text.split(/\r?\n/);
1298
+ const snapshotStartIndex = lines.findIndex(
1299
+ (line) => /^###\s+Snapshot\s*$/i.test(line.trim()) || /^```ya?ml\s*$/i.test(line.trim()),
1300
+ );
1301
+ const hasSnapshotMarker = snapshotStartIndex !== -1;
1302
+ const pageTitle = firstMatch(text, /^- Page Title:\s*(.+)$/m);
1303
+ const pageUrl = firstMatch(text, /^- Page URL:\s*(.+)$/m);
1304
+ const refCount = [...text.matchAll(/\[ref=[^\]\s]+\]/g)].length;
1305
+ const roleCounts = new Map();
1306
+ const namedLinks = [];
1307
+ const textNodes = [];
1308
+ for (const line of hasSnapshotMarker ? lines.slice(snapshotStartIndex + 1) : lines) {
1309
+ const role = parseSnapshotRole(line);
1310
+ if (role !== undefined) {
1311
+ roleCounts.set(role, (roleCounts.get(role) ?? 0) + 1);
1312
+ }
1313
+ const link = parseNamedSnapshotNode(line, "link");
1314
+ if (link !== undefined) {
1315
+ namedLinks.push(link);
1316
+ }
1317
+ const textNode = parseSnapshotTextNode(line);
1318
+ if (textNode !== undefined) {
1319
+ textNodes.push(textNode);
1320
+ }
1321
+ }
1322
+ if (!hasSnapshotMarker || refCount < 3 || roleCounts.size === 0 || namedLinks.length === 0) {
1323
+ return undefined;
1324
+ }
1325
+ return {
1326
+ lineCount: lines.length,
1327
+ ...(pageTitle !== undefined ? { pageTitle } : {}),
1328
+ ...(pageUrl !== undefined ? { pageUrl } : {}),
1329
+ refCount,
1330
+ roleCounts,
1331
+ namedLinks,
1332
+ textNodes,
1333
+ };
1198
1334
  }
1199
1335
  function browserSnapshotConfidence(parsed) {
1200
- if (parsed === undefined) {
1201
- return 0;
1202
- }
1203
- if (parsed.refCount >= 20 && parsed.namedLinks.length >= 10 && parsed.pageTitle !== undefined) {
1204
- return 1;
1205
- }
1206
- if (parsed.refCount >= 3 && parsed.namedLinks.length >= 1) {
1207
- return 0.9;
1208
- }
1209
- return 0.6;
1336
+ if (parsed === undefined) {
1337
+ return 0;
1338
+ }
1339
+ if (parsed.refCount >= 20 && parsed.namedLinks.length >= 10 && parsed.pageTitle !== undefined) {
1340
+ return 1;
1341
+ }
1342
+ if (parsed.refCount >= 3 && parsed.namedLinks.length >= 1) {
1343
+ return 0.9;
1344
+ }
1345
+ return 0.6;
1210
1346
  }
1211
1347
  function summarizeBrowserSnapshot(parsed) {
1212
- const storyLikeLinks = parsed.namedLinks.filter((link) => isStoryLikeLink(link.name));
1213
- return {
1214
- kind: "browser-snapshot",
1215
- lineCount: parsed.lineCount,
1216
- ...(parsed.pageTitle !== undefined ? { pageTitle: parsed.pageTitle } : {}),
1217
- ...(parsed.pageUrl !== undefined ? { pageUrl: parsed.pageUrl } : {}),
1218
- refCount: parsed.refCount,
1219
- namedLinkCount: parsed.namedLinks.length,
1220
- textNodeCount: parsed.roleCounts.get("text") ?? parsed.textNodes.length,
1221
- storyLikeLinkCount: Math.min(storyLikeLinks.length, 30),
1222
- roleCounts: [...parsed.roleCounts.entries()]
1223
- .sort(([leftRole, leftCount], [rightRole, rightCount]) => rightCount - leftCount || leftRole.localeCompare(rightRole))
1224
- .map(([role, count]) => ({ role, count })),
1225
- topInteractiveNodes: parsed.namedLinks.slice(0, 8),
1226
- topTextNodes: parsed.textNodes.filter((node) => node.length > 0).slice(0, 8),
1227
- };
1348
+ const storyLikeLinks = parsed.namedLinks.filter((link) => isStoryLikeLink(link.name));
1349
+ return {
1350
+ kind: "browser-snapshot",
1351
+ lineCount: parsed.lineCount,
1352
+ ...(parsed.pageTitle !== undefined ? { pageTitle: parsed.pageTitle } : {}),
1353
+ ...(parsed.pageUrl !== undefined ? { pageUrl: parsed.pageUrl } : {}),
1354
+ refCount: parsed.refCount,
1355
+ namedLinkCount: parsed.namedLinks.length,
1356
+ textNodeCount: parsed.roleCounts.get("text") ?? parsed.textNodes.length,
1357
+ storyLikeLinkCount: Math.min(storyLikeLinks.length, 30),
1358
+ roleCounts: [...parsed.roleCounts.entries()]
1359
+ .sort(
1360
+ ([leftRole, leftCount], [rightRole, rightCount]) => rightCount - leftCount || leftRole.localeCompare(rightRole),
1361
+ )
1362
+ .map(([role, count]) => ({ role, count })),
1363
+ topInteractiveNodes: parsed.namedLinks.slice(0, 8),
1364
+ topTextNodes: parsed.textNodes.filter((node) => node.length > 0).slice(0, 8),
1365
+ };
1228
1366
  }
1229
1367
  function browserSnapshotFacts(details) {
1230
- const facts = [
1231
- { name: "lines", value: details.lineCount },
1232
- { name: "links", value: details.namedLinkCount },
1233
- { name: "refs", value: details.refCount },
1234
- { name: "storyLikeLinks", value: `${details.storyLikeLinkCount} (benchmark alias: Stories)` },
1235
- ];
1236
- if (details.pageTitle !== undefined) {
1237
- facts.push({ name: "title", value: details.pageTitle });
1238
- }
1239
- facts.push({ name: "roles", value: details.roleCounts.slice(0, 8).map(({ role, count }) => `${role}:${count}`).join(", ") });
1240
- facts.push({ name: "topLinks", value: details.topInteractiveNodes.slice(0, 5).map((node) => node.name).join(" | ") });
1241
- return facts;
1368
+ const facts = [
1369
+ { name: "lines", value: details.lineCount },
1370
+ { name: "links", value: details.namedLinkCount },
1371
+ { name: "refs", value: details.refCount },
1372
+ { name: "storyLikeLinks", value: `${details.storyLikeLinkCount} (benchmark alias: Stories)` },
1373
+ ];
1374
+ if (details.pageTitle !== undefined) {
1375
+ facts.push({ name: "title", value: details.pageTitle });
1376
+ }
1377
+ facts.push({
1378
+ name: "roles",
1379
+ value: details.roleCounts
1380
+ .slice(0, 8)
1381
+ .map(({ role, count }) => `${role}:${count}`)
1382
+ .join(", "),
1383
+ });
1384
+ facts.push({
1385
+ name: "topLinks",
1386
+ value: details.topInteractiveNodes
1387
+ .slice(0, 5)
1388
+ .map((node) => node.name)
1389
+ .join(" | "),
1390
+ });
1391
+ return facts;
1242
1392
  }
1243
1393
  function renderBrowserSnapshotSummary(details) {
1244
- const lines = [
1245
- "browser snapshot summary",
1246
- `lines: ${details.lineCount}`,
1247
- `links: ${details.namedLinkCount}`,
1248
- `refs: ${details.refCount}`,
1249
- `storyLikeLinks: ${details.storyLikeLinkCount} (benchmark alias: Stories)`,
1250
- ];
1251
- if (details.pageTitle !== undefined) {
1252
- lines.push(`title: ${details.pageTitle}`);
1253
- }
1254
- lines.push(`roles: ${details.roleCounts.slice(0, 8).map(({ role, count }) => `${role}:${count}`).join(", ")}`);
1255
- lines.push(`topLinks: ${details.topInteractiveNodes.slice(0, 5).map((node) => node.name).join(" | ")}`);
1256
- if (details.topTextNodes.length > 0) {
1257
- lines.push(`topText: ${details.topTextNodes.slice(0, 5).join(" | ")}`);
1258
- }
1259
- return lines.join("\n");
1394
+ const lines = [
1395
+ "browser snapshot summary",
1396
+ `lines: ${details.lineCount}`,
1397
+ `links: ${details.namedLinkCount}`,
1398
+ `refs: ${details.refCount}`,
1399
+ `storyLikeLinks: ${details.storyLikeLinkCount} (benchmark alias: Stories)`,
1400
+ ];
1401
+ if (details.pageTitle !== undefined) {
1402
+ lines.push(`title: ${details.pageTitle}`);
1403
+ }
1404
+ lines.push(
1405
+ `roles: ${details.roleCounts
1406
+ .slice(0, 8)
1407
+ .map(({ role, count }) => `${role}:${count}`)
1408
+ .join(", ")}`,
1409
+ );
1410
+ lines.push(
1411
+ `topLinks: ${details.topInteractiveNodes
1412
+ .slice(0, 5)
1413
+ .map((node) => node.name)
1414
+ .join(" | ")}`,
1415
+ );
1416
+ if (details.topTextNodes.length > 0) {
1417
+ lines.push(`topText: ${details.topTextNodes.slice(0, 5).join(" | ")}`);
1418
+ }
1419
+ return lines.join("\n");
1260
1420
  }
1261
1421
  function parseSnapshotRole(line) {
1262
- const match = /^\s*-\s+'?([A-Za-z][A-Za-z0-9_-]*)\b/.exec(line);
1263
- return match?.[1];
1422
+ const match = /^\s*-\s+'?([A-Za-z][A-Za-z0-9_-]*)\b/.exec(line);
1423
+ return match?.[1];
1264
1424
  }
1265
1425
  function parseNamedSnapshotNode(line, role) {
1266
- const match = new RegExp(`^\\s*-\\s+${role}\\s+"([^"]+)"`).exec(line);
1267
- if (!match?.[1]) {
1268
- return undefined;
1269
- }
1270
- const ref = /\[ref=([^\]\s]+)\]/.exec(line)?.[1];
1271
- return {
1272
- role,
1273
- name: oneLine(match[1], 160),
1274
- ...(ref !== undefined ? { ref } : {}),
1275
- };
1426
+ const match = new RegExp(`^\\s*-\\s+${role}\\s+"([^"]+)"`).exec(line);
1427
+ if (!match?.[1]) {
1428
+ return undefined;
1429
+ }
1430
+ const ref = /\[ref=([^\]\s]+)\]/.exec(line)?.[1];
1431
+ return {
1432
+ role,
1433
+ name: oneLine(match[1], 160),
1434
+ ...(ref !== undefined ? { ref } : {}),
1435
+ };
1276
1436
  }
1277
1437
  function parseSnapshotTextNode(line) {
1278
- const match = /^\s*-\s+text:\s*(.+?)\s*$/.exec(line);
1279
- if (!match?.[1]) {
1280
- return undefined;
1281
- }
1282
- return oneLine(match[1].replace(/^['"]|['"]$/g, ""), 160);
1438
+ const match = /^\s*-\s+text:\s*(.+?)\s*$/.exec(line);
1439
+ if (!match?.[1]) {
1440
+ return undefined;
1441
+ }
1442
+ return oneLine(match[1].replace(/^['"]|['"]$/g, ""), 160);
1283
1443
  }
1284
1444
  function firstMatch(text, pattern) {
1285
- return pattern.exec(text)?.[1]?.trim();
1445
+ return pattern.exec(text)?.[1]?.trim();
1286
1446
  }
1287
1447
  function isStoryLikeLink(value) {
1288
- return value.length > 10 && !/^https?:\/\//i.test(value);
1448
+ return value.length > 10 && !/^https?:\/\//i.test(value);
1289
1449
  }
1290
1450
  export function reduceGitLogOutput(text) {
1291
- const lines = text.split(/\r?\n/).filter((line) => line.trim().length > 0);
1292
- const commits = lines.map(parseGitLogLine).filter((commit) => commit !== undefined);
1293
- const parseRatio = lines.length === 0 ? 0 : commits.length / lines.length;
1294
- const confidence = gitLogConfidence(commits.length, parseRatio);
1295
- const candidate = {
1296
- ...GIT_LOG_REDUCER,
1297
- confidence,
1298
- reason: `Parsed ${commits.length}/${lines.length} non-empty line(s) as conventional git log entries.`,
1299
- };
1300
- if (commits.length < 2 || confidence < 0.8) {
1301
- return { candidate };
1302
- }
1303
- const details = summarizeGitLog(commits);
1304
- return {
1305
- candidate,
1306
- result: {
1307
- ...GIT_LOG_REDUCER,
1308
- confidence,
1309
- reason: candidate.reason,
1310
- facts: gitLogFacts(details),
1311
- visibleText: renderGitLogSummary(details),
1312
- details,
1313
- },
1314
- };
1451
+ const lines = text.split(/\r?\n/).filter((line) => line.trim().length > 0);
1452
+ const commits = lines.map(parseGitLogLine).filter((commit) => commit !== undefined);
1453
+ const parseRatio = lines.length === 0 ? 0 : commits.length / lines.length;
1454
+ const confidence = gitLogConfidence(commits.length, parseRatio);
1455
+ const candidate = {
1456
+ ...GIT_LOG_REDUCER,
1457
+ confidence,
1458
+ reason: `Parsed ${commits.length}/${lines.length} non-empty line(s) as conventional git log entries.`,
1459
+ };
1460
+ if (commits.length < 2 || confidence < 0.8) {
1461
+ return { candidate };
1462
+ }
1463
+ const details = summarizeGitLog(commits);
1464
+ return {
1465
+ candidate,
1466
+ result: {
1467
+ ...GIT_LOG_REDUCER,
1468
+ confidence,
1469
+ reason: candidate.reason,
1470
+ facts: gitLogFacts(details),
1471
+ visibleText: renderGitLogSummary(details),
1472
+ details,
1473
+ },
1474
+ };
1315
1475
  }
1316
1476
  function parseGitLogLine(line) {
1317
- const match = /^([0-9a-f]{7,40})(?:\s+(\d{4}-\d{2}-\d{2}))?\s+(.+?)\s+([A-Za-z][A-Za-z0-9-]*)(?:\(([^)]+)\))?!?:\s+(.+)$/.exec(line.trim());
1318
- if (!match?.[1] || !match[4] || !match[6]) {
1319
- return undefined;
1320
- }
1321
- const author = match[3]?.trim();
1322
- return {
1323
- hash: match[1],
1324
- ...(match[2] !== undefined ? { date: match[2] } : {}),
1325
- ...(author !== undefined && author.length > 0 ? { author } : {}),
1326
- type: match[4].toLowerCase(),
1327
- ...(match[5] !== undefined ? { scope: match[5].trim() } : {}),
1328
- subject: oneLine(match[6], 180),
1329
- };
1477
+ const match =
1478
+ /^([0-9a-f]{7,40})(?:\s+(\d{4}-\d{2}-\d{2}))?\s+(.+?)\s+([A-Za-z][A-Za-z0-9-]*)(?:\(([^)]+)\))?!?:\s+(.+)$/.exec(
1479
+ line.trim(),
1480
+ );
1481
+ if (!match?.[1] || !match[4] || !match[6]) {
1482
+ return undefined;
1483
+ }
1484
+ const author = match[3]?.trim();
1485
+ return {
1486
+ hash: match[1],
1487
+ ...(match[2] !== undefined ? { date: match[2] } : {}),
1488
+ ...(author !== undefined && author.length > 0 ? { author } : {}),
1489
+ type: match[4].toLowerCase(),
1490
+ ...(match[5] !== undefined ? { scope: match[5].trim() } : {}),
1491
+ subject: oneLine(match[6], 180),
1492
+ };
1330
1493
  }
1331
1494
  function gitLogConfidence(parsedLines, parseRatio) {
1332
- if (parsedLines >= 10 && parseRatio >= 0.8) {
1333
- return 1;
1334
- }
1335
- if (parsedLines >= 2 && parseRatio >= 0.8) {
1336
- return 0.9;
1337
- }
1338
- if (parsedLines > 0 && parseRatio >= 0.5) {
1339
- return 0.6;
1340
- }
1341
- return 0;
1495
+ if (parsedLines >= 10 && parseRatio >= 0.8) {
1496
+ return 1;
1497
+ }
1498
+ if (parsedLines >= 2 && parseRatio >= 0.8) {
1499
+ return 0.9;
1500
+ }
1501
+ if (parsedLines > 0 && parseRatio >= 0.5) {
1502
+ return 0.6;
1503
+ }
1504
+ return 0;
1342
1505
  }
1343
1506
  function summarizeGitLog(commits) {
1344
- return {
1345
- kind: "git-log",
1346
- commitCount: commits.length,
1347
- typeCounts: countValues(commits.map((commit) => commit.type)).map(({ value, count }) => ({ type: value, count })),
1348
- scopeCounts: countValues(commits.map((commit) => commit.scope ?? "unscoped")).map(({ value, count }) => ({ scope: value, count })).slice(0, 8),
1349
- authorCounts: countValues(commits.map((commit) => commit.author ?? "unknown")).map(({ value, count }) => ({ author: value, count })).slice(0, 8),
1350
- recentCommits: commits.slice(0, 8),
1351
- };
1507
+ return {
1508
+ kind: "git-log",
1509
+ commitCount: commits.length,
1510
+ typeCounts: countValues(commits.map((commit) => commit.type)).map(({ value, count }) => ({ type: value, count })),
1511
+ scopeCounts: countValues(commits.map((commit) => commit.scope ?? "unscoped"))
1512
+ .map(({ value, count }) => ({ scope: value, count }))
1513
+ .slice(0, 8),
1514
+ authorCounts: countValues(commits.map((commit) => commit.author ?? "unknown"))
1515
+ .map(({ value, count }) => ({ author: value, count }))
1516
+ .slice(0, 8),
1517
+ recentCommits: commits.slice(0, 8),
1518
+ };
1352
1519
  }
1353
1520
  function gitLogFacts(details) {
1354
- return [
1355
- { name: "commits", value: details.commitCount },
1356
- { name: "types", value: details.typeCounts.map(({ type, count }) => `${type}:${count}`).join(", ") },
1357
- { name: "authors", value: details.authorCounts.slice(0, 5).map(({ author, count }) => `${author}:${count}`).join(", ") },
1358
- { name: "recent", value: details.recentCommits.slice(0, 5).map(renderGitCommit).join(" | ") },
1359
- ];
1521
+ return [
1522
+ { name: "commits", value: details.commitCount },
1523
+ { name: "types", value: details.typeCounts.map(({ type, count }) => `${type}:${count}`).join(", ") },
1524
+ {
1525
+ name: "authors",
1526
+ value: details.authorCounts
1527
+ .slice(0, 5)
1528
+ .map(({ author, count }) => `${author}:${count}`)
1529
+ .join(", "),
1530
+ },
1531
+ { name: "recent", value: details.recentCommits.slice(0, 5).map(renderGitCommit).join(" | ") },
1532
+ ];
1360
1533
  }
1361
1534
  function renderGitLogSummary(details) {
1362
- return [
1363
- "git log summary",
1364
- `commits: ${details.commitCount}`,
1365
- `types: ${details.typeCounts.map(({ type, count }) => `${type}:${count}`).join(", ")}`,
1366
- `authors: ${details.authorCounts.slice(0, 5).map(({ author, count }) => `${author}:${count}`).join(", ")}`,
1367
- `recent: ${details.recentCommits.slice(0, 5).map(renderGitCommit).join(" | ")}`,
1368
- ].join("\n");
1535
+ return [
1536
+ "git log summary",
1537
+ `commits: ${details.commitCount}`,
1538
+ `types: ${details.typeCounts.map(({ type, count }) => `${type}:${count}`).join(", ")}`,
1539
+ `authors: ${details.authorCounts
1540
+ .slice(0, 5)
1541
+ .map(({ author, count }) => `${author}:${count}`)
1542
+ .join(", ")}`,
1543
+ `recent: ${details.recentCommits.slice(0, 5).map(renderGitCommit).join(" | ")}`,
1544
+ ].join("\n");
1369
1545
  }
1370
1546
  function renderGitCommit(commit) {
1371
- const scope = commit.scope !== undefined ? `(${commit.scope})` : "";
1372
- return `${commit.hash} ${commit.type}${scope}: ${commit.subject}`;
1547
+ const scope = commit.scope !== undefined ? `(${commit.scope})` : "";
1548
+ return `${commit.hash} ${commit.type}${scope}: ${commit.subject}`;
1373
1549
  }
1374
1550
  export function reduceAccessLog(text) {
1375
- const lines = text.split(/\r?\n/).filter((line) => line.trim().length > 0);
1376
- const entries = [];
1377
- for (const line of lines) {
1378
- const parsed = parseAccessLogLine(line);
1379
- if (parsed) {
1380
- entries.push(parsed);
1381
- }
1382
- }
1383
- const parseRatio = lines.length === 0 ? 0 : entries.length / lines.length;
1384
- const confidence = confidenceForAccessLog(lines.length, parseRatio);
1385
- const candidate = {
1386
- ...ACCESS_LOG_REDUCER,
1387
- confidence,
1388
- reason: `Parsed ${entries.length}/${lines.length} non-empty line(s) as access log entries.`,
1389
- };
1390
- if (entries.length < MIN_ACCESS_LOG_LINES || confidence < MIN_ACCESS_LOG_CONFIDENCE) {
1391
- return { candidate };
1392
- }
1393
- const details = summarizeAccessLog(entries, lines.length - entries.length);
1394
- return {
1395
- candidate,
1396
- result: {
1397
- ...ACCESS_LOG_REDUCER,
1398
- confidence,
1399
- reason: candidate.reason,
1400
- facts: accessLogFacts(details),
1401
- visibleText: renderAccessLogSummary(details),
1402
- details,
1403
- },
1404
- };
1551
+ const lines = text.split(/\r?\n/).filter((line) => line.trim().length > 0);
1552
+ const entries = [];
1553
+ for (const line of lines) {
1554
+ const parsed = parseAccessLogLine(line);
1555
+ if (parsed) {
1556
+ entries.push(parsed);
1557
+ }
1558
+ }
1559
+ const parseRatio = lines.length === 0 ? 0 : entries.length / lines.length;
1560
+ const confidence = confidenceForAccessLog(lines.length, parseRatio);
1561
+ const candidate = {
1562
+ ...ACCESS_LOG_REDUCER,
1563
+ confidence,
1564
+ reason: `Parsed ${entries.length}/${lines.length} non-empty line(s) as access log entries.`,
1565
+ };
1566
+ if (entries.length < MIN_ACCESS_LOG_LINES || confidence < MIN_ACCESS_LOG_CONFIDENCE) {
1567
+ return { candidate };
1568
+ }
1569
+ const details = summarizeAccessLog(entries, lines.length - entries.length);
1570
+ return {
1571
+ candidate,
1572
+ result: {
1573
+ ...ACCESS_LOG_REDUCER,
1574
+ confidence,
1575
+ reason: candidate.reason,
1576
+ facts: accessLogFacts(details),
1577
+ visibleText: renderAccessLogSummary(details),
1578
+ details,
1579
+ },
1580
+ };
1405
1581
  }
1406
1582
  function parseAccessLogLine(line) {
1407
- const match = ACCESS_LOG_LINE.exec(line);
1408
- if (!match) {
1409
- return undefined;
1410
- }
1411
- const method = match[2];
1412
- const path = match[3];
1413
- const status = Number(match[4]);
1414
- const latencyMs = Number(match[5]);
1415
- if (!method || !path || !Number.isInteger(status) || !Number.isInteger(latencyMs)) {
1416
- return undefined;
1417
- }
1418
- return { method, path, status, latencyMs };
1583
+ const match = ACCESS_LOG_LINE.exec(line);
1584
+ if (!match) {
1585
+ return undefined;
1586
+ }
1587
+ const method = match[2];
1588
+ const path = match[3];
1589
+ const status = Number(match[4]);
1590
+ const latencyMs = Number(match[5]);
1591
+ if (!method || !path || !Number.isInteger(status) || !Number.isInteger(latencyMs)) {
1592
+ return undefined;
1593
+ }
1594
+ return { method, path, status, latencyMs };
1419
1595
  }
1420
1596
  function confidenceForAccessLog(lineCount, parseRatio) {
1421
- if (lineCount < MIN_ACCESS_LOG_LINES) {
1422
- return roundConfidence(parseRatio * 0.5);
1423
- }
1424
- return roundConfidence(parseRatio);
1597
+ if (lineCount < MIN_ACCESS_LOG_LINES) {
1598
+ return roundConfidence(parseRatio * 0.5);
1599
+ }
1600
+ return roundConfidence(parseRatio);
1425
1601
  }
1426
1602
  function summarizeAccessLog(entries, ignoredLineCount) {
1427
- const statusCounts = {};
1428
- let errorCount = 0;
1429
- let latencyTotal = 0;
1430
- const slowExamples = [];
1431
- for (const entry of entries) {
1432
- const status = String(entry.status);
1433
- statusCounts[status] = (statusCounts[status] ?? 0) + 1;
1434
- if (entry.status >= 400) {
1435
- errorCount += 1;
1436
- }
1437
- latencyTotal += entry.latencyMs;
1438
- if (entry.latencyMs >= SLOW_REQUEST_THRESHOLD_MS && slowExamples.length < 5) {
1439
- slowExamples.push({ method: entry.method, path: entry.path, status: entry.status, latencyMs: entry.latencyMs });
1440
- }
1441
- }
1442
- const requestCount = entries.length;
1443
- const slowRequestCount = entries.filter((entry) => entry.latencyMs >= SLOW_REQUEST_THRESHOLD_MS).length;
1444
- return {
1445
- kind: "access-log",
1446
- requestCount,
1447
- statusCounts: sortStatusCounts(statusCounts),
1448
- errorCount,
1449
- errorRatePercent: roundPercent((errorCount / requestCount) * 100),
1450
- averageLatencyMs: Math.round(latencyTotal / requestCount),
1451
- slowThresholdMs: SLOW_REQUEST_THRESHOLD_MS,
1452
- slowRequestCount,
1453
- slowExamples,
1454
- ignoredLineCount,
1455
- };
1603
+ const statusCounts = {};
1604
+ let errorCount = 0;
1605
+ let latencyTotal = 0;
1606
+ const slowExamples = [];
1607
+ for (const entry of entries) {
1608
+ const status = String(entry.status);
1609
+ statusCounts[status] = (statusCounts[status] ?? 0) + 1;
1610
+ if (entry.status >= 400) {
1611
+ errorCount += 1;
1612
+ }
1613
+ latencyTotal += entry.latencyMs;
1614
+ if (entry.latencyMs >= SLOW_REQUEST_THRESHOLD_MS && slowExamples.length < 5) {
1615
+ slowExamples.push({ method: entry.method, path: entry.path, status: entry.status, latencyMs: entry.latencyMs });
1616
+ }
1617
+ }
1618
+ const requestCount = entries.length;
1619
+ const slowRequestCount = entries.filter((entry) => entry.latencyMs >= SLOW_REQUEST_THRESHOLD_MS).length;
1620
+ return {
1621
+ kind: "access-log",
1622
+ requestCount,
1623
+ statusCounts: sortStatusCounts(statusCounts),
1624
+ errorCount,
1625
+ errorRatePercent: roundPercent((errorCount / requestCount) * 100),
1626
+ averageLatencyMs: Math.round(latencyTotal / requestCount),
1627
+ slowThresholdMs: SLOW_REQUEST_THRESHOLD_MS,
1628
+ slowRequestCount,
1629
+ slowExamples,
1630
+ ignoredLineCount,
1631
+ };
1456
1632
  }
1457
1633
  function accessLogFacts(details) {
1458
- return [
1459
- { name: "requests", value: details.requestCount },
1460
- { name: "errors", value: details.errorCount },
1461
- { name: "errorRatePercent", value: details.errorRatePercent },
1462
- { name: "averageLatencyMs", value: details.averageLatencyMs },
1463
- { name: `slow>=${details.slowThresholdMs}ms`, value: details.slowRequestCount },
1464
- ...Object.entries(details.statusCounts).map(([status, count]) => ({ name: `status.${status}`, value: count })),
1465
- ];
1634
+ return [
1635
+ { name: "requests", value: details.requestCount },
1636
+ { name: "errors", value: details.errorCount },
1637
+ { name: "errorRatePercent", value: details.errorRatePercent },
1638
+ { name: "averageLatencyMs", value: details.averageLatencyMs },
1639
+ { name: `slow>=${details.slowThresholdMs}ms`, value: details.slowRequestCount },
1640
+ ...Object.entries(details.statusCounts).map(([status, count]) => ({ name: `status.${status}`, value: count })),
1641
+ ];
1466
1642
  }
1467
1643
  function renderAccessLogSummary(details) {
1468
- const lines = [
1469
- "access-log summary",
1470
- `requests: ${details.requestCount}`,
1471
- `errors: ${details.errorCount} (${details.errorRatePercent.toFixed(1)}%)`,
1472
- `avgLatencyMs: ${details.averageLatencyMs}`,
1473
- `slow>=${details.slowThresholdMs}ms: ${details.slowRequestCount}`,
1474
- `status: ${Object.entries(details.statusCounts).map(([status, count]) => `${status}:${count}`).join(", ")}`,
1475
- ];
1476
- if (details.slowExamples.length > 0) {
1477
- lines.push("slow examples:");
1478
- for (const example of details.slowExamples) {
1479
- lines.push(`- ${example.method} ${example.path} ${example.status} ${example.latencyMs}ms`);
1480
- }
1481
- }
1482
- if (details.ignoredLineCount > 0) {
1483
- lines.push(`ignoredLines: ${details.ignoredLineCount}`);
1484
- }
1485
- return lines.join("\n");
1644
+ const lines = [
1645
+ "access-log summary",
1646
+ `requests: ${details.requestCount}`,
1647
+ `errors: ${details.errorCount} (${details.errorRatePercent.toFixed(1)}%)`,
1648
+ `avgLatencyMs: ${details.averageLatencyMs}`,
1649
+ `slow>=${details.slowThresholdMs}ms: ${details.slowRequestCount}`,
1650
+ `status: ${Object.entries(details.statusCounts)
1651
+ .map(([status, count]) => `${status}:${count}`)
1652
+ .join(", ")}`,
1653
+ ];
1654
+ if (details.slowExamples.length > 0) {
1655
+ lines.push("slow examples:");
1656
+ for (const example of details.slowExamples) {
1657
+ lines.push(`- ${example.method} ${example.path} ${example.status} ${example.latencyMs}ms`);
1658
+ }
1659
+ }
1660
+ if (details.ignoredLineCount > 0) {
1661
+ lines.push(`ignoredLines: ${details.ignoredLineCount}`);
1662
+ }
1663
+ return lines.join("\n");
1486
1664
  }
1487
1665
  function sortStatusCounts(statusCounts) {
1488
- return Object.fromEntries(Object.entries(statusCounts).sort(([left], [right]) => Number(left) - Number(right)));
1666
+ return Object.fromEntries(Object.entries(statusCounts).sort(([left], [right]) => Number(left) - Number(right)));
1489
1667
  }
1490
1668
  function roundPercent(value) {
1491
- return Math.round(value * 10) / 10;
1669
+ return Math.round(value * 10) / 10;
1492
1670
  }
1493
1671
  function roundConfidence(value) {
1494
- return Math.round(value * 100) / 100;
1672
+ return Math.round(value * 100) / 100;
1495
1673
  }