@hassangameryt/freeflow 0.2.0 → 0.4.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 (312) hide show
  1. package/.agents/plugins/marketplace.json +1 -1
  2. package/.claude-plugin/marketplace.json +4 -4
  3. package/{plugins/freeflow/.claude-plugin → .claude-plugin}/plugin.json +5 -2
  4. package/{plugins/freeflow/.codex-plugin → .codex-plugin}/plugin.json +8 -9
  5. package/README.md +194 -127
  6. package/assets/feedback-loop.png +0 -0
  7. package/assets/freeflow-plugin-icon.png +0 -0
  8. package/assets/freeflow_workflow_map.png +0 -0
  9. package/assets/workflow.png +0 -0
  10. package/command-surface.json +129 -0
  11. package/hooks/freeflow-runtime-context.mjs +419 -0
  12. package/hooks/tests/check-runtime-context-hook.sh +223 -0
  13. package/package.json +41 -22
  14. package/pi-extension/dist/index.js +269 -0
  15. package/pi-extension/dist/integrations/mcp-config.js +29 -0
  16. package/pi-extension/dist/output-router/host-producer-identification.js +232 -0
  17. package/pi-extension/dist/output-router/native-safety-net.js +216 -0
  18. package/pi-extension/dist/output-router/observed-tool-routing.js +122 -0
  19. package/pi-extension/dist/output-router/renderers.js +1023 -0
  20. package/pi-extension/dist/output-router/router-tools.js +554 -0
  21. package/pi-extension/dist/output-router/schemas.js +383 -0
  22. package/pi-extension/dist/output-router/status.js +744 -0
  23. package/pi-extension/dist/output-router/utils.js +502 -0
  24. package/pi-extension/dist/runtime/runtime-context.js +798 -0
  25. package/pi-extension/dist/settings/settings-tui.js +314 -0
  26. package/pi-extension/dist/settings/settings-ui.js +1487 -0
  27. package/pi-extension/freeflow/index.js +1 -0
  28. package/pi-extension/src/index.ts +295 -0
  29. package/pi-extension/src/integrations/mcp-config.ts +32 -0
  30. package/pi-extension/src/output-router/host-producer-identification.ts +256 -0
  31. package/pi-extension/src/output-router/native-safety-net.ts +248 -0
  32. package/pi-extension/src/output-router/observed-tool-routing.ts +134 -0
  33. package/pi-extension/src/output-router/renderers.ts +1108 -0
  34. package/pi-extension/src/output-router/router-tools.ts +579 -0
  35. package/pi-extension/src/output-router/schemas.ts +387 -0
  36. package/pi-extension/src/output-router/status.ts +791 -0
  37. package/pi-extension/src/output-router/utils.ts +554 -0
  38. package/pi-extension/src/runtime/runtime-context.ts +895 -0
  39. package/pi-extension/src/settings/settings-tui.ts +408 -0
  40. package/pi-extension/src/settings/settings-ui.ts +1730 -0
  41. package/pi-extension/tests/integration/pi-extension.test.js +4018 -0
  42. package/pi-extension/tests/output-router/observed-producers.test.js +132 -0
  43. package/pi-extension/tests/output-router/search.test.js +722 -0
  44. package/pi-extension/tests/settings/settings-tui.test.js +243 -0
  45. package/pi-extension/tsconfig.json +16 -0
  46. package/{plugins/freeflow/router/dist → router/dist/benchmarks}/benchmark-harness.d.ts +16 -13
  47. package/router/dist/benchmarks/benchmark-harness.js +92 -0
  48. package/router/dist/benchmarks/benchmarks.d.ts +109 -0
  49. package/router/dist/benchmarks/benchmarks.js +979 -0
  50. package/router/dist/benchmarks/codex-qa-benchmarks.d.ts +95 -0
  51. package/router/dist/benchmarks/codex-qa-benchmarks.js +500 -0
  52. package/router/dist/benchmarks/command-benchmarks.d.ts +143 -0
  53. package/router/dist/benchmarks/command-benchmarks.js +698 -0
  54. package/router/dist/benchmarks/context-mode-normalized-benchmarks.d.ts +82 -0
  55. package/router/dist/benchmarks/context-mode-normalized-benchmarks.js +773 -0
  56. package/router/dist/benchmarks/context-mode-real-deep-benchmark.d.ts +132 -0
  57. package/router/dist/benchmarks/context-mode-real-deep-benchmark.js +1380 -0
  58. package/router/dist/benchmarks/context-saving-baseline.d.ts +60 -0
  59. package/router/dist/benchmarks/context-saving-baseline.js +470 -0
  60. package/router/dist/benchmarks/index-benchmarks.d.ts +103 -0
  61. package/router/dist/benchmarks/index-benchmarks.js +862 -0
  62. package/router/dist/benchmarks/storage-policy-benchmarks.d.ts +101 -0
  63. package/router/dist/benchmarks/storage-policy-benchmarks.js +618 -0
  64. package/router/dist/benchmarks/vault-index-benchmarks.d.ts +56 -0
  65. package/router/dist/benchmarks/vault-index-benchmarks.js +322 -0
  66. package/router/dist/config/config.d.ts +103 -0
  67. package/router/dist/config/config.js +632 -0
  68. package/{plugins/freeflow/router/dist → router/dist/config}/router-contract.d.ts +2 -2
  69. package/router/dist/config/router-contract.js +54 -0
  70. package/{plugins/freeflow/router/dist → router/dist/config}/schema.d.ts +19 -10
  71. package/router/dist/config/schema.js +648 -0
  72. package/router/dist/config/types.d.ts +573 -0
  73. package/router/dist/config/types.js +41 -0
  74. package/router/dist/evidence/bounded-evidence.d.ts +40 -0
  75. package/router/dist/evidence/bounded-evidence.js +323 -0
  76. package/router/dist/evidence/evidence-range-selector.d.ts +24 -0
  77. package/router/dist/evidence/evidence-range-selector.js +232 -0
  78. package/router/dist/evidence/evidence-search.d.ts +27 -0
  79. package/router/dist/evidence/evidence-search.js +490 -0
  80. package/{plugins/freeflow/router/dist → router/dist/evidence}/evidence.d.ts +18 -18
  81. package/router/dist/evidence/evidence.js +200 -0
  82. package/router/dist/evidence/failure-contracts.d.ts +37 -0
  83. package/router/dist/evidence/failure-contracts.js +166 -0
  84. package/router/dist/evidence/line-ranges.d.ts +24 -0
  85. package/router/dist/evidence/line-ranges.js +22 -0
  86. package/router/dist/experiments/local-index.d.ts +64 -0
  87. package/router/dist/experiments/local-index.js +581 -0
  88. package/router/dist/index.d.ts +30 -0
  89. package/router/dist/index.js +32 -0
  90. package/router/dist/local/local-traversal.d.ts +18 -0
  91. package/router/dist/local/local-traversal.js +334 -0
  92. package/router/dist/processing/engine.d.ts +132 -0
  93. package/router/dist/processing/engine.js +533 -0
  94. package/router/dist/processing/reducers.d.ts +280 -0
  95. package/router/dist/processing/reducers.js +1673 -0
  96. package/router/dist/processing/renderers.d.ts +31 -0
  97. package/router/dist/processing/renderers.js +195 -0
  98. package/router/dist/processing/scripts.d.ts +91 -0
  99. package/router/dist/processing/scripts.js +386 -0
  100. package/{plugins/freeflow/router/dist → router/dist/repo}/repo-traversal.d.ts +9 -9
  101. package/router/dist/repo/repo-traversal.js +245 -0
  102. package/router/dist/routing/observed-reducers.d.ts +41 -0
  103. package/router/dist/routing/observed-reducers.js +661 -0
  104. package/router/dist/routing/observed-routing.d.ts +26 -0
  105. package/router/dist/routing/observed-routing.js +378 -0
  106. package/router/dist/routing/parsers.d.ts +16 -0
  107. package/router/dist/routing/parsers.js +521 -0
  108. package/router/dist/routing/run-filters.d.ts +60 -0
  109. package/router/dist/routing/run-filters.js +238 -0
  110. package/router/dist/routing/run-reducers.d.ts +49 -0
  111. package/router/dist/routing/run-reducers.js +122 -0
  112. package/router/dist/sandbox/adapter-roots.d.ts +46 -0
  113. package/router/dist/sandbox/adapter-roots.js +66 -0
  114. package/router/dist/sandbox/eryx-python-adapter.d.ts +13 -0
  115. package/router/dist/sandbox/eryx-python-adapter.js +868 -0
  116. package/router/dist/sandbox/jq-wasm-adapter.d.ts +9 -0
  117. package/router/dist/sandbox/jq-wasm-adapter.js +429 -0
  118. package/router/dist/sandbox/quickjs-wasi-adapter.d.ts +13 -0
  119. package/router/dist/sandbox/quickjs-wasi-adapter.js +384 -0
  120. package/router/dist/sandbox/script-sandbox.d.ts +134 -0
  121. package/router/dist/sandbox/script-sandbox.js +337 -0
  122. package/router/dist/setup/script-transform-adapters.d.ts +32 -0
  123. package/router/dist/setup/script-transform-adapters.js +278 -0
  124. package/router/dist/tools/batch.d.ts +33 -0
  125. package/router/dist/tools/batch.js +586 -0
  126. package/router/dist/tools/run.d.ts +62 -0
  127. package/router/dist/tools/run.js +1304 -0
  128. package/router/dist/tools/search.d.ts +66 -0
  129. package/router/dist/tools/search.js +1364 -0
  130. package/router/dist/transform/engine.d.ts +168 -0
  131. package/router/dist/transform/engine.js +2072 -0
  132. package/router/dist/vault/vault-index.d.ts +125 -0
  133. package/router/dist/vault/vault-index.js +416 -0
  134. package/router/dist/vault/vault.d.ts +138 -0
  135. package/router/dist/vault/vault.js +630 -0
  136. package/runtime/interaction-contract.md +16 -0
  137. package/skills/bypass/SKILL.md +82 -0
  138. package/skills/commit-work/SKILL.md +106 -0
  139. package/skills/commit-work/references/staging-decisions.md +73 -0
  140. package/skills/decision-gate/SKILL.md +48 -0
  141. package/skills/design-for-depth/SKILL.md +106 -0
  142. package/skills/design-for-depth/references/design-pressure-signals.md +314 -0
  143. package/skills/design-for-depth/references/interface-design-loop.md +156 -0
  144. package/skills/design-for-depth/references/software-design-philosophy.md +248 -0
  145. package/skills/diagnose-failure/SKILL.md +105 -0
  146. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +36 -0
  147. package/skills/diagnose-failure/references/flaky-and-performance.md +43 -0
  148. package/skills/discuss/SKILL.md +121 -0
  149. package/skills/discuss/references/checkpoints.md +66 -0
  150. package/skills/evaluate-skill/SKILL.md +105 -0
  151. package/skills/evaluate-skill/references/definition-schema.md +345 -0
  152. package/skills/evaluate-skill/references/evaluation-design.md +86 -0
  153. package/skills/evaluate-skill/references/execution-and-evidence.md +85 -0
  154. package/skills/evaluate-skill/references/review-and-revision.md +51 -0
  155. package/skills/evaluate-skill/scripts/lib/definitions.mjs +296 -0
  156. package/skills/evaluate-skill/scripts/lib/evidence.mjs +46 -0
  157. package/skills/evaluate-skill/scripts/lib/grade-comparison.mjs +60 -0
  158. package/skills/evaluate-skill/scripts/lib/grade-interaction.mjs +148 -0
  159. package/skills/evaluate-skill/scripts/lib/grade-support.mjs +126 -0
  160. package/skills/evaluate-skill/scripts/lib/grade-workspace.mjs +192 -0
  161. package/skills/evaluate-skill/scripts/lib/grade.mjs +56 -0
  162. package/skills/evaluate-skill/scripts/lib/pi.mjs +489 -0
  163. package/skills/evaluate-skill/scripts/lib/process.mjs +79 -0
  164. package/skills/evaluate-skill/scripts/lib/rpc.mjs +444 -0
  165. package/skills/evaluate-skill/scripts/lib/runner.mjs +346 -0
  166. package/skills/evaluate-skill/scripts/lib/sandbox.mjs +409 -0
  167. package/skills/evaluate-skill/scripts/lib/view.mjs +337 -0
  168. package/skills/evaluate-skill/scripts/pi-guard.mjs +164 -0
  169. package/skills/evaluate-skill/scripts/skill-eval.mjs +101 -0
  170. package/skills/execute-work/SKILL.md +99 -0
  171. package/skills/execute-work/references/code-practices.md +78 -0
  172. package/skills/execute-work/references/execution-loop.md +77 -0
  173. package/skills/finish-branch/SKILL.md +117 -0
  174. package/skills/finish-branch/references/integration-options.md +80 -0
  175. package/skills/handoff/SKILL.md +92 -0
  176. package/skills/handoff/references/templates.md +141 -0
  177. package/skills/launch-work/SKILL.md +116 -0
  178. package/skills/launch-work/references/launch-readiness.md +85 -0
  179. package/skills/migration-work/SKILL.md +115 -0
  180. package/skills/migration-work/references/migration-lifecycle.md +82 -0
  181. package/skills/mode-contract/SKILL.md +82 -0
  182. package/skills/output-router/SKILL.md +206 -0
  183. package/skills/output-router/references/safety-policy.md +40 -0
  184. package/skills/release-work/SKILL.md +118 -0
  185. package/skills/release-work/references/release-evidence.md +78 -0
  186. package/skills/review-artifact/SKILL.md +126 -0
  187. package/skills/review-artifact/references/reviewer-prompt.md +148 -0
  188. package/skills/review-work/SKILL.md +126 -0
  189. package/skills/review-work/references/reviewer-prompt.md +134 -0
  190. package/skills/review-work/references/security-risk-lens.md +67 -0
  191. package/skills/setup-freeflow/SKILL.md +98 -0
  192. package/skills/setup-freeflow/references/activation-contract.md +120 -0
  193. package/skills/setup-freeflow/references/host-setup.md +72 -0
  194. package/skills/setup-freeflow/references/output-router-setup.md +110 -0
  195. package/skills/simplify-code/SKILL.md +80 -0
  196. package/skills/simplify-code/references/simplification-patterns.md +70 -0
  197. package/skills/tdd/SKILL.md +96 -0
  198. package/skills/tdd/references/test-design.md +96 -0
  199. package/skills/track-work/SKILL.md +84 -0
  200. package/skills/track-work/references/working-record-schema.md +378 -0
  201. package/skills/verify-work/SKILL.md +85 -0
  202. package/skills/verify-work/references/browser-runtime-evidence.md +55 -0
  203. package/skills/verify-work/references/integration-evidence.md +51 -0
  204. package/skills/verify-work/references/performance-evidence.md +57 -0
  205. package/skills/workflow/SKILL.md +91 -0
  206. package/skills/workflow/references/domain-skill-composition.md +57 -0
  207. package/skills/workflow/references/workflow-loop.md +92 -0
  208. package/skills/write-plan/SKILL.md +123 -0
  209. package/skills/write-plan/references/plan-shapes.md +156 -0
  210. package/skills/write-skill/SKILL.md +119 -0
  211. package/skills/write-skill/references/activation-boundaries.md +62 -0
  212. package/skills/write-skill/references/agent-first-instructions.md +87 -0
  213. package/skills/write-skill/references/progressive-disclosure.md +63 -0
  214. package/skills/write-skill/scripts/lib/skill-author.mjs +653 -0
  215. package/skills/write-skill/scripts/skill-author.mjs +104 -0
  216. package/skills/write-spec/SKILL.md +104 -0
  217. package/skills/write-spec/references/artifact-standards.md +86 -0
  218. package/skills/write-spec/references/decision-records.md +77 -0
  219. package/skills/write-spec/references/spec-shapes.md +128 -0
  220. package/plugins/freeflow/command-surface.json +0 -77
  221. package/plugins/freeflow/docs/README.md +0 -40
  222. package/plugins/freeflow/docs/adr/0001-three-modes.md +0 -27
  223. package/plugins/freeflow/docs/adr/0002-evals-before-hooks.md +0 -17
  224. package/plugins/freeflow/docs/adr/0003-release-boundary.md +0 -21
  225. package/plugins/freeflow/docs/adr/0004-research-replaces-shallow-discovery-skills.md +0 -26
  226. package/plugins/freeflow/docs/adr/README.md +0 -8
  227. package/plugins/freeflow/docs/architecture.md +0 -86
  228. package/plugins/freeflow/docs/output-router.md +0 -212
  229. package/plugins/freeflow/docs/release-evidence.md +0 -69
  230. package/plugins/freeflow/docs/skills.md +0 -43
  231. package/plugins/freeflow/docs/workflow.md +0 -57
  232. package/plugins/freeflow/hooks/freeflow-runtime-context.mjs +0 -257
  233. package/plugins/freeflow/pi-extension/index.js +0 -1176
  234. package/plugins/freeflow/router/dist/benchmark-harness.js +0 -92
  235. package/plugins/freeflow/router/dist/benchmarks.d.ts +0 -102
  236. package/plugins/freeflow/router/dist/benchmarks.js +0 -880
  237. package/plugins/freeflow/router/dist/bounded-evidence.d.ts +0 -40
  238. package/plugins/freeflow/router/dist/bounded-evidence.js +0 -310
  239. package/plugins/freeflow/router/dist/codex-qa-benchmarks.d.ts +0 -91
  240. package/plugins/freeflow/router/dist/codex-qa-benchmarks.js +0 -461
  241. package/plugins/freeflow/router/dist/command-benchmarks.d.ts +0 -128
  242. package/plugins/freeflow/router/dist/command-benchmarks.js +0 -652
  243. package/plugins/freeflow/router/dist/config.d.ts +0 -22
  244. package/plugins/freeflow/router/dist/config.js +0 -114
  245. package/plugins/freeflow/router/dist/evidence-range-selector.d.ts +0 -22
  246. package/plugins/freeflow/router/dist/evidence-range-selector.js +0 -180
  247. package/plugins/freeflow/router/dist/evidence-search.d.ts +0 -25
  248. package/plugins/freeflow/router/dist/evidence-search.js +0 -450
  249. package/plugins/freeflow/router/dist/evidence.js +0 -196
  250. package/plugins/freeflow/router/dist/experimental-local-index.d.ts +0 -1
  251. package/plugins/freeflow/router/dist/experimental-local-index.js +0 -1
  252. package/plugins/freeflow/router/dist/experiments/local-index.d.ts +0 -58
  253. package/plugins/freeflow/router/dist/experiments/local-index.js +0 -562
  254. package/plugins/freeflow/router/dist/index-benchmarks.d.ts +0 -84
  255. package/plugins/freeflow/router/dist/index-benchmarks.js +0 -458
  256. package/plugins/freeflow/router/dist/index.d.ts +0 -12
  257. package/plugins/freeflow/router/dist/index.js +0 -12
  258. package/plugins/freeflow/router/dist/line-ranges.d.ts +0 -22
  259. package/plugins/freeflow/router/dist/line-ranges.js +0 -22
  260. package/plugins/freeflow/router/dist/parsers.d.ts +0 -16
  261. package/plugins/freeflow/router/dist/parsers.js +0 -473
  262. package/plugins/freeflow/router/dist/repo-traversal.js +0 -242
  263. package/plugins/freeflow/router/dist/retrieve.d.ts +0 -33
  264. package/plugins/freeflow/router/dist/retrieve.js +0 -855
  265. package/plugins/freeflow/router/dist/router-contract.js +0 -54
  266. package/plugins/freeflow/router/dist/run.d.ts +0 -26
  267. package/plugins/freeflow/router/dist/run.js +0 -356
  268. package/plugins/freeflow/router/dist/schema.js +0 -354
  269. package/plugins/freeflow/router/dist/types.d.ts +0 -234
  270. package/plugins/freeflow/router/dist/types.js +0 -9
  271. package/plugins/freeflow/router/dist/vault.d.ts +0 -80
  272. package/plugins/freeflow/router/dist/vault.js +0 -484
  273. package/plugins/freeflow/skills/bypass/SKILL.md +0 -68
  274. package/plugins/freeflow/skills/commit-work/SKILL.md +0 -98
  275. package/plugins/freeflow/skills/commit-work/references/staging-decisions.md +0 -51
  276. package/plugins/freeflow/skills/diagnose-failure/SKILL.md +0 -95
  277. package/plugins/freeflow/skills/diagnose-failure/references/feedback-loop-catalog.md +0 -25
  278. package/plugins/freeflow/skills/diagnose-failure/references/flaky-and-performance.md +0 -31
  279. package/plugins/freeflow/skills/evaluate-skill/SKILL.md +0 -96
  280. package/plugins/freeflow/skills/evaluate-skill/references/eval-patterns.md +0 -41
  281. package/plugins/freeflow/skills/evaluate-skill/references/grading-priority.md +0 -33
  282. package/plugins/freeflow/skills/execute-plan/SKILL.md +0 -132
  283. package/plugins/freeflow/skills/execute-plan/references/execution-map.md +0 -122
  284. package/plugins/freeflow/skills/handoff/SKILL.md +0 -66
  285. package/plugins/freeflow/skills/handoff/references/templates.md +0 -64
  286. package/plugins/freeflow/skills/interview-gate/SKILL.md +0 -90
  287. package/plugins/freeflow/skills/mode-contract/SKILL.md +0 -79
  288. package/plugins/freeflow/skills/output-router/SKILL.md +0 -69
  289. package/plugins/freeflow/skills/output-router/references/safety-policy.md +0 -33
  290. package/plugins/freeflow/skills/research/ARTIFACT-DESTINATIONS.md +0 -58
  291. package/plugins/freeflow/skills/research/CHECKPOINTS.md +0 -74
  292. package/plugins/freeflow/skills/research/LANGUAGE.md +0 -61
  293. package/plugins/freeflow/skills/research/SKILL.md +0 -128
  294. package/plugins/freeflow/skills/review-artifact/SKILL.md +0 -115
  295. package/plugins/freeflow/skills/review-artifact/references/reviewer-prompt.md +0 -56
  296. package/plugins/freeflow/skills/review-work/SKILL.md +0 -96
  297. package/plugins/freeflow/skills/review-work/references/reviewer-prompt.md +0 -97
  298. package/plugins/freeflow/skills/setup-freeflow/SKILL.md +0 -94
  299. package/plugins/freeflow/skills/setup-freeflow/references/activation-contract.md +0 -99
  300. package/plugins/freeflow/skills/setup-freeflow/references/host-setup.md +0 -35
  301. package/plugins/freeflow/skills/setup-freeflow/references/output-router-setup.md +0 -52
  302. package/plugins/freeflow/skills/verify-work/SKILL.md +0 -62
  303. package/plugins/freeflow/skills/workflow/SKILL.md +0 -95
  304. package/plugins/freeflow/skills/workflow/references/workflow-map.md +0 -102
  305. package/plugins/freeflow/skills/write-plan/SKILL.md +0 -88
  306. package/plugins/freeflow/skills/write-plan/references/plan-shapes.md +0 -70
  307. package/plugins/freeflow/skills/write-skill/SKILL.md +0 -88
  308. package/plugins/freeflow/skills/write-spec/SKILL.md +0 -90
  309. package/plugins/freeflow/skills/write-spec/references/artifact-standards.md +0 -67
  310. package/plugins/freeflow/skills/write-spec/references/spec-shapes.md +0 -69
  311. /package/{plugins/freeflow/assets → assets}/freeflow-plugin-icon-transparent.png +0 -0
  312. /package/{plugins/freeflow/hooks → hooks}/hooks.json +0 -0
@@ -0,0 +1,243 @@
1
+ import assert from "node:assert/strict";
2
+ import test from "node:test";
3
+
4
+ import { CURSOR_MARKER, visibleWidth } from "@earendil-works/pi-tui";
5
+
6
+ import { PiSettingsComponent } from "../../dist/settings/settings-tui.js";
7
+
8
+ const theme = {
9
+ fg(_color, text) {
10
+ return text;
11
+ },
12
+ bold(text) {
13
+ return text;
14
+ },
15
+ };
16
+
17
+ function deferred() {
18
+ let resolve;
19
+ let reject;
20
+ const promise = new Promise((resolvePromise, rejectPromise) => {
21
+ resolve = resolvePromise;
22
+ reject = rejectPromise;
23
+ });
24
+ return { promise, resolve, reject };
25
+ }
26
+
27
+ function createComponent(entries, options = {}) {
28
+ const notifications = [];
29
+ let closed = false;
30
+ const component = new PiSettingsComponent({
31
+ title: options.title ?? "Test Settings",
32
+ entries,
33
+ theme: options.theme ?? theme,
34
+ requestRender() {},
35
+ notify(message, level) {
36
+ notifications.push({ message, level });
37
+ },
38
+ done() {
39
+ closed = true;
40
+ },
41
+ });
42
+ return { component, notifications, isClosed: () => closed };
43
+ }
44
+
45
+ test("Pi settings publish values only after persistence and block concurrent edits", async () => {
46
+ let current = "disabled";
47
+ let commits = 0;
48
+ const write = deferred();
49
+ const entry = {
50
+ id: "feature.enabled",
51
+ label: "Feature",
52
+ description: "Enable the feature.",
53
+ currentValue: () => current,
54
+ inactive: () => false,
55
+ currentChoiceKey: () => (current === "enabled" ? "true" : "false"),
56
+ choices: [
57
+ { key: "true", value: true, label: "enabled" },
58
+ { key: "false", value: false, label: "disabled" },
59
+ ],
60
+ async commit() {
61
+ commits += 1;
62
+ await write.promise;
63
+ current = "enabled";
64
+ return { changed: true, reloadRequired: true };
65
+ },
66
+ };
67
+ const { component } = createComponent([entry]);
68
+
69
+ component.handleInput("\r");
70
+ component.handleInput("\u001b[A");
71
+ component.handleInput("\r");
72
+ component.handleInput("\r");
73
+
74
+ assert.equal(commits, 1);
75
+ assert.equal(current, "disabled");
76
+ write.resolve();
77
+ await component.waitForWrites();
78
+ assert.equal(current, "enabled");
79
+ assert.match(component.render(80).join("\n"), /Feature\s+enabled/);
80
+ assert.deepEqual(component.sessionResult(), { changed: true, configChanged: true, failed: false });
81
+ });
82
+
83
+ test("Pi settings retain the persisted value when a write fails", async () => {
84
+ const current = "disabled";
85
+ const entry = {
86
+ id: "feature.enabled",
87
+ label: "Feature",
88
+ description: "Enable the feature.",
89
+ currentValue: () => current,
90
+ inactive: () => false,
91
+ currentChoiceKey: () => "false",
92
+ choices: [
93
+ { key: "true", value: true, label: "enabled" },
94
+ { key: "false", value: false, label: "disabled" },
95
+ ],
96
+ async commit() {
97
+ throw new Error("disk unavailable");
98
+ },
99
+ };
100
+ const { component, notifications } = createComponent([entry]);
101
+
102
+ component.handleInput("\r");
103
+ component.handleInput("\u001b[A");
104
+ component.handleInput("\r");
105
+ await component.waitForWrites();
106
+ component.handleInput("\u001b");
107
+
108
+ assert.equal(current, "disabled");
109
+ assert.match(component.render(80).join("\n"), /Feature\s+disabled/);
110
+ assert.deepEqual(component.sessionResult(), { changed: false, configChanged: false, failed: true });
111
+ assert.deepEqual(notifications, [{ message: "Write failed: disk unavailable", level: "error" }]);
112
+ });
113
+
114
+ test("Pi settings delegate paste, cursor, grapheme, and IME behavior to Input", async () => {
115
+ let current = "";
116
+ const entry = {
117
+ id: "feature.label",
118
+ label: "Label",
119
+ description: "Unicode label.",
120
+ currentValue: () => current,
121
+ inactive: () => false,
122
+ edit: {
123
+ initialValue: () => current,
124
+ parse: (text) => text,
125
+ },
126
+ async commit(value) {
127
+ current = value;
128
+ return { changed: true, reloadRequired: true };
129
+ },
130
+ };
131
+ const { component } = createComponent([entry]);
132
+ component.focused = true;
133
+
134
+ component.handleInput("\r");
135
+ assert.ok(component.render(40).join("\n").includes(CURSOR_MARKER));
136
+ component.handleInput("\u001b[200~ab🙂\u001b[201~");
137
+ component.handleInput("\u001b[D");
138
+ component.handleInput("\u007f");
139
+ component.handleInput("界");
140
+ component.handleInput("\r");
141
+ await component.waitForWrites();
142
+
143
+ assert.equal(current, "a界🙂");
144
+ });
145
+
146
+ test("Pi settings start value editing at the end of the existing value", async () => {
147
+ let current = "abc";
148
+ const entry = {
149
+ id: "feature.label",
150
+ label: "Label",
151
+ description: "Editable label.",
152
+ currentValue: () => current,
153
+ inactive: () => false,
154
+ edit: {
155
+ initialValue: () => current,
156
+ parse: (text) => text,
157
+ },
158
+ async commit(value) {
159
+ current = value;
160
+ return { changed: true, reloadRequired: true };
161
+ },
162
+ };
163
+ const { component } = createComponent([entry]);
164
+
165
+ component.handleInput("\r");
166
+ component.handleInput("X");
167
+ component.handleInput("\r");
168
+ await component.waitForWrites();
169
+
170
+ assert.equal(current, "abcX");
171
+ });
172
+
173
+ test("Pi settings keep typed input open when parsing fails", () => {
174
+ let commits = 0;
175
+ const entry = {
176
+ id: "feature.limit",
177
+ label: "Limit",
178
+ description: "Positive integer.",
179
+ currentValue: () => "1",
180
+ inactive: () => false,
181
+ edit: {
182
+ initialValue: () => "",
183
+ parse(text) {
184
+ const value = Number(text);
185
+ if (!Number.isInteger(value) || value <= 0) throw new Error("expected a positive integer");
186
+ return value;
187
+ },
188
+ },
189
+ async commit() {
190
+ commits += 1;
191
+ return { changed: true, reloadRequired: true };
192
+ },
193
+ };
194
+ const { component } = createComponent([entry]);
195
+
196
+ component.handleInput("\r");
197
+ component.handleInput("invalid");
198
+ component.handleInput("\r");
199
+
200
+ assert.equal(commits, 0);
201
+ assert.match(component.render(60).join("\n"), /Invalid value: expected a positive integer/);
202
+ });
203
+
204
+ test("Pi settings search accepts bracketed paste", () => {
205
+ const entries = ["Alpha", "Beta"].map((label) => ({
206
+ id: label.toLowerCase(),
207
+ label,
208
+ description: `${label} setting`,
209
+ currentValue: () => "off",
210
+ inactive: () => true,
211
+ }));
212
+ const { component } = createComponent(entries);
213
+
214
+ component.handleInput("\u001b[200~Beta\u001b[201~");
215
+ const rendered = component.render(60).join("\n");
216
+ assert.match(rendered, /Beta/);
217
+ assert.doesNotMatch(rendered, /Alpha/);
218
+ });
219
+
220
+ test("Pi settings keep ANSI and Unicode rendering within terminal width", () => {
221
+ const ansiTheme = {
222
+ fg(_color, text) {
223
+ return `\u001b[36m${text}\u001b[0m`;
224
+ },
225
+ bold(text) {
226
+ return `\u001b[1m${text}\u001b[22m`;
227
+ },
228
+ };
229
+ const entry = {
230
+ id: "unicode",
231
+ label: "設定🙂",
232
+ description: "幅の広い Unicode description with several words.",
233
+ currentValue: () => "有効界",
234
+ inactive: () => true,
235
+ };
236
+ const { component } = createComponent([entry], { title: "設定パネル", theme: ansiTheme });
237
+
238
+ for (const width of [12, 20, 40]) {
239
+ for (const line of component.render(width)) {
240
+ assert.ok(visibleWidth(line) <= width, `line width ${visibleWidth(line)} exceeds ${width}: ${line}`);
241
+ }
242
+ }
243
+ });
@@ -0,0 +1,16 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2022",
4
+ "module": "NodeNext",
5
+ "moduleResolution": "NodeNext",
6
+ "rootDir": "src",
7
+ "outDir": "dist",
8
+ "declaration": false,
9
+ "sourceMap": false,
10
+ "strict": false,
11
+ "noEmitOnError": true,
12
+ "skipLibCheck": true,
13
+ "types": ["node"]
14
+ },
15
+ "include": ["src/**/*.ts"]
16
+ }
@@ -1,14 +1,14 @@
1
1
  export interface LatencySummary {
2
- p50: number;
3
- p95: number;
2
+ p50: number;
3
+ p95: number;
4
4
  }
5
5
  export interface BenchmarkCliDefaults {
6
- reportPath: string;
6
+ reportPath: string;
7
7
  }
8
8
  export interface ParsedBenchmarkCliArgs {
9
- iterations?: number;
10
- reportPath: string;
11
- jsonReportPath?: string | false;
9
+ iterations?: number;
10
+ reportPath: string;
11
+ jsonReportPath?: string | false;
12
12
  }
13
13
  export declare function normalizeIterations(value: unknown, defaultIterations?: number): number;
14
14
  export declare function approximateTokens(bytes: number): number;
@@ -20,13 +20,16 @@ export declare function reductionPercent(raw: number, routed: number): number;
20
20
  export declare function formatPercent(value: number): string;
21
21
  export declare function escapeMarkdownTableCell(value: string): string;
22
22
  export declare function defaultJsonRunReportPath(markdownReportPath: string): string;
23
- export declare function parseBenchmarkCliArgs(argv: readonly string[], defaults: BenchmarkCliDefaults): ParsedBenchmarkCliArgs;
23
+ export declare function parseBenchmarkCliArgs(
24
+ argv: readonly string[],
25
+ defaults: BenchmarkCliDefaults,
26
+ ): ParsedBenchmarkCliArgs;
24
27
  export declare function writeBenchmarkReportPair<TReport>(options: {
25
- report: TReport;
26
- markdownReportPath: string;
27
- jsonReportPath?: string | false | undefined;
28
- renderMarkdown(report: TReport): string;
28
+ report: TReport;
29
+ markdownReportPath: string;
30
+ jsonReportPath?: string | false | undefined;
31
+ renderMarkdown(report: TReport): string;
29
32
  }): Promise<{
30
- markdown: string;
31
- json?: string;
33
+ markdown: string;
34
+ json?: string;
32
35
  }>;
@@ -0,0 +1,92 @@
1
+ import { mkdir, writeFile } from "node:fs/promises";
2
+ import { basename, dirname, resolve } from "node:path";
3
+ export function normalizeIterations(value, defaultIterations = 3) {
4
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
5
+ return Math.max(1, Math.floor(value));
6
+ }
7
+ if (Number.isFinite(defaultIterations) && defaultIterations > 0) {
8
+ return Math.max(1, Math.floor(defaultIterations));
9
+ }
10
+ return 1;
11
+ }
12
+ export function approximateTokens(bytes) {
13
+ return Math.ceil(bytes / 4);
14
+ }
15
+ export function percentile(values, quantile) {
16
+ if (values.length === 0) {
17
+ return 0;
18
+ }
19
+ const sorted = [...values].sort((a, b) => a - b);
20
+ const index = Math.min(sorted.length - 1, Math.max(0, Math.ceil(sorted.length * quantile) - 1));
21
+ return sorted[index] ?? 0;
22
+ }
23
+ export function latencySummary(values) {
24
+ return {
25
+ p50: percentile(values, 0.5),
26
+ p95: percentile(values, 0.95),
27
+ };
28
+ }
29
+ export function averagePercent(values) {
30
+ if (values.length === 0) {
31
+ return 0;
32
+ }
33
+ return roundPercent(values.reduce((sum, value) => sum + value, 0) / values.length);
34
+ }
35
+ export function medianPercent(values) {
36
+ return roundPercent(percentile(values, 0.5));
37
+ }
38
+ export function reductionPercent(raw, routed) {
39
+ if (raw <= 0) {
40
+ return 0;
41
+ }
42
+ return roundPercent(((raw - routed) / raw) * 100);
43
+ }
44
+ export function formatPercent(value) {
45
+ return `${value.toFixed(2)}%`;
46
+ }
47
+ export function escapeMarkdownTableCell(value) {
48
+ return value.replace(/\|/g, "\\|").replace(/\n/g, " ");
49
+ }
50
+ export function defaultJsonRunReportPath(markdownReportPath) {
51
+ const markdownName = basename(markdownReportPath);
52
+ const jsonName = markdownName.endsWith(".md")
53
+ ? `${markdownName.slice(0, -".md".length)}.json`
54
+ : `${markdownName}.json`;
55
+ return resolve(process.cwd(), "router/evals/runs", jsonName);
56
+ }
57
+ export function parseBenchmarkCliArgs(argv, defaults) {
58
+ let iterations;
59
+ let reportPath = defaults.reportPath;
60
+ let jsonReportPath;
61
+ for (const arg of argv) {
62
+ if (arg.startsWith("--iterations=")) {
63
+ const value = Number(arg.slice("--iterations=".length));
64
+ if (Number.isFinite(value) && value > 0) {
65
+ iterations = value;
66
+ }
67
+ } else if (arg.startsWith("--report=")) {
68
+ reportPath = resolve(process.cwd(), arg.slice("--report=".length));
69
+ } else if (arg.startsWith("--json-report=")) {
70
+ const value = arg.slice("--json-report=".length);
71
+ jsonReportPath = value === "off" ? false : resolve(process.cwd(), value);
72
+ }
73
+ }
74
+ const parsed = iterations === undefined ? { reportPath } : { iterations, reportPath };
75
+ return jsonReportPath === undefined ? parsed : { ...parsed, jsonReportPath };
76
+ }
77
+ export async function writeBenchmarkReportPair(options) {
78
+ await mkdir(dirname(options.markdownReportPath), { recursive: true });
79
+ await writeFile(options.markdownReportPath, options.renderMarkdown(options.report), "utf8");
80
+ if (options.jsonReportPath === false) {
81
+ return { markdown: options.markdownReportPath };
82
+ }
83
+ if (options.jsonReportPath) {
84
+ await mkdir(dirname(options.jsonReportPath), { recursive: true });
85
+ await writeFile(options.jsonReportPath, `${JSON.stringify(options.report, null, 2)}\n`, "utf8");
86
+ return { markdown: options.markdownReportPath, json: options.jsonReportPath };
87
+ }
88
+ return { markdown: options.markdownReportPath };
89
+ }
90
+ function roundPercent(value) {
91
+ return Math.round(value * 100) / 100;
92
+ }
@@ -0,0 +1,109 @@
1
+ export interface RunRouterBenchmarksOptions {
2
+ iterations?: number;
3
+ generatedAt?: string;
4
+ }
5
+ export interface RouterBenchmarkReport {
6
+ generatedAt: string;
7
+ iterations: number;
8
+ summary: RouterBenchmarkSummary;
9
+ fixtures: RouterBenchmarkFixtureResult[];
10
+ skippedExternalTools: SkippedExternalTool[];
11
+ }
12
+ export interface RouterBenchmarkSummary {
13
+ fixtures: number;
14
+ modeResults: number;
15
+ improved: ModeSummary;
16
+ nativeBaseline: ModeSummary;
17
+ freeflowBaseline: ModeSummary;
18
+ generatedFalsePositiveCount: number;
19
+ sandboxFailureFixed: boolean;
20
+ }
21
+ export interface ModeSummary {
22
+ passed: number;
23
+ failed: number;
24
+ skipped: number;
25
+ pathCorrect: number;
26
+ spanCorrect: number;
27
+ excerptComplete: number;
28
+ generatedFalsePositiveCount: number;
29
+ totalRawBytes: number;
30
+ totalRoutedBytes: number;
31
+ totalRawTokensApprox: number;
32
+ totalRoutedTokensApprox: number;
33
+ weightedByteReductionPercent: number;
34
+ weightedTokenReductionPercent: number;
35
+ averageByteReductionPercent: number;
36
+ medianByteReductionPercent: number;
37
+ averageTokenReductionPercent: number;
38
+ medianTokenReductionPercent: number;
39
+ }
40
+ export interface RouterBenchmarkFixtureResult {
41
+ id: string;
42
+ title: string;
43
+ kind: BenchmarkKind;
44
+ expected: BenchmarkExpected;
45
+ results: RouterBenchmarkModeResult[];
46
+ }
47
+ export interface RouterBenchmarkModeResult {
48
+ mode: BenchmarkMode;
49
+ toolPathUsed: string;
50
+ skipped: boolean;
51
+ skipReason?: string;
52
+ rawBytes: number;
53
+ rawTokensApprox: number;
54
+ routedBytes: number;
55
+ routedTokensApprox: number;
56
+ byteReductionPercent: number;
57
+ tokenReductionPercent: number;
58
+ latencyMs: {
59
+ p50: number;
60
+ p95: number;
61
+ };
62
+ actualPath?: string;
63
+ actualLines?: string;
64
+ correctness: CorrectnessResult;
65
+ recovery: RecoveryResult;
66
+ notes: string[];
67
+ }
68
+ export interface BenchmarkExpected {
69
+ path?: string;
70
+ pathIncludes?: string;
71
+ lines?: string;
72
+ requiredExcerpt?: string[];
73
+ }
74
+ export interface CorrectnessResult {
75
+ passed: boolean;
76
+ pathCorrect: boolean;
77
+ spanCorrect: boolean;
78
+ excerptComplete: boolean;
79
+ generatedFalsePositive: boolean;
80
+ }
81
+ export interface RecoveryResult {
82
+ status: "passed" | "failed" | "not-applicable" | "skipped";
83
+ detail: string;
84
+ }
85
+ export interface SkippedExternalTool {
86
+ name: string;
87
+ reason: string;
88
+ }
89
+ type BenchmarkKind = "repo-query" | "repo-expand" | "vault-query";
90
+ type BenchmarkMode = "native-baseline-proxy" | "pre-hardening-freeflow-proxy" | "improved-freeflow-router";
91
+ export declare function runRouterBenchmarks(options?: RunRouterBenchmarksOptions): Promise<RouterBenchmarkReport>;
92
+ export declare function renderRouterBenchmarkReport(report: RouterBenchmarkReport): string;
93
+ export declare function writeRouterBenchmarkReport(report: RouterBenchmarkReport, reportPath: string): Promise<void>;
94
+ export declare function writeRouterBenchmarkJsonReport(
95
+ report: RouterBenchmarkReport,
96
+ reportPath: string,
97
+ ): Promise<void>;
98
+ export interface WriteRouterBenchmarkReportsOptions {
99
+ jsonReportPath?: string | false;
100
+ }
101
+ export declare function writeRouterBenchmarkReports(
102
+ report: RouterBenchmarkReport,
103
+ markdownReportPath: string,
104
+ options?: WriteRouterBenchmarkReportsOptions,
105
+ ): Promise<{
106
+ markdown: string;
107
+ json?: string;
108
+ }>;
109
+ export {};