@hassangameryt/freeflow 0.3.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 (690) hide show
  1. package/.claude-plugin/marketplace.json +4 -4
  2. package/.claude-plugin/plugin.json +5 -2
  3. package/.codex-plugin/plugin.json +8 -9
  4. package/README.md +190 -128
  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/command-surface.json +51 -7
  9. package/hooks/freeflow-runtime-context.mjs +264 -136
  10. package/hooks/tests/check-runtime-context-hook.sh +223 -0
  11. package/package.json +23 -24
  12. package/pi-extension/dist/index.js +264 -80
  13. package/pi-extension/dist/integrations/mcp-config.js +29 -0
  14. package/pi-extension/dist/output-router/host-producer-identification.js +232 -0
  15. package/pi-extension/dist/output-router/native-safety-net.js +216 -0
  16. package/pi-extension/dist/output-router/observed-tool-routing.js +122 -0
  17. package/pi-extension/dist/output-router/renderers.js +1023 -0
  18. package/pi-extension/dist/output-router/router-tools.js +554 -0
  19. package/pi-extension/dist/output-router/schemas.js +383 -0
  20. package/pi-extension/dist/output-router/status.js +744 -0
  21. package/pi-extension/dist/output-router/utils.js +502 -0
  22. package/pi-extension/dist/runtime/runtime-context.js +798 -0
  23. package/pi-extension/dist/settings/settings-tui.js +314 -0
  24. package/pi-extension/dist/settings/settings-ui.js +1487 -0
  25. package/pi-extension/freeflow/index.js +1 -0
  26. package/pi-extension/src/index.ts +230 -56
  27. package/pi-extension/src/{host-producer-identification.ts → output-router/host-producer-identification.ts} +37 -7
  28. package/pi-extension/src/{native-safety-net.ts → output-router/native-safety-net.ts} +7 -11
  29. package/pi-extension/src/{observed-tool-routing.ts → output-router/observed-tool-routing.ts} +19 -11
  30. package/pi-extension/src/{renderers.ts → output-router/renderers.ts} +246 -55
  31. package/pi-extension/src/{router-tools.ts → output-router/router-tools.ts} +130 -29
  32. package/pi-extension/src/{schemas.ts → output-router/schemas.ts} +171 -43
  33. package/pi-extension/src/output-router/status.ts +791 -0
  34. package/pi-extension/src/{utils.ts → output-router/utils.ts} +80 -18
  35. package/pi-extension/src/runtime/runtime-context.ts +895 -0
  36. package/pi-extension/src/settings/settings-tui.ts +408 -0
  37. package/pi-extension/src/settings/settings-ui.ts +1730 -0
  38. package/pi-extension/tests/integration/pi-extension.test.js +4018 -0
  39. package/pi-extension/tests/output-router/observed-producers.test.js +132 -0
  40. package/pi-extension/tests/output-router/search.test.js +722 -0
  41. package/pi-extension/tests/settings/settings-tui.test.js +243 -0
  42. package/router/dist/benchmarks/benchmark-harness.d.ts +16 -13
  43. package/router/dist/benchmarks/benchmark-harness.js +62 -62
  44. package/router/dist/benchmarks/benchmarks.d.ts +80 -73
  45. package/router/dist/benchmarks/benchmarks.js +860 -761
  46. package/router/dist/benchmarks/codex-qa-benchmarks.d.ts +71 -67
  47. package/router/dist/benchmarks/codex-qa-benchmarks.js +429 -390
  48. package/router/dist/benchmarks/command-benchmarks.d.ts +107 -92
  49. package/router/dist/benchmarks/command-benchmarks.js +616 -571
  50. package/router/dist/benchmarks/context-mode-normalized-benchmarks.d.ts +64 -56
  51. package/router/dist/benchmarks/context-mode-normalized-benchmarks.js +679 -613
  52. package/router/dist/benchmarks/context-mode-real-deep-benchmark.d.ts +103 -95
  53. package/router/dist/benchmarks/context-mode-real-deep-benchmark.js +1188 -1103
  54. package/router/dist/benchmarks/context-saving-baseline.d.ts +47 -41
  55. package/router/dist/benchmarks/context-saving-baseline.js +405 -374
  56. package/router/dist/benchmarks/index-benchmarks.d.ts +74 -69
  57. package/router/dist/benchmarks/index-benchmarks.js +761 -661
  58. package/router/dist/benchmarks/storage-policy-benchmarks.d.ts +80 -69
  59. package/router/dist/benchmarks/storage-policy-benchmarks.js +549 -469
  60. package/router/dist/benchmarks/vault-index-benchmarks.d.ts +43 -34
  61. package/router/dist/benchmarks/vault-index-benchmarks.js +294 -229
  62. package/router/dist/config/config.d.ts +61 -54
  63. package/router/dist/config/config.js +551 -372
  64. package/router/dist/config/router-contract.d.ts +2 -2
  65. package/router/dist/config/router-contract.js +38 -38
  66. package/router/dist/config/schema.d.ts +19 -10
  67. package/router/dist/config/schema.js +586 -538
  68. package/router/dist/config/types.d.ts +413 -361
  69. package/router/dist/config/types.js +19 -8
  70. package/router/dist/evidence/bounded-evidence.d.ts +26 -26
  71. package/router/dist/evidence/bounded-evidence.js +265 -252
  72. package/router/dist/evidence/evidence-range-selector.d.ts +16 -14
  73. package/router/dist/evidence/evidence-range-selector.js +192 -191
  74. package/router/dist/evidence/evidence-search.d.ts +18 -16
  75. package/router/dist/evidence/evidence-search.js +401 -377
  76. package/router/dist/evidence/evidence.d.ts +17 -17
  77. package/router/dist/evidence/evidence.js +163 -159
  78. package/router/dist/evidence/failure-contracts.d.ts +25 -15
  79. package/router/dist/evidence/failure-contracts.js +132 -126
  80. package/router/dist/evidence/line-ranges.d.ts +17 -15
  81. package/router/dist/evidence/line-ranges.js +18 -18
  82. package/router/dist/experiments/local-index.d.ts +47 -41
  83. package/router/dist/experiments/local-index.js +477 -458
  84. package/router/dist/local/local-traversal.d.ts +18 -0
  85. package/router/dist/local/local-traversal.js +334 -0
  86. package/router/dist/processing/engine.d.ts +94 -70
  87. package/router/dist/processing/engine.js +462 -364
  88. package/router/dist/processing/reducers.d.ts +208 -194
  89. package/router/dist/processing/reducers.js +1434 -1256
  90. package/router/dist/processing/renderers.d.ts +24 -18
  91. package/router/dist/processing/renderers.js +161 -152
  92. package/router/dist/processing/scripts.d.ts +80 -67
  93. package/router/dist/processing/scripts.js +343 -290
  94. package/router/dist/repo/repo-traversal.d.ts +9 -9
  95. package/router/dist/repo/repo-traversal.js +205 -202
  96. package/router/dist/routing/observed-reducers.d.ts +25 -19
  97. package/router/dist/routing/observed-reducers.js +559 -545
  98. package/router/dist/routing/observed-routing.d.ts +23 -14
  99. package/router/dist/routing/observed-routing.js +319 -318
  100. package/router/dist/routing/parsers.d.ts +10 -10
  101. package/router/dist/routing/parsers.js +454 -406
  102. package/router/dist/routing/run-filters.d.ts +45 -41
  103. package/router/dist/routing/run-filters.js +215 -207
  104. package/router/dist/routing/run-reducers.d.ts +45 -26
  105. package/router/dist/routing/run-reducers.js +102 -90
  106. package/router/dist/sandbox/adapter-roots.d.ts +34 -26
  107. package/router/dist/sandbox/adapter-roots.js +43 -41
  108. package/router/dist/sandbox/eryx-python-adapter.d.ts +9 -5
  109. package/router/dist/sandbox/eryx-python-adapter.js +659 -588
  110. package/router/dist/sandbox/jq-wasm-adapter.d.ts +3 -3
  111. package/router/dist/sandbox/jq-wasm-adapter.js +319 -311
  112. package/router/dist/sandbox/quickjs-wasi-adapter.d.ts +9 -5
  113. package/router/dist/sandbox/quickjs-wasi-adapter.js +336 -333
  114. package/router/dist/sandbox/script-sandbox.d.ts +105 -84
  115. package/router/dist/sandbox/script-sandbox.js +294 -260
  116. package/router/dist/setup/script-transform-adapters.d.ts +23 -19
  117. package/router/dist/setup/script-transform-adapters.js +223 -204
  118. package/router/dist/tools/batch.d.ts +27 -16
  119. package/router/dist/tools/batch.js +493 -416
  120. package/router/dist/tools/run.d.ts +47 -35
  121. package/router/dist/tools/run.js +1171 -1086
  122. package/router/dist/tools/search.d.ts +53 -31
  123. package/router/dist/tools/search.js +1224 -1065
  124. package/router/dist/transform/engine.d.ts +121 -96
  125. package/router/dist/transform/engine.js +1853 -1736
  126. package/router/dist/vault/vault-index.d.ts +97 -78
  127. package/router/dist/vault/vault-index.js +347 -342
  128. package/router/dist/vault/vault.d.ts +109 -74
  129. package/router/dist/vault/vault.js +528 -533
  130. package/runtime/interaction-contract.md +16 -0
  131. package/skills/bypass/SKILL.md +52 -38
  132. package/skills/commit-work/SKILL.md +54 -46
  133. package/skills/commit-work/references/staging-decisions.md +46 -24
  134. package/skills/decision-gate/SKILL.md +48 -0
  135. package/skills/design-for-depth/SKILL.md +75 -59
  136. package/skills/design-for-depth/references/design-pressure-signals.md +56 -7
  137. package/skills/design-for-depth/references/interface-design-loop.md +156 -0
  138. package/skills/design-for-depth/references/software-design-philosophy.md +32 -3
  139. package/skills/diagnose-failure/SKILL.md +65 -60
  140. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +36 -0
  141. package/skills/diagnose-failure/references/flaky-and-performance.md +30 -18
  142. package/skills/discuss/SKILL.md +121 -0
  143. package/skills/discuss/references/checkpoints.md +66 -0
  144. package/skills/evaluate-skill/SKILL.md +62 -53
  145. package/skills/evaluate-skill/references/definition-schema.md +345 -0
  146. package/skills/evaluate-skill/references/evaluation-design.md +86 -0
  147. package/skills/evaluate-skill/references/execution-and-evidence.md +85 -0
  148. package/skills/evaluate-skill/references/review-and-revision.md +51 -0
  149. package/skills/evaluate-skill/scripts/lib/definitions.mjs +296 -0
  150. package/skills/evaluate-skill/scripts/lib/evidence.mjs +46 -0
  151. package/skills/evaluate-skill/scripts/lib/grade-comparison.mjs +60 -0
  152. package/skills/evaluate-skill/scripts/lib/grade-interaction.mjs +148 -0
  153. package/skills/evaluate-skill/scripts/lib/grade-support.mjs +126 -0
  154. package/skills/evaluate-skill/scripts/lib/grade-workspace.mjs +192 -0
  155. package/skills/evaluate-skill/scripts/lib/grade.mjs +56 -0
  156. package/skills/evaluate-skill/scripts/lib/pi.mjs +489 -0
  157. package/skills/evaluate-skill/scripts/lib/process.mjs +79 -0
  158. package/skills/evaluate-skill/scripts/lib/rpc.mjs +444 -0
  159. package/skills/evaluate-skill/scripts/lib/runner.mjs +346 -0
  160. package/skills/evaluate-skill/scripts/lib/sandbox.mjs +409 -0
  161. package/skills/evaluate-skill/scripts/lib/view.mjs +337 -0
  162. package/skills/evaluate-skill/scripts/pi-guard.mjs +164 -0
  163. package/skills/evaluate-skill/scripts/skill-eval.mjs +101 -0
  164. package/skills/execute-work/SKILL.md +99 -0
  165. package/skills/execute-work/references/code-practices.md +78 -0
  166. package/skills/execute-work/references/execution-loop.md +77 -0
  167. package/skills/finish-branch/SKILL.md +117 -0
  168. package/skills/finish-branch/references/integration-options.md +80 -0
  169. package/skills/handoff/SKILL.md +65 -39
  170. package/skills/handoff/references/templates.md +104 -27
  171. package/skills/launch-work/SKILL.md +116 -0
  172. package/skills/launch-work/references/launch-readiness.md +85 -0
  173. package/skills/migration-work/SKILL.md +115 -0
  174. package/skills/migration-work/references/migration-lifecycle.md +82 -0
  175. package/skills/mode-contract/SKILL.md +50 -52
  176. package/skills/output-router/SKILL.md +21 -17
  177. package/skills/output-router/references/safety-policy.md +1 -1
  178. package/skills/release-work/SKILL.md +118 -0
  179. package/skills/release-work/references/release-evidence.md +78 -0
  180. package/skills/review-artifact/SKILL.md +77 -69
  181. package/skills/review-artifact/references/reviewer-prompt.md +133 -42
  182. package/skills/review-work/SKILL.md +82 -57
  183. package/skills/review-work/references/reviewer-prompt.md +97 -60
  184. package/skills/review-work/references/security-risk-lens.md +67 -0
  185. package/skills/setup-freeflow/SKILL.md +58 -66
  186. package/skills/setup-freeflow/references/activation-contract.md +89 -69
  187. package/skills/setup-freeflow/references/host-setup.md +54 -17
  188. package/skills/setup-freeflow/references/output-router-setup.md +62 -54
  189. package/skills/simplify-code/SKILL.md +80 -0
  190. package/skills/simplify-code/references/simplification-patterns.md +70 -0
  191. package/skills/tdd/SKILL.md +96 -0
  192. package/skills/tdd/references/test-design.md +96 -0
  193. package/skills/track-work/SKILL.md +84 -0
  194. package/skills/track-work/references/working-record-schema.md +378 -0
  195. package/skills/verify-work/SKILL.md +59 -38
  196. package/skills/verify-work/references/browser-runtime-evidence.md +55 -0
  197. package/skills/verify-work/references/integration-evidence.md +51 -0
  198. package/skills/verify-work/references/performance-evidence.md +57 -0
  199. package/skills/workflow/SKILL.md +53 -59
  200. package/skills/workflow/references/domain-skill-composition.md +57 -0
  201. package/skills/workflow/references/workflow-loop.md +92 -0
  202. package/skills/write-plan/SKILL.md +85 -58
  203. package/skills/write-plan/references/plan-shapes.md +132 -46
  204. package/skills/write-skill/SKILL.md +82 -51
  205. package/skills/write-skill/references/activation-boundaries.md +62 -0
  206. package/skills/write-skill/references/agent-first-instructions.md +87 -0
  207. package/skills/write-skill/references/progressive-disclosure.md +63 -0
  208. package/skills/write-skill/scripts/lib/skill-author.mjs +653 -0
  209. package/skills/write-skill/scripts/skill-author.mjs +104 -0
  210. package/skills/write-spec/SKILL.md +65 -54
  211. package/skills/write-spec/references/artifact-standards.md +60 -41
  212. package/skills/write-spec/references/decision-records.md +77 -0
  213. package/skills/write-spec/references/spec-shapes.md +119 -60
  214. package/evals/README.md +0 -32
  215. package/evals/fixtures/copy-conventions.md +0 -31
  216. package/evals/fixtures/output-router/README.md +0 -12
  217. package/evals/fixtures/output-router/failed-command-output.txt +0 -9
  218. package/evals/fixtures/output-router/large-router-manual.md +0 -63
  219. package/evals/fixtures/output-router/native-large-output.txt +0 -40
  220. package/evals/fixtures/output-router/noisy-test-output.txt +0 -30
  221. package/evals/fixtures/output-router/verification-output.txt +0 -8
  222. package/evals/fixtures/tiny-artifact-review-app/README.md +0 -5
  223. package/evals/fixtures/tiny-artifact-review-app/docs/billing-policy.md +0 -10
  224. package/evals/fixtures/tiny-artifact-review-app/docs/specs/billing-webhook-api-draft.md +0 -21
  225. package/evals/fixtures/tiny-artifact-review-app/docs/specs/failed-payment-grace-period.md +0 -24
  226. package/evals/fixtures/tiny-artifact-review-app/docs/specs/immediate-downgrade.md +0 -17
  227. package/evals/fixtures/tiny-artifact-review-app/tests/billing.test.md +0 -8
  228. package/evals/fixtures/tiny-commit-ambiguous-app/.freeflow-eval-setup.sh +0 -15
  229. package/evals/fixtures/tiny-commit-ambiguous-app/README.md +0 -3
  230. package/evals/fixtures/tiny-commit-ambiguous-app/src/billing.ts +0 -5
  231. package/evals/fixtures/tiny-commit-ambiguous-app/src/settings-page.ts +0 -1
  232. package/evals/fixtures/tiny-commit-ambiguous-app/tests/settings.test.md +0 -5
  233. package/evals/fixtures/tiny-commit-app/.freeflow-eval-setup.sh +0 -14
  234. package/evals/fixtures/tiny-commit-app/README.md +0 -3
  235. package/evals/fixtures/tiny-commit-app/docs/specs/settings-title.md +0 -5
  236. package/evals/fixtures/tiny-commit-app/src/billing.ts +0 -5
  237. package/evals/fixtures/tiny-commit-app/src/settings-page.ts +0 -1
  238. package/evals/fixtures/tiny-commit-app/tests/settings.test.md +0 -5
  239. package/evals/fixtures/tiny-commit-everything-app/.freeflow-eval-setup.sh +0 -15
  240. package/evals/fixtures/tiny-commit-everything-app/README.md +0 -3
  241. package/evals/fixtures/tiny-commit-everything-app/docs/specs/settings-title.md +0 -5
  242. package/evals/fixtures/tiny-commit-everything-app/src/billing.ts +0 -5
  243. package/evals/fixtures/tiny-commit-everything-app/src/settings-page.ts +0 -1
  244. package/evals/fixtures/tiny-commit-everything-app/tests/settings.test.md +0 -5
  245. package/evals/fixtures/tiny-commit-staged-app/.freeflow-eval-setup.sh +0 -15
  246. package/evals/fixtures/tiny-commit-staged-app/README.md +0 -3
  247. package/evals/fixtures/tiny-commit-staged-app/docs/specs/settings-title.md +0 -5
  248. package/evals/fixtures/tiny-commit-staged-app/src/billing.ts +0 -5
  249. package/evals/fixtures/tiny-commit-staged-app/src/settings-page.ts +0 -1
  250. package/evals/fixtures/tiny-commit-staged-app/tests/settings.test.md +0 -5
  251. package/evals/fixtures/tiny-commit-staged-mixed-app/.freeflow-eval-setup.sh +0 -15
  252. package/evals/fixtures/tiny-commit-staged-mixed-app/README.md +0 -3
  253. package/evals/fixtures/tiny-commit-staged-mixed-app/docs/billing-policy.md +0 -3
  254. package/evals/fixtures/tiny-commit-staged-mixed-app/docs/release-notes.md +0 -3
  255. package/evals/fixtures/tiny-commit-staged-mixed-app/src/billing.ts +0 -5
  256. package/evals/fixtures/tiny-dashboard-stale-app/README.md +0 -11
  257. package/evals/fixtures/tiny-dashboard-stale-app/src/api-client.ts +0 -13
  258. package/evals/fixtures/tiny-dashboard-stale-app/src/dashboard-data.ts +0 -19
  259. package/evals/fixtures/tiny-dashboard-stale-app/tests/dashboard-data.test.md +0 -9
  260. package/evals/fixtures/tiny-decision-destination-app/README.md +0 -6
  261. package/evals/fixtures/tiny-decision-destination-app/src/risk.ts +0 -8
  262. package/evals/fixtures/tiny-decision-destination-app/tests/risk.test.md +0 -4
  263. package/evals/fixtures/tiny-design-depth-app/README.md +0 -15
  264. package/evals/fixtures/tiny-design-depth-app/src/routes.ts +0 -25
  265. package/evals/fixtures/tiny-design-depth-app/src/sendEmail.ts +0 -3
  266. package/evals/fixtures/tiny-design-depth-app/src/sendSms.ts +0 -3
  267. package/evals/fixtures/tiny-design-depth-app/src/telemetry.ts +0 -3
  268. package/evals/fixtures/tiny-design-depth-app/tests/routes.test.md +0 -12
  269. package/evals/fixtures/tiny-eval-method-app/README.md +0 -8
  270. package/evals/fixtures/tiny-eval-method-app/evals/reports/review-pr-eval-result.md +0 -5
  271. package/evals/fixtures/tiny-eval-method-app/evals/reports/triage-question-failure.md +0 -17
  272. package/evals/fixtures/tiny-eval-method-app/evals/runs/review-pr-with-skill-output.diff +0 -11
  273. package/evals/fixtures/tiny-eval-method-app/evals/runs/review-pr-with-skill-output.md +0 -3
  274. package/evals/fixtures/tiny-eval-method-app/skills/review-pr/SKILL.md +0 -10
  275. package/evals/fixtures/tiny-eval-method-app/skills/triage-question/SKILL.md +0 -10
  276. package/evals/fixtures/tiny-execute-plan-app/README.md +0 -5
  277. package/evals/fixtures/tiny-execute-plan-app/checks/settings-and-billing-check.sh +0 -5
  278. package/evals/fixtures/tiny-execute-plan-app/docs/billing-policy.md +0 -10
  279. package/evals/fixtures/tiny-execute-plan-app/docs/plans/immediate-downgrade-plan.md +0 -11
  280. package/evals/fixtures/tiny-execute-plan-app/docs/plans/settings-title-plan.md +0 -12
  281. package/evals/fixtures/tiny-execute-plan-app/docs/plans/settings-title-with-bad-check.md +0 -15
  282. package/evals/fixtures/tiny-execute-plan-app/docs/plans/unverified-billing-plan.md +0 -9
  283. package/evals/fixtures/tiny-execute-plan-app/docs/specs/failed-payment-grace-period.md +0 -9
  284. package/evals/fixtures/tiny-execute-plan-app/docs/specs/settings-title.md +0 -9
  285. package/evals/fixtures/tiny-execute-plan-app/src/billing.ts +0 -7
  286. package/evals/fixtures/tiny-execute-plan-app/src/settings-page.ts +0 -1
  287. package/evals/fixtures/tiny-execute-plan-app/tests/billing.test.md +0 -7
  288. package/evals/fixtures/tiny-execute-plan-app/tests/settings.test.md +0 -5
  289. package/evals/fixtures/tiny-execute-review-loop-app/README.md +0 -5
  290. package/evals/fixtures/tiny-execute-review-loop-app/docs/billing-policy.md +0 -9
  291. package/evals/fixtures/tiny-execute-review-loop-app/docs/plans/settings-title-plan.md +0 -12
  292. package/evals/fixtures/tiny-execute-review-loop-app/docs/review/previous-agent-summary.md +0 -15
  293. package/evals/fixtures/tiny-execute-review-loop-app/docs/review/review-pass-1-non-pass.md +0 -11
  294. package/evals/fixtures/tiny-execute-review-loop-app/docs/specs/settings-title.md +0 -9
  295. package/evals/fixtures/tiny-execute-review-loop-app/src/billing.ts +0 -7
  296. package/evals/fixtures/tiny-execute-review-loop-app/src/settings-page.ts +0 -1
  297. package/evals/fixtures/tiny-execute-review-loop-app/tests/billing.test.md +0 -7
  298. package/evals/fixtures/tiny-execute-review-loop-app/tests/settings.test.md +0 -5
  299. package/evals/fixtures/tiny-flaky-session-app/README.md +0 -10
  300. package/evals/fixtures/tiny-flaky-session-app/src/session-timeout.ts +0 -13
  301. package/evals/fixtures/tiny-flaky-session-app/tests/session-timeout.test.md +0 -8
  302. package/evals/fixtures/tiny-handoff-app/README.md +0 -5
  303. package/evals/fixtures/tiny-handoff-app/docs/billing-policy.md +0 -10
  304. package/evals/fixtures/tiny-handoff-app/docs/handoffs/old-agent-handoff.md +0 -8
  305. package/evals/fixtures/tiny-handoff-app/docs/plans/billing-retry-plan.md +0 -11
  306. package/evals/fixtures/tiny-handoff-app/src/billing.ts +0 -7
  307. package/evals/fixtures/tiny-handoff-report-app/README.md +0 -3
  308. package/evals/fixtures/tiny-handoff-report-app/evals/runs/handoff-3/hof-003/with-skill-output.diff +0 -1
  309. package/evals/fixtures/tiny-handoff-report-app/evals/runs/handoff-3/hof-003/with-skill-output.md +0 -1
  310. package/evals/fixtures/tiny-handoff-report-app/evals/scores/hof-003-score.txt +0 -4
  311. package/evals/fixtures/tiny-plan-app/README.md +0 -5
  312. package/evals/fixtures/tiny-plan-app/docs/billing-policy.md +0 -10
  313. package/evals/fixtures/tiny-plan-app/docs/specs/failed-payment-grace-period.md +0 -25
  314. package/evals/fixtures/tiny-plan-app/src/billing.ts +0 -17
  315. package/evals/fixtures/tiny-plan-app/src/settings-page.ts +0 -1
  316. package/evals/fixtures/tiny-plan-app/tests/billing.test.md +0 -8
  317. package/evals/fixtures/tiny-plan-app/tests/settings.test.md +0 -5
  318. package/evals/fixtures/tiny-plan-strict-api-app/README.md +0 -3
  319. package/evals/fixtures/tiny-plan-strict-api-app/docs/billing-policy.md +0 -5
  320. package/evals/fixtures/tiny-plan-strict-api-app/docs/specs/billing-webhook-api.md +0 -25
  321. package/evals/fixtures/tiny-plan-strict-api-app/src/billing.ts +0 -5
  322. package/evals/fixtures/tiny-plan-strict-api-app/src/webhooks.ts +0 -3
  323. package/evals/fixtures/tiny-plan-strict-api-app/tests/billing-webhook.test.md +0 -8
  324. package/evals/fixtures/tiny-post-setup-invalid-config-app/.freeflow/config.json +0 -3
  325. package/evals/fixtures/tiny-post-setup-invalid-config-app/AGENTS.md +0 -15
  326. package/evals/fixtures/tiny-post-setup-invalid-config-app/README.md +0 -16
  327. package/evals/fixtures/tiny-post-setup-invalid-config-app/docs/auth-review.md +0 -15
  328. package/evals/fixtures/tiny-post-setup-invalid-config-app/docs/billing-policy.md +0 -10
  329. package/evals/fixtures/tiny-post-setup-invalid-config-app/docs/copy-conventions.md +0 -15
  330. package/evals/fixtures/tiny-post-setup-invalid-config-app/src/auth-middleware.ts +0 -12
  331. package/evals/fixtures/tiny-post-setup-invalid-config-app/src/billing.ts +0 -20
  332. package/evals/fixtures/tiny-post-setup-invalid-config-app/src/login-copy.ts +0 -1
  333. package/evals/fixtures/tiny-post-setup-invalid-config-app/src/settings-page.ts +0 -1
  334. package/evals/fixtures/tiny-post-setup-invalid-config-app/tests/auth-middleware.test.md +0 -8
  335. package/evals/fixtures/tiny-post-setup-invalid-config-app/tests/billing.test.md +0 -8
  336. package/evals/fixtures/tiny-post-setup-invalid-config-app/tests/copy.test.md +0 -7
  337. package/evals/fixtures/tiny-post-setup-missing-config-app/AGENTS.md +0 -15
  338. package/evals/fixtures/tiny-post-setup-missing-config-app/README.md +0 -16
  339. package/evals/fixtures/tiny-post-setup-missing-config-app/docs/auth-review.md +0 -15
  340. package/evals/fixtures/tiny-post-setup-missing-config-app/docs/billing-policy.md +0 -10
  341. package/evals/fixtures/tiny-post-setup-missing-config-app/docs/copy-conventions.md +0 -15
  342. package/evals/fixtures/tiny-post-setup-missing-config-app/src/auth-middleware.ts +0 -12
  343. package/evals/fixtures/tiny-post-setup-missing-config-app/src/billing.ts +0 -20
  344. package/evals/fixtures/tiny-post-setup-missing-config-app/src/login-copy.ts +0 -1
  345. package/evals/fixtures/tiny-post-setup-missing-config-app/src/settings-page.ts +0 -1
  346. package/evals/fixtures/tiny-post-setup-missing-config-app/tests/auth-middleware.test.md +0 -8
  347. package/evals/fixtures/tiny-post-setup-missing-config-app/tests/billing.test.md +0 -8
  348. package/evals/fixtures/tiny-post-setup-missing-config-app/tests/copy.test.md +0 -7
  349. package/evals/fixtures/tiny-post-setup-source-conflict-app/.freeflow/config.json +0 -3
  350. package/evals/fixtures/tiny-post-setup-source-conflict-app/AGENTS.md +0 -15
  351. package/evals/fixtures/tiny-post-setup-source-conflict-app/README.md +0 -16
  352. package/evals/fixtures/tiny-post-setup-source-conflict-app/docs/auth-review.md +0 -15
  353. package/evals/fixtures/tiny-post-setup-source-conflict-app/docs/billing-policy.md +0 -10
  354. package/evals/fixtures/tiny-post-setup-source-conflict-app/docs/copy-conventions.md +0 -15
  355. package/evals/fixtures/tiny-post-setup-source-conflict-app/src/auth-middleware.ts +0 -12
  356. package/evals/fixtures/tiny-post-setup-source-conflict-app/src/billing.ts +0 -20
  357. package/evals/fixtures/tiny-post-setup-source-conflict-app/src/login-copy.ts +0 -1
  358. package/evals/fixtures/tiny-post-setup-source-conflict-app/src/settings-page.ts +0 -1
  359. package/evals/fixtures/tiny-post-setup-source-conflict-app/tests/auth-middleware.test.md +0 -8
  360. package/evals/fixtures/tiny-post-setup-source-conflict-app/tests/billing.test.md +0 -8
  361. package/evals/fixtures/tiny-post-setup-source-conflict-app/tests/copy.test.md +0 -7
  362. package/evals/fixtures/tiny-report-performance-app/README.md +0 -11
  363. package/evals/fixtures/tiny-report-performance-app/docs/performance.md +0 -10
  364. package/evals/fixtures/tiny-report-performance-app/src/report-summary.ts +0 -29
  365. package/evals/fixtures/tiny-report-performance-app/tests/report-performance.test.md +0 -8
  366. package/evals/fixtures/tiny-review-request-app/README.md +0 -3
  367. package/evals/fixtures/tiny-review-request-app/docs/billing-policy.md +0 -5
  368. package/evals/fixtures/tiny-review-request-app/docs/plans/billing-webhook-api.md +0 -19
  369. package/evals/fixtures/tiny-review-request-app/docs/review/previous-agent-summary.md +0 -20
  370. package/evals/fixtures/tiny-review-request-app/docs/specs/billing-webhook-api.md +0 -25
  371. package/evals/fixtures/tiny-review-request-app/src/billing.ts +0 -11
  372. package/evals/fixtures/tiny-review-request-app/src/webhooks.ts +0 -19
  373. package/evals/fixtures/tiny-review-request-app/tests/billing-webhook.test.md +0 -8
  374. package/evals/fixtures/tiny-review-work-app/README.md +0 -5
  375. package/evals/fixtures/tiny-review-work-app/docs/billing-policy.md +0 -9
  376. package/evals/fixtures/tiny-review-work-app/docs/plans/settings-title-plan.md +0 -12
  377. package/evals/fixtures/tiny-review-work-app/docs/review/previous-agent-summary.md +0 -15
  378. package/evals/fixtures/tiny-review-work-app/docs/specs/settings-title.md +0 -9
  379. package/evals/fixtures/tiny-review-work-app/src/billing.ts +0 -7
  380. package/evals/fixtures/tiny-review-work-app/src/settings-page.ts +0 -1
  381. package/evals/fixtures/tiny-review-work-app/tests/billing.test.md +0 -7
  382. package/evals/fixtures/tiny-review-work-app/tests/settings.test.md +0 -5
  383. package/evals/fixtures/tiny-saas-app/README.md +0 -16
  384. package/evals/fixtures/tiny-saas-app/docs/auth-review.md +0 -15
  385. package/evals/fixtures/tiny-saas-app/docs/billing-policy.md +0 -10
  386. package/evals/fixtures/tiny-saas-app/docs/copy-conventions.md +0 -15
  387. package/evals/fixtures/tiny-saas-app/docs/specs/settings-title.md +0 -13
  388. package/evals/fixtures/tiny-saas-app/src/auth-middleware.ts +0 -12
  389. package/evals/fixtures/tiny-saas-app/src/billing.ts +0 -20
  390. package/evals/fixtures/tiny-saas-app/src/login-copy.ts +0 -1
  391. package/evals/fixtures/tiny-saas-app/src/settings-page.ts +0 -1
  392. package/evals/fixtures/tiny-saas-app/tests/auth-middleware.test.md +0 -8
  393. package/evals/fixtures/tiny-saas-app/tests/billing.test.md +0 -8
  394. package/evals/fixtures/tiny-saas-app/tests/copy.test.md +0 -7
  395. package/evals/fixtures/tiny-setup-agents-app/AGENTS.md +0 -6
  396. package/evals/fixtures/tiny-setup-both-app/AGENTS.md +0 -5
  397. package/evals/fixtures/tiny-setup-both-app/CLAUDE.md +0 -5
  398. package/evals/fixtures/tiny-setup-claude-app/CLAUDE.md +0 -6
  399. package/evals/fixtures/tiny-setup-codex-rules-app/.codex/rules/shell.rules +0 -5
  400. package/evals/fixtures/tiny-setup-codex-rules-app/AGENTS.md +0 -6
  401. package/evals/fixtures/tiny-setup-conflict-app/AGENTS.md +0 -6
  402. package/evals/fixtures/tiny-setup-existing-block-app/AGENTS.md +0 -14
  403. package/evals/fixtures/tiny-setup-four-rules-app/CLAUDE.md +0 -6
  404. package/evals/fixtures/tiny-skill-dev-app/README.md +0 -10
  405. package/evals/fixtures/tiny-skill-dev-app/evals/prompts/review-pr-001.txt +0 -8
  406. package/evals/fixtures/tiny-skill-dev-app/evals/reports/review-pr-failure.md +0 -17
  407. package/evals/fixtures/tiny-skill-dev-app/skills/review-pr/SKILL.md +0 -13
  408. package/evals/fixtures/tiny-team-app/README.md +0 -5
  409. package/evals/fixtures/tiny-team-app/docs/product.md +0 -6
  410. package/evals/fixtures/tiny-team-app/src/teams.ts +0 -9
  411. package/evals/fixtures/tiny-team-app/src/users.ts +0 -5
  412. package/evals/fixtures/tiny-team-app/tests/teams.test.md +0 -7
  413. package/evals/fixtures/tiny-verify-app/README.md +0 -5
  414. package/evals/fixtures/tiny-verify-app/src/settings-nav.ts +0 -1
  415. package/evals/fixtures/tiny-verify-app/src/settings-page.ts +0 -1
  416. package/evals/fixtures/tiny-verify-app/tests/settings.test.md +0 -7
  417. package/evals/prompts/aon-001.txt +0 -2
  418. package/evals/prompts/byp-001.txt +0 -2
  419. package/evals/prompts/byp-002.txt +0 -2
  420. package/evals/prompts/cap-001.txt +0 -3
  421. package/evals/prompts/cap-002.txt +0 -3
  422. package/evals/prompts/cmd-001.txt +0 -3
  423. package/evals/prompts/cmd-002.txt +0 -3
  424. package/evals/prompts/cmd-003.txt +0 -6
  425. package/evals/prompts/cmd-004.txt +0 -5
  426. package/evals/prompts/cmd-005.txt +0 -5
  427. package/evals/prompts/cmd-006.txt +0 -5
  428. package/evals/prompts/cmd-007.txt +0 -5
  429. package/evals/prompts/cmd-008.txt +0 -5
  430. package/evals/prompts/cmd-009.txt +0 -8
  431. package/evals/prompts/cmd-010.txt +0 -5
  432. package/evals/prompts/cmd-011.txt +0 -5
  433. package/evals/prompts/cmd-012.txt +0 -5
  434. package/evals/prompts/cmd-013.txt +0 -5
  435. package/evals/prompts/cmd-014.txt +0 -7
  436. package/evals/prompts/cmd-015.txt +0 -7
  437. package/evals/prompts/cmd-016.txt +0 -5
  438. package/evals/prompts/cmt-001.txt +0 -1
  439. package/evals/prompts/cmt-002.txt +0 -1
  440. package/evals/prompts/cmt-003.txt +0 -1
  441. package/evals/prompts/cmt-004.txt +0 -1
  442. package/evals/prompts/cmt-005.txt +0 -1
  443. package/evals/prompts/dfd-001.txt +0 -3
  444. package/evals/prompts/dia-001.txt +0 -3
  445. package/evals/prompts/dia-002.txt +0 -3
  446. package/evals/prompts/dia-003.txt +0 -3
  447. package/evals/prompts/dis-001.txt +0 -5
  448. package/evals/prompts/dis-002.txt +0 -5
  449. package/evals/prompts/esk-001.txt +0 -5
  450. package/evals/prompts/esk-002.txt +0 -5
  451. package/evals/prompts/esk-003.txt +0 -5
  452. package/evals/prompts/grc-001.txt +0 -3
  453. package/evals/prompts/hof-001.txt +0 -3
  454. package/evals/prompts/hof-002.txt +0 -1
  455. package/evals/prompts/hof-003.txt +0 -1
  456. package/evals/prompts/hof-004.txt +0 -1
  457. package/evals/prompts/hof-005.txt +0 -1
  458. package/evals/prompts/ivg-001.txt +0 -1
  459. package/evals/prompts/ivg-002.txt +0 -11
  460. package/evals/prompts/ivg-003.txt +0 -1
  461. package/evals/prompts/ivg-004.txt +0 -1
  462. package/evals/prompts/mode-001.txt +0 -3
  463. package/evals/prompts/mode-002.txt +0 -1
  464. package/evals/prompts/mode-003.txt +0 -3
  465. package/evals/prompts/mode-004.txt +0 -3
  466. package/evals/prompts/mode-005.txt +0 -3
  467. package/evals/prompts/mode-006.txt +0 -3
  468. package/evals/prompts/mode-007.txt +0 -3
  469. package/evals/prompts/otr-001.txt +0 -3
  470. package/evals/prompts/otr-002.txt +0 -3
  471. package/evals/prompts/rar-001.txt +0 -5
  472. package/evals/prompts/rar-002.txt +0 -3
  473. package/evals/prompts/rar-003.txt +0 -6
  474. package/evals/prompts/rar-004.txt +0 -15
  475. package/evals/prompts/rar-005.txt +0 -10
  476. package/evals/prompts/rbr-001.txt +0 -3
  477. package/evals/prompts/rbr-002.txt +0 -3
  478. package/evals/prompts/rbr-003.txt +0 -7
  479. package/evals/prompts/rev-001.txt +0 -3
  480. package/evals/prompts/rev-002.txt +0 -5
  481. package/evals/prompts/rev-003.txt +0 -6
  482. package/evals/prompts/rev-004.txt +0 -1
  483. package/evals/prompts/rev-005.txt +0 -15
  484. package/evals/prompts/rev-006.txt +0 -5
  485. package/evals/prompts/stp-001.txt +0 -1
  486. package/evals/prompts/stp-002.txt +0 -1
  487. package/evals/prompts/stp-003.txt +0 -1
  488. package/evals/prompts/stp-004.txt +0 -1
  489. package/evals/prompts/stp-005.txt +0 -1
  490. package/evals/prompts/stp-006.txt +0 -1
  491. package/evals/prompts/stp-007.txt +0 -1
  492. package/evals/prompts/stp-008.txt +0 -1
  493. package/evals/prompts/stp-009.txt +0 -1
  494. package/evals/prompts/stp-010.txt +0 -1
  495. package/evals/prompts/stp-011.txt +0 -1
  496. package/evals/prompts/vfy-003.txt +0 -1
  497. package/evals/prompts/wpl-001.txt +0 -3
  498. package/evals/prompts/wpl-002.txt +0 -5
  499. package/evals/prompts/wpl-003.txt +0 -3
  500. package/evals/prompts/wpl-004.txt +0 -5
  501. package/evals/prompts/wpl-005.txt +0 -1
  502. package/evals/prompts/wrc-001.txt +0 -10
  503. package/evals/prompts/wrc-002.txt +0 -3
  504. package/evals/prompts/wsk-001.txt +0 -6
  505. package/evals/prompts/wsp-001.txt +0 -5
  506. package/evals/prompts/wsp-002.txt +0 -15
  507. package/evals/prompts/wsp-003.txt +0 -3
  508. package/evals/prompts/wsp-004.txt +0 -15
  509. package/evals/prompts/wsp-005.txt +0 -5
  510. package/evals/prompts/wsp-006.txt +0 -12
  511. package/evals/prompts/wsp-007.txt +0 -9
  512. package/evals/prompts/wsp-008.txt +0 -13
  513. package/evals/prompts/xpl-001.txt +0 -1
  514. package/evals/prompts/xpl-002.txt +0 -3
  515. package/evals/prompts/xpl-003.txt +0 -3
  516. package/evals/prompts/xpl-004.txt +0 -3
  517. package/evals/prompts/xpl-005.txt +0 -5
  518. package/evals/prompts/xpl-006.txt +0 -3
  519. package/evals/registries/adversarial-evals.json +0 -39
  520. package/evals/registries/fixture-evals.json +0 -2513
  521. package/evals/registries/handoff-evals.json +0 -74
  522. package/evals/registries/legacy-evals.json +0 -147
  523. package/evals/registries/skill-evidence.json +0 -193
  524. package/evals/registries/verify-work-evals.json +0 -46
  525. package/evals/reports/acceptance/v0.1-acceptance-report.md +0 -130
  526. package/evals/reports/by-command-surface/command-surface-1-report.md +0 -45
  527. package/evals/reports/by-command-surface/command-surface-10-report.md +0 -76
  528. package/evals/reports/by-command-surface/command-surface-11-report.md +0 -77
  529. package/evals/reports/by-command-surface/command-surface-12-report.md +0 -91
  530. package/evals/reports/by-command-surface/command-surface-13-report.md +0 -64
  531. package/evals/reports/by-command-surface/command-surface-14-report.md +0 -66
  532. package/evals/reports/by-command-surface/command-surface-15-report.md +0 -124
  533. package/evals/reports/by-command-surface/command-surface-16-report.md +0 -39
  534. package/evals/reports/by-command-surface/command-surface-2-report.md +0 -80
  535. package/evals/reports/by-command-surface/command-surface-3-report.md +0 -86
  536. package/evals/reports/by-command-surface/command-surface-4-report.md +0 -76
  537. package/evals/reports/by-command-surface/command-surface-5-report.md +0 -80
  538. package/evals/reports/by-command-surface/command-surface-6-report.md +0 -74
  539. package/evals/reports/by-command-surface/command-surface-7-report.md +0 -84
  540. package/evals/reports/by-command-surface/command-surface-8-report.md +0 -79
  541. package/evals/reports/by-command-surface/command-surface-9-report.md +0 -79
  542. package/evals/reports/by-command-surface/command-surface-matrix.md +0 -91
  543. package/evals/reports/by-skill/bypass-1-report.md +0 -124
  544. package/evals/reports/by-skill/capture-decisions-1-report.md +0 -99
  545. package/evals/reports/by-skill/capture-decisions-2-report.md +0 -102
  546. package/evals/reports/by-skill/commit-work-1-report.md +0 -165
  547. package/evals/reports/by-skill/commit-work-2-report.md +0 -109
  548. package/evals/reports/by-skill/design-for-depth-1-report.md +0 -95
  549. package/evals/reports/by-skill/diagnose-failure-1-report.md +0 -91
  550. package/evals/reports/by-skill/diagnose-failure-2-report.md +0 -94
  551. package/evals/reports/by-skill/discover-1-report.md +0 -118
  552. package/evals/reports/by-skill/evaluate-skill-2-report.md +0 -71
  553. package/evals/reports/by-skill/execute-plan-1-report.md +0 -103
  554. package/evals/reports/by-skill/execute-plan-2-report.md +0 -112
  555. package/evals/reports/by-skill/execute-plan-3-report.md +0 -87
  556. package/evals/reports/by-skill/grill-context-1-report.md +0 -103
  557. package/evals/reports/by-skill/handoff-1-report.md +0 -123
  558. package/evals/reports/by-skill/handoff-2-report.md +0 -105
  559. package/evals/reports/by-skill/handoff-3-report.md +0 -100
  560. package/evals/reports/by-skill/handoff-7-report.md +0 -77
  561. package/evals/reports/by-skill/interview-gate-1-report.md +0 -63
  562. package/evals/reports/by-skill/interview-gate-2-report.md +0 -105
  563. package/evals/reports/by-skill/mode-contract-1-report.md +0 -74
  564. package/evals/reports/by-skill/mode-contract-2-report.md +0 -87
  565. package/evals/reports/by-skill/mode-contract-3-report.md +0 -65
  566. package/evals/reports/by-skill/mode-contract-4-report.md +0 -71
  567. package/evals/reports/by-skill/mode-contract-5-report.md +0 -70
  568. package/evals/reports/by-skill/output-router-1-report.md +0 -76
  569. package/evals/reports/by-skill/output-router-2-report.md +0 -65
  570. package/evals/reports/by-skill/research-1-report.md +0 -144
  571. package/evals/reports/by-skill/research-brief-1-report.md +0 -94
  572. package/evals/reports/by-skill/research-brief-2-report.md +0 -75
  573. package/evals/reports/by-skill/review-artifact-1-report.md +0 -91
  574. package/evals/reports/by-skill/review-artifact-2-report.md +0 -79
  575. package/evals/reports/by-skill/review-artifact-3-report.md +0 -120
  576. package/evals/reports/by-skill/review-artifact-4-report.md +0 -89
  577. package/evals/reports/by-skill/review-artifact-5-report.md +0 -66
  578. package/evals/reports/by-skill/review-work-1-report.md +0 -85
  579. package/evals/reports/by-skill/review-work-2-report.md +0 -87
  580. package/evals/reports/by-skill/review-work-3-report.md +0 -104
  581. package/evals/reports/by-skill/review-work-4-report.md +0 -108
  582. package/evals/reports/by-skill/review-work-5-report.md +0 -73
  583. package/evals/reports/by-skill/review-work-6-report.md +0 -66
  584. package/evals/reports/by-skill/setup-freeflow-1-report.md +0 -199
  585. package/evals/reports/by-skill/setup-freeflow-2-report.md +0 -105
  586. package/evals/reports/by-skill/setup-freeflow-3-report.md +0 -105
  587. package/evals/reports/by-skill/setup-freeflow-4-report.md +0 -88
  588. package/evals/reports/by-skill/setup-freeflow-5-report.md +0 -115
  589. package/evals/reports/by-skill/skill-authoring-1-report.md +0 -81
  590. package/evals/reports/by-skill/verify-work-1-report.md +0 -111
  591. package/evals/reports/by-skill/verify-work-2-report.md +0 -120
  592. package/evals/reports/by-skill/workflow-1-report.md +0 -65
  593. package/evals/reports/by-skill/write-plan-1-report.md +0 -123
  594. package/evals/reports/by-skill/write-plan-2-report.md +0 -98
  595. package/evals/reports/by-skill/write-plan-3-report.md +0 -110
  596. package/evals/reports/by-skill/write-spec-1-report.md +0 -88
  597. package/evals/reports/by-skill/write-spec-2-report.md +0 -89
  598. package/evals/reports/by-skill/write-spec-3-report.md +0 -90
  599. package/evals/reports/by-skill/write-spec-4-report.md +0 -167
  600. package/evals/reports/harness/claude-harness-1-report.md +0 -103
  601. package/evals/reports/harness/eval-harness-1-report.md +0 -91
  602. package/evals/reports/iterations/iteration-1-report.md +0 -71
  603. package/evals/reports/iterations/iteration-2-report.md +0 -78
  604. package/evals/reports/iterations/iteration-3-report.md +0 -118
  605. package/evals/reports/iterations/iteration-4-report.md +0 -131
  606. package/evals/reports/iterations/iteration-5-report.md +0 -149
  607. package/evals/reports/iterations/iteration-6-report.md +0 -136
  608. package/evals/reports/runtime/always-on-runtime-1-report.md +0 -107
  609. package/evals/reports/runtime/context-mode-normalized-benchmark-1-report.md +0 -52
  610. package/evals/reports/runtime/context-mode-real-deep-baseline-1-report.md +0 -410
  611. package/evals/reports/runtime/context-mode-real-deep-final-slice-11-report.md +0 -203
  612. package/evals/reports/runtime/context-mode-real-deep-final-slice-11-review.md +0 -51
  613. package/evals/reports/runtime/context-saving-native-tools-baseline-1-report.md +0 -99
  614. package/evals/reports/runtime/eryx-python-compatibility-reprobe-1-report.md +0 -166
  615. package/evals/reports/runtime/eryx-python-proof-spike-1-report.md +0 -96
  616. package/evals/reports/runtime/eryx-python-proof-spike-2-report.md +0 -151
  617. package/evals/reports/runtime/eryx-python-script-derive-execution-1-report.md +0 -233
  618. package/evals/reports/runtime/jq-script-derive-execution-1-report.md +0 -215
  619. package/evals/reports/runtime/jq-wasm-proof-spike-1-report.md +0 -67
  620. package/evals/reports/runtime/output-router-benchmark-1-report.md +0 -81
  621. package/evals/reports/runtime/output-router-capture-derive-provider-eval-1-report.md +0 -35
  622. package/evals/reports/runtime/output-router-codex-large-index-sqlite-adhoc-report.md +0 -150
  623. package/evals/reports/runtime/output-router-codex-qa-benchmark-1-report.md +0 -46
  624. package/evals/reports/runtime/output-router-command-benchmark-1-report.md +0 -62
  625. package/evals/reports/runtime/output-router-index-benchmark-1-report.md +0 -64
  626. package/evals/reports/runtime/output-router-regression-1-report.md +0 -81
  627. package/evals/reports/runtime/output-router-transform-eval-1-report.md +0 -28
  628. package/evals/reports/runtime/pi-observed-routing-eval-1-report.md +0 -44
  629. package/evals/reports/runtime/quickjs-script-derive-execution-1-report.md +0 -159
  630. package/evals/reports/runtime/quickjs-wasi-proof-spike-1-report.md +0 -55
  631. package/evals/reports/runtime/render-format-row-protocol-1-report.md +0 -58
  632. package/evals/reports/runtime/script-sandbox-adapter-selection-review-1-report.md +0 -84
  633. package/evals/reports/runtime/script-sandbox-probe-resource-hardening-1-report.md +0 -51
  634. package/evals/reports/runtime/storage-policy-benchmark-1-report.md +0 -176
  635. package/evals/reports/runtime/vault-index-storage-spike-1-report.md +0 -39
  636. package/evals/reports/runtime/workflow-context-hook-1-report.md +0 -82
  637. package/evals/runbooks/adversarial-eval-runbook.md +0 -28
  638. package/evals/runbooks/fixture-eval-runbook.md +0 -137
  639. package/evals/runbooks/handoff-eval-runbook.md +0 -22
  640. package/evals/runbooks/verify-work-eval-runbook.md +0 -27
  641. package/evals/scripts/audit-command-surface.sh +0 -160
  642. package/evals/scripts/check-activation-contract.sh +0 -121
  643. package/evals/scripts/check-runtime-context-hook.sh +0 -183
  644. package/evals/scripts/grade-fixture-eval.sh +0 -446
  645. package/evals/scripts/run-claude-fixture-eval.sh +0 -26
  646. package/evals/scripts/run-codex-fixture-eval.sh +0 -26
  647. package/evals/scripts/run-eryx-python-proof-spike.js +0 -559
  648. package/evals/scripts/run-fixture-eval-by-id.sh +0 -23
  649. package/evals/scripts/run-fixture-eval.sh +0 -464
  650. package/evals/scripts/run-jq-wasm-proof-spike.js +0 -299
  651. package/evals/scripts/run-output-router-transform-eval.js +0 -115
  652. package/evals/scripts/run-pi-observed-routing-eval.js +0 -478
  653. package/evals/scripts/run-quickjs-wasi-proof-spike.js +0 -339
  654. package/evals/scripts/skill-evidence.sh +0 -316
  655. package/evals/scripts/validate-release-metadata.sh +0 -486
  656. package/evals/suites/v0.1-acceptance-suite.md +0 -131
  657. package/pi-extension/dist/host-producer-identification.js +0 -186
  658. package/pi-extension/dist/mcp-config.js +0 -30
  659. package/pi-extension/dist/native-safety-net.js +0 -209
  660. package/pi-extension/dist/observed-tool-routing.js +0 -114
  661. package/pi-extension/dist/renderers.js +0 -835
  662. package/pi-extension/dist/router-tools.js +0 -438
  663. package/pi-extension/dist/runtime-context.js +0 -288
  664. package/pi-extension/dist/schemas.js +0 -254
  665. package/pi-extension/dist/status.js +0 -449
  666. package/pi-extension/dist/utils.js +0 -467
  667. package/pi-extension/src/runtime-context.ts +0 -347
  668. package/pi-extension/src/status.ts +0 -505
  669. package/plugin-docs/README.md +0 -40
  670. package/plugin-docs/adr/0001-three-modes.md +0 -27
  671. package/plugin-docs/adr/0002-evals-before-hooks.md +0 -17
  672. package/plugin-docs/adr/0003-release-boundary.md +0 -21
  673. package/plugin-docs/adr/0004-discover-replaces-shallow-discovery-skills.md +0 -26
  674. package/plugin-docs/adr/README.md +0 -10
  675. package/plugin-docs/architecture.md +0 -89
  676. package/plugin-docs/output-router.md +0 -396
  677. package/plugin-docs/release-evidence.md +0 -77
  678. package/plugin-docs/skills.md +0 -44
  679. package/plugin-docs/workflow.md +0 -57
  680. package/skills/diagnose-failure/references/feedback-loop-catalog.md +0 -25
  681. package/skills/discover/ARTIFACT-DESTINATIONS.md +0 -58
  682. package/skills/discover/CHECKPOINTS.md +0 -74
  683. package/skills/discover/SKILL.md +0 -173
  684. package/skills/evaluate-skill/references/eval-patterns.md +0 -41
  685. package/skills/evaluate-skill/references/grading-priority.md +0 -33
  686. package/skills/execute-plan/SKILL.md +0 -133
  687. package/skills/execute-plan/references/execution-map.md +0 -122
  688. package/skills/interview-gate/SKILL.md +0 -90
  689. package/skills/workflow/references/workflow-map.md +0 -104
  690. /package/pi-extension/src/{mcp-config.ts → integrations/mcp-config.ts} +0 -0
@@ -0,0 +1,1730 @@
1
+ import { execFile } from "node:child_process";
2
+ import { appendFile, mkdir, readFile, rm, writeFile } from "node:fs/promises";
3
+ import { dirname, isAbsolute, join, resolve } from "node:path";
4
+ import { promisify } from "node:util";
5
+
6
+ import {
7
+ DEFAULT_OBSERVED_ROUTING_CONFIG,
8
+ DEFAULT_OUTPUT_ROUTER_ENABLED,
9
+ DEFAULT_POST_TOOL_ROUTING,
10
+ DEFAULT_ROUTER_THRESHOLDS,
11
+ DEFAULT_SCRIPT_TRANSFORM_CONFIG,
12
+ DEFAULT_STORAGE_POLICY,
13
+ DEFAULT_VAULT_RETENTION,
14
+ DEFAULT_VAULT_ROOT,
15
+ normalizeFreeflowConfig,
16
+ } from "../../../router/dist/index.js";
17
+ import {
18
+ handleModeCommand,
19
+ readCapabilityState,
20
+ readFreeflowConfig,
21
+ readFreeflowConfigLayers,
22
+ readFreeflowLocalConfig,
23
+ readModeState,
24
+ resetSessionOverrides,
25
+ setSessionCoreOverride,
26
+ VALID_MODES,
27
+ } from "../runtime/runtime-context.js";
28
+ import {
29
+ PiSettingsComponent,
30
+ type SettingsCommitResult,
31
+ type SettingsEntry,
32
+ type SettingsSessionResult,
33
+ } from "./settings-tui.js";
34
+
35
+ const POST_TOOL_ROUTING_VALUES = ["off", "safety-net", "strict"] as const;
36
+ const STORAGE_POLICY_VALUES = ["hybrid-dedupe", "store-everything"] as const;
37
+ const OBSERVED_PERSISTENCE_VALUES = ["none", "metadata-only", "exact"] as const;
38
+ const SCRIPT_LANGUAGES = ["javascript", "python", "jq"] as const;
39
+ const DEFAULT_FREEFLOW_ENABLED = true;
40
+ const DEFAULT_INTERACTION_CONTRACT_ENABLED = true;
41
+ const DEFAULT_SKILLS_ENABLED = true;
42
+ const MODE_VALUES = ["conversation", "workflow", "strict-workflow"];
43
+ const MODE_LABELS = {
44
+ default: "Use configured default",
45
+ conversation: "conversation",
46
+ workflow: "workflow",
47
+ "strict-workflow": "strict-workflow",
48
+ };
49
+ const MODE_DESCRIPTIONS = {
50
+ conversation: "Discussion and read-only inspection",
51
+ workflow: "Adaptive workflow for consequential work",
52
+ "strict-workflow": "Stronger decision and evidence pressure for high-risk work",
53
+ };
54
+ const LOCAL_INHERIT = "inherit";
55
+ const execFileAsync = promisify(execFile);
56
+
57
+ type ConfigScope = "session" | "local" | "repository";
58
+ type ConfigSource = "session" | "local" | "repository" | "builtin";
59
+
60
+ type SettingKind = "boolean" | "enum" | "integer" | "string" | "list" | "json" | "group";
61
+
62
+ type SettingsItem = {
63
+ id: string;
64
+ label: string;
65
+ description: string;
66
+ path?: string[];
67
+ kind: SettingKind;
68
+ value: unknown;
69
+ defaultValue?: unknown;
70
+ values?: string[];
71
+ valueLabels?: Record<string, string>;
72
+ valueDescriptions?: Record<string, string>;
73
+ inactive?: boolean;
74
+ displaySuffix?: string;
75
+ children?: SettingsItem[];
76
+ parse?: (text: string) => unknown;
77
+ format?: (value: unknown) => string;
78
+ configScope?: ConfigScope;
79
+ configValues?: Record<string, unknown>;
80
+ effectiveValue?: unknown;
81
+ effectiveSource?: ConfigSource;
82
+ inheritedValue?: unknown;
83
+ inheritedSource?: ConfigSource;
84
+ localOverrideValue?: unknown;
85
+ transient?: boolean;
86
+ };
87
+
88
+ type OpenSettingsOptions = {
89
+ title: string;
90
+ items: SettingsItem[];
91
+ ctx: any;
92
+ onChange: (item: SettingsItem, value: unknown) => Promise<SettingsCommitResult>;
93
+ initialChoice?: SettingsItem;
94
+ };
95
+
96
+ function isRecord(value: unknown): value is Record<string, unknown> {
97
+ return typeof value === "object" && value !== null && !Array.isArray(value);
98
+ }
99
+
100
+ function cloneJson<T>(value: T): T {
101
+ if (value === undefined) return value;
102
+ try {
103
+ return JSON.parse(JSON.stringify(value));
104
+ } catch (error) {
105
+ throw new Error(`Could not clone settings value: ${error instanceof Error ? error.message : String(error)}`);
106
+ }
107
+ }
108
+
109
+ function getPath(source: unknown, path: string[]): unknown {
110
+ let current = source;
111
+ for (const key of path) {
112
+ if (!isRecord(current)) return undefined;
113
+ current = current[key];
114
+ }
115
+ return current;
116
+ }
117
+
118
+ function setPath(target: Record<string, unknown>, path: string[], value: unknown) {
119
+ let current: Record<string, unknown> = target;
120
+ for (const key of path.slice(0, -1)) {
121
+ const next = current[key];
122
+ if (!isRecord(next)) {
123
+ current[key] = {};
124
+ }
125
+ current = current[key] as Record<string, unknown>;
126
+ }
127
+ current[path[path.length - 1]!] = value;
128
+ }
129
+
130
+ function deletePath(target: Record<string, unknown>, path: string[]) {
131
+ let current: unknown = target;
132
+ const parents: Array<{ object: Record<string, unknown>; key: string }> = [];
133
+ for (const key of path.slice(0, -1)) {
134
+ if (!isRecord(current)) return;
135
+ parents.push({ object: current, key });
136
+ current = current[key];
137
+ }
138
+ if (!isRecord(current)) return;
139
+ delete current[path[path.length - 1]!];
140
+
141
+ for (let index = parents.length - 1; index >= 0; index--) {
142
+ const { object, key } = parents[index]!;
143
+ const child = object[key];
144
+ if (isRecord(child) && Object.keys(child).length === 0) {
145
+ delete object[key];
146
+ }
147
+ }
148
+ }
149
+
150
+ function valuesEqual(a: unknown, b: unknown): boolean {
151
+ return JSON.stringify(a) === JSON.stringify(b);
152
+ }
153
+
154
+ function isEmptyValue(value: unknown): boolean {
155
+ return Array.isArray(value) && value.length === 0;
156
+ }
157
+
158
+ function setConfigValue(config: Record<string, unknown>, item: SettingsItem, value: unknown) {
159
+ if (!item.path?.length) {
160
+ throw new Error(`${item.label} is a settings group, not a writable setting.`);
161
+ }
162
+ if (item.defaultValue !== undefined && valuesEqual(value, item.defaultValue)) {
163
+ deletePath(config, item.path);
164
+ return;
165
+ }
166
+ if (isEmptyValue(value)) {
167
+ deletePath(config, item.path);
168
+ return;
169
+ }
170
+ setPath(config, item.path, value);
171
+ }
172
+
173
+ function parseStringList(text: string): string[] {
174
+ return text
175
+ .split(",")
176
+ .map((item) => item.trim())
177
+ .filter(Boolean);
178
+ }
179
+
180
+ function parsePositiveInteger(text: string): number {
181
+ const value = Number(text.trim());
182
+ if (!Number.isInteger(value) || value <= 0) {
183
+ throw new Error("Expected a positive integer.");
184
+ }
185
+ return value;
186
+ }
187
+
188
+ function parseJsonObject(text: string): unknown {
189
+ const trimmed = text.trim();
190
+ if (!trimmed) return {};
191
+ let parsed: unknown;
192
+ try {
193
+ parsed = JSON.parse(trimmed);
194
+ } catch (error) {
195
+ throw new Error(`Expected valid JSON: ${error instanceof Error ? error.message : String(error)}`);
196
+ }
197
+ if (!isRecord(parsed)) {
198
+ throw new Error("Expected a JSON object.");
199
+ }
200
+ return parsed;
201
+ }
202
+
203
+ function formatList(value: unknown): string {
204
+ return Array.isArray(value) ? value.join(", ") : "";
205
+ }
206
+
207
+ function formatJson(value: unknown): string {
208
+ return JSON.stringify(isRecord(value) ? value : {});
209
+ }
210
+
211
+ function booleanValue(value: unknown): string {
212
+ return value === true ? "enabled" : "disabled";
213
+ }
214
+
215
+ function normalizedSettingsConfig(rawConfig: Record<string, unknown>) {
216
+ const rawRouter = isRecord(rawConfig.outputRouter) ? rawConfig.outputRouter : {};
217
+ const normalized = normalizeFreeflowConfig({
218
+ ...rawConfig,
219
+ outputRouter: { ...rawRouter, enabled: true },
220
+ });
221
+ return normalized.config;
222
+ }
223
+
224
+ function rawValue(rawConfig: Record<string, unknown>, path: string[], fallback: unknown): unknown {
225
+ const value = getPath(rawConfig, path);
226
+ return value === undefined ? fallback : value;
227
+ }
228
+
229
+ function outputRouterItems(rawConfig: Record<string, unknown>, freeflowInactive = false): SettingsItem[] {
230
+ const effective = normalizedSettingsConfig(rawConfig);
231
+ const routerEnabled = normalizeFreeflowConfig(rawConfig).config.outputRouter.enabled;
232
+ const router = effective.outputRouter;
233
+ const scriptTransform = effective.scriptTransform;
234
+ const observedRouting = effective.observedRouting;
235
+ const inactive = freeflowInactive || !routerEnabled;
236
+
237
+ return [
238
+ {
239
+ id: "outputRouter.enabled",
240
+ label: "Output Router",
241
+ description: "Master switch for Freeflow routed evidence tools and router runtime guidance.",
242
+ path: ["outputRouter", "enabled"],
243
+ kind: "boolean",
244
+ value: routerEnabled,
245
+ defaultValue: DEFAULT_OUTPUT_ROUTER_ENABLED,
246
+ inactive: freeflowInactive,
247
+ },
248
+ {
249
+ id: "outputRouter.postToolRouting",
250
+ label: "Native safety net",
251
+ description:
252
+ "Post-process large/noisy native read/bash output. safety-net can replace oversized native output with vaulted routed evidence; strict is stronger.",
253
+ path: ["outputRouter", "postToolRouting"],
254
+ kind: "enum",
255
+ value: router.postToolRouting,
256
+ values: [...POST_TOOL_ROUTING_VALUES],
257
+ valueDescriptions: {
258
+ off: "Leave native tool output unchanged",
259
+ "safety-net": "Route oversized or noisy native output",
260
+ strict: "Apply stronger native output routing",
261
+ },
262
+ defaultValue: DEFAULT_POST_TOOL_ROUTING,
263
+ inactive,
264
+ },
265
+ {
266
+ id: "outputRouter.storagePolicy",
267
+ label: "Storage policy",
268
+ description: "Vault storage behavior for exactness-sensitive and noisy output.",
269
+ path: ["outputRouter", "storagePolicy"],
270
+ kind: "enum",
271
+ value: router.storagePolicy,
272
+ values: [...STORAGE_POLICY_VALUES],
273
+ defaultValue: DEFAULT_STORAGE_POLICY,
274
+ inactive,
275
+ },
276
+ {
277
+ id: "outputRouter.thresholds.largeOutputBytes",
278
+ label: "Large output bytes",
279
+ description: "Byte threshold for treating command/tool output as large.",
280
+ path: ["outputRouter", "thresholds", "largeOutputBytes"],
281
+ kind: "integer",
282
+ value: router.thresholds.largeOutputBytes,
283
+ defaultValue: DEFAULT_ROUTER_THRESHOLDS.largeOutputBytes,
284
+ parse: parsePositiveInteger,
285
+ inactive,
286
+ },
287
+ {
288
+ id: "outputRouter.thresholds.largeOutputLines",
289
+ label: "Large output lines",
290
+ description: "Line threshold for treating command/tool output as large.",
291
+ path: ["outputRouter", "thresholds", "largeOutputLines"],
292
+ kind: "integer",
293
+ value: router.thresholds.largeOutputLines,
294
+ defaultValue: DEFAULT_ROUTER_THRESHOLDS.largeOutputLines,
295
+ parse: parsePositiveInteger,
296
+ inactive,
297
+ },
298
+ {
299
+ id: "outputRouter.vault.root",
300
+ label: "Vault root",
301
+ description: "Root directory for recoverable routed output.",
302
+ path: ["outputRouter", "vault", "root"],
303
+ kind: "string",
304
+ value: router.vault.root,
305
+ defaultValue: DEFAULT_VAULT_ROOT,
306
+ inactive,
307
+ },
308
+ {
309
+ id: "outputRouter.vault.retention.ttlDays",
310
+ label: "Vault retention days",
311
+ description: "TTL retention for vaulted output. Set a positive integer; default is 7 days.",
312
+ path: ["outputRouter", "vault", "retention", "ttlDays"],
313
+ kind: "integer",
314
+ value:
315
+ router.vault.retention?.strategy === "ttl" ? router.vault.retention.ttlDays : DEFAULT_VAULT_RETENTION.ttlDays,
316
+ defaultValue: DEFAULT_VAULT_RETENTION.ttlDays,
317
+ parse: parsePositiveInteger,
318
+ inactive,
319
+ },
320
+ {
321
+ id: "outputRouter.generatedPaths",
322
+ label: "Generated paths",
323
+ description:
324
+ "Comma-separated globs for generated repo paths that should be treated as noisy or lower-priority evidence.",
325
+ path: ["outputRouter", "hints", "generatedPathGlobs"],
326
+ kind: "list",
327
+ value: rawValue(
328
+ rawConfig,
329
+ ["outputRouter", "hints", "generatedPathGlobs"],
330
+ rawValue(rawConfig, ["outputRouter", "generatedPaths"], router.hints?.generatedPathGlobs ?? []),
331
+ ),
332
+ format: formatList,
333
+ parse: parseStringList,
334
+ inactive,
335
+ },
336
+ {
337
+ id: "outputRouter.noisyCommandHints",
338
+ label: "Noisy command hints",
339
+ description: "Comma-separated command patterns that are likely to produce noisy output.",
340
+ path: ["outputRouter", "hints", "noisyCommandPatterns"],
341
+ kind: "list",
342
+ value: rawValue(
343
+ rawConfig,
344
+ ["outputRouter", "hints", "noisyCommandPatterns"],
345
+ rawValue(rawConfig, ["outputRouter", "noisyCommandHints"], router.hints?.noisyCommandPatterns ?? []),
346
+ ),
347
+ format: formatList,
348
+ parse: parseStringList,
349
+ inactive,
350
+ },
351
+ {
352
+ id: "outputRouter.scriptTransform.enabled",
353
+ label: "Script transform",
354
+ description:
355
+ "Enable sandboxed script transforms/producers. Requires proof-backed adapters; no unsandboxed fallback.",
356
+ path: ["outputRouter", "scriptTransform", "enabled"],
357
+ kind: "boolean",
358
+ value: scriptTransform.enabled,
359
+ defaultValue: DEFAULT_SCRIPT_TRANSFORM_CONFIG.enabled,
360
+ inactive,
361
+ },
362
+ {
363
+ id: "outputRouter.scriptTransform.languages",
364
+ label: "Script languages",
365
+ description: "Comma-separated sandbox languages to allow: javascript, python, jq.",
366
+ path: ["outputRouter", "scriptTransform", "languages"],
367
+ kind: "list",
368
+ value: scriptTransform.languages,
369
+ defaultValue: DEFAULT_SCRIPT_TRANSFORM_CONFIG.languages,
370
+ format: formatList,
371
+ parse: (text) => parseStringList(text).filter((value) => (SCRIPT_LANGUAGES as readonly string[]).includes(value)),
372
+ inactive,
373
+ },
374
+ {
375
+ id: "outputRouter.scriptTransform.limits.timeoutMs",
376
+ label: "Script timeout ms",
377
+ description: "Maximum sandboxed script execution time.",
378
+ path: ["outputRouter", "scriptTransform", "limits", "timeoutMs"],
379
+ kind: "integer",
380
+ value: scriptTransform.limits.timeoutMs,
381
+ defaultValue: DEFAULT_SCRIPT_TRANSFORM_CONFIG.limits.timeoutMs,
382
+ parse: parsePositiveInteger,
383
+ inactive,
384
+ },
385
+ {
386
+ id: "outputRouter.scriptTransform.limits.maxInputBytes",
387
+ label: "Script max input bytes",
388
+ description: "Maximum input size for sandboxed scripts.",
389
+ path: ["outputRouter", "scriptTransform", "limits", "maxInputBytes"],
390
+ kind: "integer",
391
+ value: scriptTransform.limits.maxInputBytes,
392
+ defaultValue: DEFAULT_SCRIPT_TRANSFORM_CONFIG.limits.maxInputBytes,
393
+ parse: parsePositiveInteger,
394
+ inactive,
395
+ },
396
+ {
397
+ id: "outputRouter.scriptTransform.limits.maxOutputBytes",
398
+ label: "Script max output bytes",
399
+ description: "Maximum output size for sandboxed scripts.",
400
+ path: ["outputRouter", "scriptTransform", "limits", "maxOutputBytes"],
401
+ kind: "integer",
402
+ value: scriptTransform.limits.maxOutputBytes,
403
+ defaultValue: DEFAULT_SCRIPT_TRANSFORM_CONFIG.limits.maxOutputBytes,
404
+ parse: parsePositiveInteger,
405
+ inactive,
406
+ },
407
+ {
408
+ id: "outputRouter.observedRouting.enabled",
409
+ label: "Observed routing",
410
+ description: "Route configured host tool results after they run, e.g. MCP/web/fetch/code-search output.",
411
+ path: ["outputRouter", "observedRouting", "enabled"],
412
+ kind: "boolean",
413
+ value: observedRouting.enabled,
414
+ defaultValue: DEFAULT_OBSERVED_ROUTING_CONFIG.enabled,
415
+ inactive,
416
+ },
417
+ ...observedProducerItems("web", "Web search", observedRouting.web, inactive),
418
+ ...observedProducerItems("fetch", "Fetch content", observedRouting.fetch, inactive),
419
+ ...observedProducerItems("codeSearch", "Code search", observedRouting.codeSearch, inactive),
420
+ {
421
+ id: "outputRouter.observedRouting.mcp.servers",
422
+ label: "MCP servers JSON",
423
+ description: 'JSON object keyed by MCP server id. Example: {"github":{"enabled":true,"persistence":"exact"}}',
424
+ path: ["outputRouter", "observedRouting", "mcp", "servers"],
425
+ kind: "json",
426
+ value: observedRouting.mcp?.servers ?? {},
427
+ defaultValue: DEFAULT_OBSERVED_ROUTING_CONFIG.mcp.servers,
428
+ format: formatJson,
429
+ parse: parseJsonObject,
430
+ inactive,
431
+ },
432
+ ];
433
+ }
434
+
435
+ function observedProducerItems(
436
+ id: "web" | "fetch" | "codeSearch",
437
+ label: string,
438
+ value: any,
439
+ inactive: boolean,
440
+ ): SettingsItem[] {
441
+ return [
442
+ {
443
+ id: `outputRouter.observedRouting.${id}.enabled`,
444
+ label: `${label} observed`,
445
+ description: `Enable observed routing for ${label.toLowerCase()} output.`,
446
+ path: ["outputRouter", "observedRouting", id, "enabled"],
447
+ kind: "boolean",
448
+ value: value?.enabled === true,
449
+ defaultValue: false,
450
+ inactive,
451
+ },
452
+ {
453
+ id: `outputRouter.observedRouting.${id}.persistence`,
454
+ label: `${label} persistence`,
455
+ description: "Persistence mode for this observed producer: none, metadata-only, or exact.",
456
+ path: ["outputRouter", "observedRouting", id, "persistence"],
457
+ kind: "enum",
458
+ value: value?.persistence ?? "none",
459
+ values: [...OBSERVED_PERSISTENCE_VALUES],
460
+ valueDescriptions: {
461
+ none: "Do not persist observed output",
462
+ "metadata-only": "Persist metadata without raw output",
463
+ exact: "Persist exact recoverable output",
464
+ },
465
+ defaultValue: value?.enabled === true ? undefined : "none",
466
+ inactive,
467
+ },
468
+ ];
469
+ }
470
+
471
+ function configValueForChoice(item: SettingsItem, value: unknown): unknown {
472
+ const key = String(value);
473
+ if (item.configValues && Object.hasOwn(item.configValues, key)) {
474
+ return item.configValues[key];
475
+ }
476
+ return value;
477
+ }
478
+
479
+ function effectiveItemValue(item: SettingsItem): unknown {
480
+ return item.effectiveValue !== undefined ? item.effectiveValue : item.value;
481
+ }
482
+
483
+ function formatCoreValue(value: unknown): string {
484
+ return typeof value === "boolean" ? booleanValue(value) : String(value ?? "");
485
+ }
486
+
487
+ function coreDisplaySuffix(item: SettingsItem, inactive = false): string {
488
+ const source = item.effectiveSource ?? "builtin";
489
+ const parts =
490
+ item.configScope === "repository" && source === "local"
491
+ ? [`effective ${formatCoreValue(effectiveItemValue(item))}`, source]
492
+ : [source];
493
+ if (inactive) parts.push("inactive");
494
+ return `(${parts.join(" · ")})`;
495
+ }
496
+
497
+ function updateScopedItemState(item: SettingsItem, value: unknown) {
498
+ if (!item.configScope) return;
499
+ const configValue = configValueForChoice(item, value);
500
+ if (item.configScope === "session" || item.configScope === "local") {
501
+ item.effectiveValue = configValue === undefined ? item.inheritedValue : configValue;
502
+ item.effectiveSource = configValue === undefined ? (item.inheritedSource ?? "builtin") : item.configScope;
503
+ return;
504
+ }
505
+ if (item.localOverrideValue !== undefined) {
506
+ item.effectiveValue = item.localOverrideValue;
507
+ item.effectiveSource = "local";
508
+ return;
509
+ }
510
+ item.effectiveValue = configValue;
511
+ item.effectiveSource =
512
+ item.defaultValue !== undefined && valuesEqual(configValue, item.defaultValue) ? "builtin" : "repository";
513
+ }
514
+
515
+ type ScopedBooleanItemOptions = {
516
+ scope: ConfigScope;
517
+ rawConfig: Record<string, unknown>;
518
+ localConfig: Record<string, unknown>;
519
+ id: string;
520
+ label: string;
521
+ description: string;
522
+ path: string[];
523
+ effectiveValue: boolean;
524
+ effectiveSource: ConfigSource;
525
+ defaultValue: boolean;
526
+ };
527
+
528
+ function createScopedBooleanItem(options: ScopedBooleanItemOptions): SettingsItem {
529
+ const repositoryValue = getPath(options.rawConfig, options.path);
530
+ const inheritedValue = typeof repositoryValue === "boolean" ? repositoryValue : options.defaultValue;
531
+ const inheritedSource: ConfigSource = typeof repositoryValue === "boolean" ? "repository" : "builtin";
532
+ const localValue = getPath(options.localConfig, options.path);
533
+ let item: SettingsItem;
534
+ if (options.scope === "local") {
535
+ const selectedValue = typeof localValue === "boolean" ? String(localValue) : LOCAL_INHERIT;
536
+ item = {
537
+ id: options.id,
538
+ label: options.label,
539
+ description: `${options.description} Choose inherit to use the repository value; use /freeflow settings repo to edit shared defaults.`,
540
+ path: options.path,
541
+ kind: "enum",
542
+ value: selectedValue,
543
+ values: [LOCAL_INHERIT, "true", "false"],
544
+ valueLabels: {
545
+ inherit: "Inherit repository",
546
+ true: "enabled",
547
+ false: "disabled",
548
+ },
549
+ valueDescriptions: {
550
+ inherit: `Use ${formatCoreValue(inheritedValue)} from ${inheritedSource}.`,
551
+ true: "Set a personal enabled override.",
552
+ false: "Set a personal disabled override.",
553
+ },
554
+ format: (value) => {
555
+ if (value === LOCAL_INHERIT) return LOCAL_INHERIT;
556
+ return booleanValue(value === "true");
557
+ },
558
+ configScope: "local",
559
+ configValues: {
560
+ inherit: undefined,
561
+ true: true,
562
+ false: false,
563
+ },
564
+ effectiveValue: options.effectiveValue,
565
+ effectiveSource: options.effectiveSource,
566
+ inheritedValue,
567
+ inheritedSource,
568
+ };
569
+ } else {
570
+ item = {
571
+ id: options.id,
572
+ label: options.label,
573
+ description: `${options.description} This edits shared .freeflow/config.json.`,
574
+ path: options.path,
575
+ kind: "boolean",
576
+ value: inheritedValue,
577
+ defaultValue: options.defaultValue,
578
+ configScope: "repository",
579
+ effectiveValue: options.effectiveValue,
580
+ effectiveSource: options.effectiveSource,
581
+ localOverrideValue: typeof localValue === "boolean" ? localValue : undefined,
582
+ };
583
+ }
584
+ item.displaySuffix = coreDisplaySuffix(item);
585
+ return item;
586
+ }
587
+
588
+ type ScopedDefaultModeItemOptions = {
589
+ scope: ConfigScope;
590
+ repositoryDefaultMode: string;
591
+ repositoryDefaultSource: ConfigSource;
592
+ localDefaultMode?: string;
593
+ effectiveValue: string;
594
+ effectiveSource: ConfigSource;
595
+ };
596
+
597
+ function createScopedDefaultModeItem(options: ScopedDefaultModeItemOptions): SettingsItem {
598
+ let item: SettingsItem;
599
+ if (options.scope === "local") {
600
+ item = {
601
+ id: "freeflow.defaultMode",
602
+ label: "Default mode",
603
+ description:
604
+ "Personal default mode for this checkout. Choose inherit to use the repository default; use /freeflow settings repo to edit shared defaults.",
605
+ path: ["defaultMode"],
606
+ kind: "enum",
607
+ value: options.localDefaultMode ?? LOCAL_INHERIT,
608
+ values: [LOCAL_INHERIT, ...MODE_VALUES],
609
+ valueLabels: {
610
+ inherit: "Inherit repository",
611
+ ...MODE_LABELS,
612
+ },
613
+ valueDescriptions: {
614
+ inherit: `Use ${options.repositoryDefaultMode} from ${options.repositoryDefaultSource}.`,
615
+ ...MODE_DESCRIPTIONS,
616
+ },
617
+ format: (value) => String(value),
618
+ configScope: "local",
619
+ configValues: { inherit: undefined },
620
+ effectiveValue: options.effectiveValue,
621
+ effectiveSource: options.effectiveSource,
622
+ inheritedValue: options.repositoryDefaultMode,
623
+ inheritedSource: options.repositoryDefaultSource,
624
+ };
625
+ } else {
626
+ item = {
627
+ id: "freeflow.defaultMode",
628
+ label: "Default mode",
629
+ description:
630
+ "Shared repository default Freeflow mode used when Skills are enabled. This edits .freeflow/config.json.",
631
+ path: ["defaultMode"],
632
+ kind: "enum",
633
+ value: options.repositoryDefaultMode,
634
+ values: MODE_VALUES,
635
+ valueLabels: MODE_LABELS,
636
+ valueDescriptions: MODE_DESCRIPTIONS,
637
+ configScope: "repository",
638
+ effectiveValue: options.effectiveValue,
639
+ effectiveSource: options.effectiveSource,
640
+ localOverrideValue: options.localDefaultMode,
641
+ };
642
+ }
643
+ item.displaySuffix = coreDisplaySuffix(item);
644
+ return item;
645
+ }
646
+
647
+ function sessionModeDisplaySuffix(
648
+ sessionMode: string,
649
+ defaultMode: string,
650
+ defaultSource: ConfigSource,
651
+ inactive: boolean,
652
+ ): string | undefined {
653
+ if (sessionMode !== "default") return undefined;
654
+ const inactiveSuffix = inactive ? " · inactive" : "";
655
+ return `(${defaultMode} · ${defaultSource}${inactiveSuffix})`;
656
+ }
657
+
658
+ function validModeOrUndefined(value: unknown): string | undefined {
659
+ return typeof value === "string" && VALID_MODES.has(value) ? value : undefined;
660
+ }
661
+
662
+ function resolveSettingsCoreView(
663
+ rawConfig: Record<string, unknown>,
664
+ layers?: Awaited<ReturnType<typeof readFreeflowConfigLayers>>,
665
+ ) {
666
+ const localConfig = layers?.local.valid && isRecord(layers.local.parsed) ? layers.local.parsed : {};
667
+ const repositorySkillsValue = getPath(rawConfig, ["skills"]);
668
+ const repositorySkills = isRecord(repositorySkillsValue) ? repositorySkillsValue : {};
669
+ const fallbackCore = {
670
+ enabled: getPath(rawConfig, ["enabled"]) !== false,
671
+ interactionContract: getPath(rawConfig, ["interactionContract"]) !== false,
672
+ skills: { enabled: getPath(repositorySkills, ["enabled"]) !== false },
673
+ defaultMode: validModeOrUndefined(rawConfig.defaultMode) ?? "workflow",
674
+ };
675
+ const fallbackSources = {
676
+ enabled: typeof getPath(rawConfig, ["enabled"]) === "boolean" ? "repository" : "builtin",
677
+ interactionContract: typeof getPath(rawConfig, ["interactionContract"]) === "boolean" ? "repository" : "builtin",
678
+ skillsEnabled: typeof getPath(repositorySkills, ["enabled"]) === "boolean" ? "repository" : "builtin",
679
+ defaultMode: validModeOrUndefined(rawConfig.defaultMode) !== undefined ? "repository" : "builtin",
680
+ } as const;
681
+ return {
682
+ localConfig,
683
+ core: layers?.coreConfig ?? fallbackCore,
684
+ sources: (layers?.sources ?? fallbackSources) as {
685
+ enabled: ConfigSource;
686
+ interactionContract: ConfigSource;
687
+ skillsEnabled: ConfigSource;
688
+ defaultMode: ConfigSource;
689
+ },
690
+ };
691
+ }
692
+
693
+ function groupEnabled(items: SettingsItem[], id: string): boolean {
694
+ return items.find((item) => item.id === id)?.value === true;
695
+ }
696
+
697
+ function createSessionBooleanItem(options: {
698
+ id: string;
699
+ label: string;
700
+ description: string;
701
+ key: "enabled" | "interactionContract" | "skillsEnabled";
702
+ inheritedValue: boolean;
703
+ inheritedSource: ConfigSource;
704
+ effectiveValue: boolean;
705
+ effectiveSource: ConfigSource;
706
+ sessionOverrides: Record<string, boolean>;
707
+ }): SettingsItem {
708
+ const override = options.sessionOverrides[options.key];
709
+ const item: SettingsItem = {
710
+ id: options.id,
711
+ label: options.label,
712
+ description: options.description,
713
+ kind: "enum",
714
+ value: typeof override === "boolean" ? String(override) : LOCAL_INHERIT,
715
+ values: [LOCAL_INHERIT, "true", "false"],
716
+ valueLabels: {
717
+ inherit: "Inherit configured value",
718
+ true: "Enabled for this session",
719
+ false: "Disabled for this session",
720
+ },
721
+ valueDescriptions: {
722
+ inherit: `Use ${formatCoreValue(options.inheritedValue)} from ${options.inheritedSource}.`,
723
+ true: "Temporarily enable this setting for the current Pi session.",
724
+ false: "Temporarily disable this setting for the current Pi session.",
725
+ },
726
+ format: (value) => {
727
+ if (value === LOCAL_INHERIT) return LOCAL_INHERIT;
728
+ return booleanValue(value === "true");
729
+ },
730
+ configScope: "session",
731
+ configValues: {
732
+ inherit: undefined,
733
+ true: true,
734
+ false: false,
735
+ },
736
+ effectiveValue: options.effectiveValue,
737
+ effectiveSource: options.effectiveSource,
738
+ inheritedValue: options.inheritedValue,
739
+ inheritedSource: options.inheritedSource,
740
+ };
741
+ item.displaySuffix = coreDisplaySuffix(item);
742
+ return item;
743
+ }
744
+
745
+ function sessionFreeflowItems(
746
+ state: Awaited<ReturnType<typeof readCapabilityState>>,
747
+ modeState: Awaited<ReturnType<typeof readModeState>>,
748
+ ): SettingsItem[] {
749
+ const sessionOverrides = state.sessionOverrides as Record<string, boolean>;
750
+ const configured = state.configuredCoreConfig;
751
+ const configuredSources = state.configuredSources as {
752
+ enabled: ConfigSource;
753
+ interactionContract: ConfigSource;
754
+ skillsEnabled: ConfigSource;
755
+ };
756
+ const effectiveSources = state.configSources as {
757
+ enabled: ConfigSource;
758
+ interactionContract: ConfigSource;
759
+ skillsEnabled: ConfigSource;
760
+ };
761
+
762
+ const freeflowItem = createSessionBooleanItem({
763
+ id: "freeflow.enabled",
764
+ label: "Freeflow",
765
+ description: "Temporary master override for this Pi session.",
766
+ key: "enabled",
767
+ inheritedValue: configured.enabled,
768
+ inheritedSource: configuredSources.enabled,
769
+ effectiveValue: state.enabled,
770
+ effectiveSource: effectiveSources.enabled,
771
+ sessionOverrides,
772
+ });
773
+ const interactionItem = createSessionBooleanItem({
774
+ id: "freeflow.interactionContract",
775
+ label: "Interaction Contract",
776
+ description: "Temporary Interaction Contract override for this Pi session.",
777
+ key: "interactionContract",
778
+ inheritedValue: configured.interactionContract,
779
+ inheritedSource: configuredSources.interactionContract,
780
+ effectiveValue: state.interactionContract.enabled,
781
+ effectiveSource: effectiveSources.interactionContract,
782
+ sessionOverrides,
783
+ });
784
+ const skillsItem = createSessionBooleanItem({
785
+ id: "freeflow.skills.enabled",
786
+ label: "Skills",
787
+ description: "Temporary Freeflow Skills override for this Pi session.",
788
+ key: "skillsEnabled",
789
+ inheritedValue: configured.skills.enabled,
790
+ inheritedSource: configuredSources.skillsEnabled,
791
+ effectiveValue: state.skills.enabled,
792
+ effectiveSource: effectiveSources.skillsEnabled,
793
+ sessionOverrides,
794
+ });
795
+
796
+ const freeflowInactive = !state.enabled;
797
+ const skillsEnabled = state.skills.enabled;
798
+ interactionItem.inactive = freeflowInactive;
799
+ skillsItem.inactive = freeflowInactive;
800
+
801
+ const sessionMode = modeState.currentMode ?? "default";
802
+ const sessionModeItem: SettingsItem = {
803
+ id: "freeflow.sessionMode",
804
+ label: "Mode",
805
+ description: "Temporary mode override for this Pi session.",
806
+ kind: "enum",
807
+ value: sessionMode,
808
+ values: ["default", ...MODE_VALUES],
809
+ valueLabels: {
810
+ ...MODE_LABELS,
811
+ default: "Use configured default",
812
+ },
813
+ valueDescriptions: {
814
+ default: `${modeState.defaultMode} from ${modeState.defaultModeSource}`,
815
+ ...MODE_DESCRIPTIONS,
816
+ },
817
+ inactive: freeflowInactive || !skillsEnabled,
818
+ displaySuffix: sessionModeDisplaySuffix(
819
+ sessionMode,
820
+ modeState.defaultMode,
821
+ modeState.defaultModeSource,
822
+ freeflowInactive || !skillsEnabled,
823
+ ),
824
+ inheritedValue: modeState.defaultMode,
825
+ inheritedSource: modeState.defaultModeSource,
826
+ };
827
+
828
+ return [
829
+ freeflowItem,
830
+ interactionItem,
831
+ skillsItem,
832
+ sessionModeItem,
833
+ {
834
+ id: "freeflow.session.reset",
835
+ label: "Reset session overrides",
836
+ description: "Clear Freeflow, Interaction Contract, Skills, and mode overrides for this Pi session.",
837
+ kind: "enum",
838
+ value: "available",
839
+ values: ["reset"],
840
+ valueLabels: { reset: "Reset all session overrides" },
841
+ valueDescriptions: { reset: "Return every session setting to its configured value." },
842
+ format: () => "available",
843
+ transient: true,
844
+ },
845
+ ];
846
+ }
847
+
848
+ function freeflowItems(
849
+ rawConfig: Record<string, unknown>,
850
+ modeState?: Awaited<ReturnType<typeof readModeState>>,
851
+ options: {
852
+ scope?: ConfigScope;
853
+ layers?: Awaited<ReturnType<typeof readFreeflowConfigLayers>>;
854
+ } = {},
855
+ ): SettingsItem[] {
856
+ const scope = options.scope ?? "repository";
857
+ const layers = options.layers;
858
+ const { localConfig, core, sources } = resolveSettingsCoreView(rawConfig, layers);
859
+
860
+ const freeflowItem = createScopedBooleanItem({
861
+ scope,
862
+ rawConfig,
863
+ localConfig,
864
+ id: "freeflow.enabled",
865
+ label: "Freeflow",
866
+ description: "Master switch for the Interaction Contract and Freeflow skills in this checkout.",
867
+ path: ["enabled"],
868
+ effectiveValue: core.enabled,
869
+ effectiveSource: sources.enabled,
870
+ defaultValue: DEFAULT_FREEFLOW_ENABLED,
871
+ });
872
+ const interactionItem = createScopedBooleanItem({
873
+ scope,
874
+ rawConfig,
875
+ localConfig,
876
+ id: "freeflow.interactionContract",
877
+ label: "Interaction Contract",
878
+ description: "Apply Freeflow's compact turn-interpretation and collaboration guidance.",
879
+ path: ["interactionContract"],
880
+ effectiveValue: core.interactionContract,
881
+ effectiveSource: sources.interactionContract,
882
+ defaultValue: DEFAULT_INTERACTION_CONTRACT_ENABLED,
883
+ });
884
+ const skillsItem = createScopedBooleanItem({
885
+ scope,
886
+ rawConfig,
887
+ localConfig,
888
+ id: "freeflow.skills.enabled",
889
+ label: "Skills",
890
+ description: "Expose Freeflow skills and load Workflow once on the first turn.",
891
+ path: ["skills", "enabled"],
892
+ effectiveValue: core.skills.enabled,
893
+ effectiveSource: sources.skillsEnabled,
894
+ defaultValue: DEFAULT_SKILLS_ENABLED,
895
+ });
896
+
897
+ const repositoryModeValue = validModeOrUndefined(rawConfig.defaultMode);
898
+ const repositoryDefaultMode = repositoryModeValue ?? "workflow";
899
+ const repositoryDefaultSource: ConfigSource = repositoryModeValue ? "repository" : "builtin";
900
+ const localDefaultMode = validModeOrUndefined(localConfig.defaultMode);
901
+ const defaultModeItem = createScopedDefaultModeItem({
902
+ scope,
903
+ repositoryDefaultMode,
904
+ repositoryDefaultSource,
905
+ localDefaultMode,
906
+ effectiveValue: core.defaultMode,
907
+ effectiveSource: sources.defaultMode,
908
+ });
909
+
910
+ const freeflowInactive = !core.enabled;
911
+ const skillsEnabled = core.skills.enabled;
912
+ interactionItem.inactive = freeflowInactive;
913
+ skillsItem.inactive = freeflowInactive;
914
+ defaultModeItem.inactive = freeflowInactive;
915
+ defaultModeItem.displaySuffix = coreDisplaySuffix(defaultModeItem, freeflowInactive || !skillsEnabled);
916
+ const sessionMode = modeState?.currentMode ?? "default";
917
+ const sessionModeItem: SettingsItem = {
918
+ id: "freeflow.sessionMode",
919
+ label: "Session mode",
920
+ description:
921
+ "Temporary mode override for this Pi session. Use configured default clears the override without changing either config file.",
922
+ kind: "enum",
923
+ value: sessionMode,
924
+ values: ["default", ...MODE_VALUES],
925
+ valueLabels: {
926
+ ...MODE_LABELS,
927
+ default: "Use configured default",
928
+ },
929
+ valueDescriptions: {
930
+ default: `${core.defaultMode} from ${sources.defaultMode}`,
931
+ ...MODE_DESCRIPTIONS,
932
+ },
933
+ inactive: freeflowInactive || !skillsEnabled,
934
+ displaySuffix: sessionModeDisplaySuffix(
935
+ sessionMode,
936
+ core.defaultMode,
937
+ sources.defaultMode,
938
+ freeflowInactive || !skillsEnabled,
939
+ ),
940
+ };
941
+
942
+ const routerItems = outputRouterItems(rawConfig, freeflowInactive);
943
+ const routerEnabled = groupEnabled(routerItems, "outputRouter.enabled");
944
+
945
+ return [
946
+ freeflowItem,
947
+ interactionItem,
948
+ skillsItem,
949
+ sessionModeItem,
950
+ defaultModeItem,
951
+ {
952
+ id: "outputRouter.group",
953
+ label: "Output Router",
954
+ description:
955
+ "Shared repository settings for routed evidence tools, native output safety net, vault storage, script transforms, and observed tool routing.",
956
+ kind: "group",
957
+ value: routerEnabled,
958
+ inactive: freeflowInactive,
959
+ displaySuffix: "(repository)",
960
+ children: routerItems,
961
+ },
962
+ ];
963
+ }
964
+
965
+ function migrateLegacyRouterConfig(config: Record<string, unknown>) {
966
+ const outputRouter = isRecord(config.outputRouter) ? config.outputRouter : undefined;
967
+ if (!outputRouter) return;
968
+ if (config.scriptTransform !== undefined && outputRouter.scriptTransform === undefined) {
969
+ outputRouter.scriptTransform = config.scriptTransform;
970
+ delete config.scriptTransform;
971
+ }
972
+ if (config.observedRouting !== undefined && outputRouter.observedRouting === undefined) {
973
+ outputRouter.observedRouting = config.observedRouting;
974
+ delete config.observedRouting;
975
+ }
976
+
977
+ const thresholds = isRecord(outputRouter.thresholds) ? outputRouter.thresholds : {};
978
+ if (outputRouter.largeOutputBytes !== undefined && thresholds.largeOutputBytes === undefined) {
979
+ thresholds.largeOutputBytes = outputRouter.largeOutputBytes;
980
+ outputRouter.thresholds = thresholds;
981
+ delete outputRouter.largeOutputBytes;
982
+ }
983
+ if (outputRouter.largeOutputLines !== undefined && thresholds.largeOutputLines === undefined) {
984
+ thresholds.largeOutputLines = outputRouter.largeOutputLines;
985
+ outputRouter.thresholds = thresholds;
986
+ delete outputRouter.largeOutputLines;
987
+ }
988
+
989
+ const vault = isRecord(outputRouter.vault) ? outputRouter.vault : {};
990
+ if (outputRouter.vaultRoot !== undefined && vault.root === undefined) {
991
+ vault.root = outputRouter.vaultRoot;
992
+ outputRouter.vault = vault;
993
+ delete outputRouter.vaultRoot;
994
+ }
995
+ if (outputRouter.vaultRetentionDays !== undefined && !isRecord(vault.retention)) {
996
+ vault.retention = {
997
+ strategy: "ttl",
998
+ ttlDays: outputRouter.vaultRetentionDays,
999
+ };
1000
+ outputRouter.vault = vault;
1001
+ delete outputRouter.vaultRetentionDays;
1002
+ }
1003
+
1004
+ const hints = isRecord(outputRouter.hints) ? outputRouter.hints : {};
1005
+ if (outputRouter.generatedPaths !== undefined && hints.generatedPathGlobs === undefined) {
1006
+ hints.generatedPathGlobs = outputRouter.generatedPaths;
1007
+ outputRouter.hints = hints;
1008
+ delete outputRouter.generatedPaths;
1009
+ }
1010
+ if (outputRouter.noisyCommandHints !== undefined && hints.noisyCommandPatterns === undefined) {
1011
+ hints.noisyCommandPatterns = outputRouter.noisyCommandHints;
1012
+ outputRouter.hints = hints;
1013
+ delete outputRouter.noisyCommandHints;
1014
+ }
1015
+ }
1016
+
1017
+ function pruneKnownDefaults(config: Record<string, unknown>) {
1018
+ const defaultPaths: Array<{ path: string[]; value: unknown }> = [
1019
+ { path: ["enabled"], value: DEFAULT_FREEFLOW_ENABLED },
1020
+ {
1021
+ path: ["interactionContract"],
1022
+ value: DEFAULT_INTERACTION_CONTRACT_ENABLED,
1023
+ },
1024
+ { path: ["skills", "enabled"], value: DEFAULT_SKILLS_ENABLED },
1025
+ { path: ["outputRouter", "enabled"], value: DEFAULT_OUTPUT_ROUTER_ENABLED },
1026
+ {
1027
+ path: ["outputRouter", "postToolRouting"],
1028
+ value: DEFAULT_POST_TOOL_ROUTING,
1029
+ },
1030
+ { path: ["outputRouter", "storagePolicy"], value: DEFAULT_STORAGE_POLICY },
1031
+ {
1032
+ path: ["outputRouter", "thresholds", "largeOutputBytes"],
1033
+ value: DEFAULT_ROUTER_THRESHOLDS.largeOutputBytes,
1034
+ },
1035
+ {
1036
+ path: ["outputRouter", "thresholds", "largeOutputLines"],
1037
+ value: DEFAULT_ROUTER_THRESHOLDS.largeOutputLines,
1038
+ },
1039
+ { path: ["outputRouter", "vault", "root"], value: DEFAULT_VAULT_ROOT },
1040
+ {
1041
+ path: ["outputRouter", "vault", "retention", "ttlDays"],
1042
+ value: DEFAULT_VAULT_RETENTION.ttlDays,
1043
+ },
1044
+ {
1045
+ path: ["outputRouter", "scriptTransform", "enabled"],
1046
+ value: DEFAULT_SCRIPT_TRANSFORM_CONFIG.enabled,
1047
+ },
1048
+ {
1049
+ path: ["outputRouter", "scriptTransform", "sandbox"],
1050
+ value: DEFAULT_SCRIPT_TRANSFORM_CONFIG.sandbox,
1051
+ },
1052
+ {
1053
+ path: ["outputRouter", "scriptTransform", "languages"],
1054
+ value: DEFAULT_SCRIPT_TRANSFORM_CONFIG.languages,
1055
+ },
1056
+ {
1057
+ path: ["outputRouter", "scriptTransform", "network"],
1058
+ value: DEFAULT_SCRIPT_TRANSFORM_CONFIG.network,
1059
+ },
1060
+ {
1061
+ path: ["outputRouter", "scriptTransform", "rawScriptPersistence"],
1062
+ value: DEFAULT_SCRIPT_TRANSFORM_CONFIG.rawScriptPersistence,
1063
+ },
1064
+ {
1065
+ path: ["outputRouter", "scriptTransform", "limits", "timeoutMs"],
1066
+ value: DEFAULT_SCRIPT_TRANSFORM_CONFIG.limits.timeoutMs,
1067
+ },
1068
+ {
1069
+ path: ["outputRouter", "scriptTransform", "limits", "maxInputBytes"],
1070
+ value: DEFAULT_SCRIPT_TRANSFORM_CONFIG.limits.maxInputBytes,
1071
+ },
1072
+ {
1073
+ path: ["outputRouter", "scriptTransform", "limits", "maxOutputBytes"],
1074
+ value: DEFAULT_SCRIPT_TRANSFORM_CONFIG.limits.maxOutputBytes,
1075
+ },
1076
+ {
1077
+ path: ["outputRouter", "observedRouting", "enabled"],
1078
+ value: DEFAULT_OBSERVED_ROUTING_CONFIG.enabled,
1079
+ },
1080
+ {
1081
+ path: ["outputRouter", "observedRouting", "onRoutingFailure"],
1082
+ value: DEFAULT_OBSERVED_ROUTING_CONFIG.onRoutingFailure,
1083
+ },
1084
+ {
1085
+ path: ["outputRouter", "observedRouting", "mcp", "servers"],
1086
+ value: DEFAULT_OBSERVED_ROUTING_CONFIG.mcp.servers,
1087
+ },
1088
+ {
1089
+ path: ["outputRouter", "observedRouting", "web", "enabled"],
1090
+ value: DEFAULT_OBSERVED_ROUTING_CONFIG.web.enabled,
1091
+ },
1092
+ {
1093
+ path: ["outputRouter", "observedRouting", "fetch", "enabled"],
1094
+ value: DEFAULT_OBSERVED_ROUTING_CONFIG.fetch.enabled,
1095
+ },
1096
+ {
1097
+ path: ["outputRouter", "observedRouting", "codeSearch", "enabled"],
1098
+ value: DEFAULT_OBSERVED_ROUTING_CONFIG.codeSearch.enabled,
1099
+ },
1100
+ ];
1101
+
1102
+ for (const item of defaultPaths) {
1103
+ if (valuesEqual(getPath(config, item.path), item.value)) {
1104
+ deletePath(config, item.path);
1105
+ }
1106
+ }
1107
+
1108
+ for (const producer of ["web", "fetch", "codeSearch"]) {
1109
+ const enabledPath = ["outputRouter", "observedRouting", producer, "enabled"];
1110
+ const persistencePath = ["outputRouter", "observedRouting", producer, "persistence"];
1111
+ if (getPath(config, enabledPath) !== true && getPath(config, persistencePath) === "none") {
1112
+ deletePath(config, persistencePath);
1113
+ }
1114
+ }
1115
+
1116
+ const servers = getPath(config, ["outputRouter", "observedRouting", "mcp", "servers"]);
1117
+ if (isRecord(servers) && Object.keys(servers).length === 0) {
1118
+ deletePath(config, ["outputRouter", "observedRouting", "mcp", "servers"]);
1119
+ }
1120
+ }
1121
+
1122
+ async function ensureLocalConfigIgnored(cwd: string) {
1123
+ let gitPath: string;
1124
+ try {
1125
+ const result = await execFileAsync("git", ["-C", cwd, "rev-parse", "--git-path", "info/exclude"]);
1126
+ gitPath = result.stdout.trim();
1127
+ } catch {
1128
+ return;
1129
+ }
1130
+
1131
+ const tracked = await execFileAsync("git", ["-C", cwd, "ls-files", "--", ".freeflow/local.json"]);
1132
+ if (tracked.stdout.trim()) {
1133
+ throw new Error(
1134
+ ".freeflow/local.json is tracked by git; remove it from the index before writing personal overrides.",
1135
+ );
1136
+ }
1137
+
1138
+ try {
1139
+ await execFileAsync("git", ["-C", cwd, "check-ignore", "-q", "--", ".freeflow/local.json"]);
1140
+ return;
1141
+ } catch {
1142
+ // Add a local exclude when the repository does not already ignore the file.
1143
+ }
1144
+
1145
+ const excludePath = isAbsolute(gitPath) ? gitPath : resolve(cwd, gitPath);
1146
+ let existing = "";
1147
+ try {
1148
+ existing = await readFile(excludePath, "utf8");
1149
+ } catch {
1150
+ // The git metadata path may not exist yet in a minimal repository.
1151
+ }
1152
+ const rule = ".freeflow/local.json";
1153
+ if (existing.split(/\r?\n/).includes(rule)) return;
1154
+ await mkdir(dirname(excludePath), { recursive: true });
1155
+ const prefix = existing && !existing.endsWith("\n") ? "\n" : "";
1156
+ await appendFile(excludePath, `${prefix}${rule}\n`, "utf8");
1157
+ }
1158
+
1159
+ async function updateConfig(
1160
+ cwd: string,
1161
+ item: SettingsItem,
1162
+ value: unknown,
1163
+ scope: ConfigScope = item.configScope ?? "repository",
1164
+ ) {
1165
+ if (scope === "local") {
1166
+ if (!item.path?.length) {
1167
+ throw new Error(`${item.label} is a settings group, not a writable setting.`);
1168
+ }
1169
+ const current = await readFreeflowLocalConfig(cwd);
1170
+ const next = cloneJson(current) as Record<string, unknown>;
1171
+ const configValue = configValueForChoice(item, value);
1172
+ const previousValue = getPath(current, item.path);
1173
+ if (configValue === undefined || isEmptyValue(configValue)) {
1174
+ if (previousValue === undefined) return;
1175
+ deletePath(next, item.path);
1176
+ } else {
1177
+ setPath(next, item.path, configValue);
1178
+ }
1179
+ const path = join(cwd, ".freeflow/local.json");
1180
+ await ensureLocalConfigIgnored(cwd);
1181
+ if (Object.keys(next).length === 0) {
1182
+ await rm(path, { force: true });
1183
+ return;
1184
+ }
1185
+ await mkdir(join(cwd, ".freeflow"), { recursive: true });
1186
+ await writeFile(path, `${JSON.stringify(next, null, 2)}\n`, "utf8");
1187
+ return;
1188
+ }
1189
+
1190
+ const current = await readFreeflowConfig(cwd);
1191
+ const next = cloneJson(current) as Record<string, unknown>;
1192
+ setConfigValue(next, item, value);
1193
+ migrateLegacyRouterConfig(next);
1194
+ pruneKnownDefaults(next);
1195
+ await mkdir(join(cwd, ".freeflow"), { recursive: true });
1196
+ await writeFile(join(cwd, ".freeflow/config.json"), `${JSON.stringify(next, null, 2)}\n`, "utf8");
1197
+ }
1198
+
1199
+ function valueForDisplay(item: SettingsItem): string {
1200
+ let value: string;
1201
+ if (item.configScope === "local" || item.configScope === "session") {
1202
+ value = formatCoreValue(effectiveItemValue(item));
1203
+ } else if (item.kind === "boolean") {
1204
+ value = booleanValue(item.value);
1205
+ } else if (item.kind === "group") {
1206
+ const status = typeof item.value === "boolean" ? booleanValue(item.value) : String(item.value ?? "");
1207
+ const count = item.children?.length ?? 0;
1208
+ value = count > 0 ? `${status} (${count})` : status;
1209
+ } else if (item.format) {
1210
+ value = item.format(item.value);
1211
+ } else {
1212
+ value = String(item.value ?? "");
1213
+ }
1214
+ return item.displaySuffix ? `${value} ${item.displaySuffix}` : value;
1215
+ }
1216
+
1217
+ function walkSettingsItems(items: SettingsItem[], visitor: (item: SettingsItem) => void) {
1218
+ for (const item of items) {
1219
+ visitor(item);
1220
+ if (item.children) walkSettingsItems(item.children, visitor);
1221
+ }
1222
+ }
1223
+
1224
+ function findSettingsItem(items: SettingsItem[], id: string): SettingsItem | undefined {
1225
+ for (const item of items) {
1226
+ if (item.id === id) return item;
1227
+ const child = item.children ? findSettingsItem(item.children, id) : undefined;
1228
+ if (child) return child;
1229
+ }
1230
+ return undefined;
1231
+ }
1232
+
1233
+ function refreshSettingsDerivedState(items: SettingsItem[]) {
1234
+ const freeflowItem = findSettingsItem(items, "freeflow.enabled");
1235
+ const freeflowInactive = freeflowItem ? effectiveItemValue(freeflowItem) !== true : false;
1236
+ const skillsItem = findSettingsItem(items, "freeflow.skills.enabled");
1237
+ const skillsEnabled = skillsItem ? effectiveItemValue(skillsItem) === true : true;
1238
+ const routerEnabled = findSettingsItem(items, "outputRouter.enabled")?.value === true;
1239
+ const sessionModeItem = findSettingsItem(items, "freeflow.sessionMode");
1240
+ const defaultModeItem = findSettingsItem(items, "freeflow.defaultMode");
1241
+ const routerGroup = findSettingsItem(items, "outputRouter.group");
1242
+
1243
+ if (sessionModeItem) {
1244
+ const defaultMode = String(
1245
+ defaultModeItem ? effectiveItemValue(defaultModeItem) : (sessionModeItem.inheritedValue ?? "workflow"),
1246
+ );
1247
+ const defaultSource = defaultModeItem?.effectiveSource ?? sessionModeItem.inheritedSource ?? "builtin";
1248
+ sessionModeItem.inactive = freeflowInactive || !skillsEnabled;
1249
+ sessionModeItem.displaySuffix = sessionModeDisplaySuffix(
1250
+ String(sessionModeItem.value),
1251
+ defaultMode,
1252
+ defaultSource,
1253
+ freeflowInactive || !skillsEnabled,
1254
+ );
1255
+ sessionModeItem.valueDescriptions = {
1256
+ default: `${defaultMode} from ${defaultSource}`,
1257
+ ...MODE_DESCRIPTIONS,
1258
+ };
1259
+ }
1260
+
1261
+ if (routerGroup) {
1262
+ routerGroup.value = routerEnabled;
1263
+ routerGroup.inactive = freeflowInactive;
1264
+ }
1265
+
1266
+ walkSettingsItems(items, (candidate) => {
1267
+ if (candidate.id === "freeflow.session.reset") {
1268
+ candidate.inactive = false;
1269
+ } else if (candidate.configScope) {
1270
+ const inactive = candidate.id === "freeflow.enabled" ? false : freeflowInactive;
1271
+ const displayInactive = candidate.id === "freeflow.defaultMode" ? freeflowInactive || !skillsEnabled : inactive;
1272
+ candidate.inactive = inactive;
1273
+ candidate.displaySuffix = coreDisplaySuffix(candidate, displayInactive);
1274
+ } else if (candidate.id === "freeflow.sessionMode") {
1275
+ candidate.inactive = freeflowInactive || !skillsEnabled;
1276
+ } else if (candidate.id === "outputRouter.group") {
1277
+ candidate.inactive = freeflowInactive;
1278
+ } else if (candidate.id === "outputRouter.enabled") {
1279
+ candidate.inactive = freeflowInactive;
1280
+ } else if (candidate.id.startsWith("outputRouter.")) {
1281
+ candidate.inactive = freeflowInactive || !routerEnabled;
1282
+ } else {
1283
+ candidate.inactive = freeflowInactive;
1284
+ }
1285
+ });
1286
+ }
1287
+
1288
+ function settingsChoices(item: SettingsItem) {
1289
+ if (item.kind === "boolean") {
1290
+ return [
1291
+ { key: "true", value: true, label: "enabled", description: "Enable this setting." },
1292
+ { key: "false", value: false, label: "disabled", description: "Disable this setting." },
1293
+ ];
1294
+ }
1295
+ if (item.kind !== "enum") return undefined;
1296
+ return (item.values ?? []).map((value) => ({
1297
+ key: value,
1298
+ value,
1299
+ label: item.valueLabels?.[value] ?? value,
1300
+ description: item.valueDescriptions?.[value],
1301
+ }));
1302
+ }
1303
+
1304
+ function resetSessionItemState(items: SettingsItem[]) {
1305
+ walkSettingsItems(items, (item) => {
1306
+ if (item.configScope === "session") {
1307
+ item.value = LOCAL_INHERIT;
1308
+ item.effectiveValue = item.inheritedValue;
1309
+ item.effectiveSource = item.inheritedSource ?? "builtin";
1310
+ } else if (item.id === "freeflow.sessionMode") {
1311
+ item.value = "default";
1312
+ }
1313
+ });
1314
+ }
1315
+
1316
+ function settingsEntries(
1317
+ items: SettingsItem[],
1318
+ rootItems: SettingsItem[],
1319
+ onChange: OpenSettingsOptions["onChange"],
1320
+ ): SettingsEntry[] {
1321
+ return items.map((item) => ({
1322
+ id: item.id,
1323
+ label: item.label,
1324
+ description: item.description,
1325
+ currentValue: () => valueForDisplay(item),
1326
+ inactive: () => item.inactive === true,
1327
+ currentChoiceKey: () => String(item.value),
1328
+ choices: settingsChoices(item),
1329
+ children: item.children ? () => settingsEntries(item.children!, rootItems, onChange) : undefined,
1330
+ edit:
1331
+ !item.children && !["boolean", "enum", "group"].includes(item.kind)
1332
+ ? {
1333
+ initialValue: () => valueForDisplay(item),
1334
+ parse: (text: string) => (item.parse ? item.parse(text) : text),
1335
+ }
1336
+ : undefined,
1337
+ commit:
1338
+ item.kind === "group"
1339
+ ? undefined
1340
+ : async (value: unknown) => {
1341
+ if (valuesEqual(item.value, value)) return { changed: false, reloadRequired: false };
1342
+ const outcome = await onChange(item, value);
1343
+ if (outcome.changed) {
1344
+ updateScopedItemState(item, value);
1345
+ if (item.id === "freeflow.session.reset") {
1346
+ resetSessionItemState(rootItems);
1347
+ } else if (!item.transient) {
1348
+ item.value = value;
1349
+ }
1350
+ refreshSettingsDerivedState(rootItems);
1351
+ }
1352
+ return outcome;
1353
+ },
1354
+ }));
1355
+ }
1356
+
1357
+ async function openSettings(options: OpenSettingsOptions): Promise<SettingsSessionResult> {
1358
+ if (typeof options.ctx?.ui?.custom !== "function") {
1359
+ options.ctx?.ui?.notify?.(
1360
+ `${options.title} requires Pi TUI mode. Use the status command for a compact summary.`,
1361
+ "warning",
1362
+ );
1363
+ return { changed: false, configChanged: false, failed: false };
1364
+ }
1365
+
1366
+ const entries = settingsEntries(options.items, options.items, options.onChange);
1367
+ const initialChoice = options.initialChoice
1368
+ ? entries.find((entry) => entry.id === options.initialChoice?.id)
1369
+ : undefined;
1370
+ let component: PiSettingsComponent | undefined;
1371
+ await options.ctx.ui.custom((tui: any, theme: any, _keybindings: unknown, done: (value?: undefined) => void) => {
1372
+ component = new PiSettingsComponent({
1373
+ title: options.title,
1374
+ entries,
1375
+ initialChoice,
1376
+ theme: theme ?? {},
1377
+ requestRender: () => tui.requestRender(),
1378
+ notify: (message, level) => options.ctx?.ui?.notify?.(message, level),
1379
+ done,
1380
+ });
1381
+ return component;
1382
+ });
1383
+
1384
+ await component?.waitForWrites();
1385
+ return component?.sessionResult() ?? { changed: false, configChanged: false, failed: false };
1386
+ }
1387
+
1388
+ function outputRouterStatusText(rawConfig: Record<string, unknown>): string {
1389
+ const settingsConfig = normalizedSettingsConfig(rawConfig);
1390
+ const actual = normalizeFreeflowConfig(rawConfig).config;
1391
+ const freeflowEnabled = rawConfig.enabled !== false;
1392
+ const router = actual.outputRouter;
1393
+ const configured = settingsConfig;
1394
+ const generatedPaths = router.hints?.generatedPathGlobs?.length ?? 0;
1395
+ const noisyHints = router.hints?.noisyCommandPatterns?.length ?? 0;
1396
+ return [
1397
+ `Output Router: ${freeflowEnabled && router.enabled ? "enabled" : "disabled"}${freeflowEnabled ? "" : " (inactive: Freeflow off)"}`,
1398
+ `script transform: ${freeflowEnabled && configured.scriptTransform.enabled ? "enabled" : "disabled"}${freeflowEnabled && router.enabled ? "" : " (inactive)"}`,
1399
+ `observed routing: ${freeflowEnabled && configured.observedRouting.enabled ? "enabled" : "disabled"}${freeflowEnabled && router.enabled ? "" : " (inactive)"}`,
1400
+ `native safety net: ${freeflowEnabled ? router.postToolRouting : "off"}`,
1401
+ `storage: ${router.storagePolicy}`,
1402
+ `generated paths: ${generatedPaths}`,
1403
+ `noisy command hints: ${noisyHints}`,
1404
+ ].join("; ");
1405
+ }
1406
+
1407
+ function freeflowStatusText(state: Awaited<ReturnType<typeof readCapabilityState>>): string {
1408
+ if (!state.configured) {
1409
+ return state.configExists
1410
+ ? `Freeflow: inactive (invalid config: ${state.parseError ?? "unknown parse error"}); run /setup-freeflow or fix .freeflow/config.json`
1411
+ : "Freeflow: inactive (repo not set up); run /setup-freeflow";
1412
+ }
1413
+ const sessionSuffix = (source: ConfigSource) => (source === "session" ? " (session override)" : "");
1414
+ return [
1415
+ `Freeflow: ${state.enabled ? "enabled" : "disabled"}${sessionSuffix(state.configSources.enabled as ConfigSource)}`,
1416
+ `interaction contract: ${state.interactionContract.effective ? "enabled" : "disabled"}${sessionSuffix(
1417
+ state.configSources.interactionContract as ConfigSource,
1418
+ )}`,
1419
+ `skills: ${state.skills.effective ? "enabled" : "disabled (workflow modes inactive)"}${sessionSuffix(
1420
+ state.configSources.skillsEnabled as ConfigSource,
1421
+ )}`,
1422
+ `output router: ${state.outputRouter.enabled ? "enabled" : "disabled"}`,
1423
+ ].join("; ");
1424
+ }
1425
+
1426
+ const NON_TUI_SETTINGS_GUIDANCE =
1427
+ "Freeflow settings require Pi TUI mode. Use /freeflow status to inspect current state; supported non-TUI changes are /freeflow enable, /freeflow disable, and /freeflow mode <conversation|workflow|strict-workflow|reset>.";
1428
+ const NON_TUI_MODE_GUIDANCE =
1429
+ "Freeflow mode selector requires Pi TUI mode. Use /freeflow mode conversation, /freeflow mode workflow, /freeflow mode strict-workflow, or /freeflow mode reset.";
1430
+
1431
+ function nonTuiGuidance(ctx: any, message: string) {
1432
+ if (ctx?.hasUI === false) throw new Error(message);
1433
+ ctx?.ui?.notify?.(message, "warning");
1434
+ return { changed: false, reloaded: false, error: "tui_required" };
1435
+ }
1436
+
1437
+ async function finalizeSettingsSession(
1438
+ session: SettingsSessionResult,
1439
+ ctx: any,
1440
+ afterChange: (changed: boolean) => Promise<void> | void,
1441
+ savedMessage: string,
1442
+ reloadWarning = "Run /reload for Freeflow changes to fully apply.",
1443
+ ) {
1444
+ if (!session.configChanged) return false;
1445
+
1446
+ await afterChange(true);
1447
+ if (session.failed) return false;
1448
+
1449
+ ctx.ui.notify(savedMessage, "info");
1450
+ if (typeof ctx.reload !== "function") {
1451
+ ctx.ui.notify(reloadWarning, "warning");
1452
+ return false;
1453
+ }
1454
+
1455
+ await ctx.reload();
1456
+ return true;
1457
+ }
1458
+
1459
+ async function finalizeSessionSettings(
1460
+ session: SettingsSessionResult,
1461
+ ctx: any,
1462
+ afterChange: (changed: boolean) => Promise<void> | void,
1463
+ ) {
1464
+ if (!session.changed) return false;
1465
+
1466
+ await afterChange(true);
1467
+ if (session.failed) return false;
1468
+ if (!session.configChanged) {
1469
+ ctx.ui.notify("Freeflow session overrides updated.", "info");
1470
+ return false;
1471
+ }
1472
+
1473
+ ctx.ui.notify("Freeflow session overrides updated. Reloading skills and resources...", "info");
1474
+ if (typeof ctx.reload !== "function") {
1475
+ ctx.ui.notify("Run /reload for Freeflow session overrides to fully apply.", "warning");
1476
+ return false;
1477
+ }
1478
+
1479
+ await ctx.reload();
1480
+ return true;
1481
+ }
1482
+
1483
+ function actionIsMutation(action: string): boolean {
1484
+ return action === "" || action === "settings" || ["enable", "on", "true", "disable", "off", "false"].includes(action);
1485
+ }
1486
+
1487
+ async function maybeBlockLayerMutation(action: string, ctx: any, layerName: string) {
1488
+ if (!actionIsMutation(action)) return false;
1489
+ const state = await readCapabilityState(ctx.cwd);
1490
+ if (!state.configured) {
1491
+ ctx.ui.notify(
1492
+ `Freeflow is installed but this repo is not set up. Run /setup-freeflow before configuring ${layerName}.`,
1493
+ "warning",
1494
+ );
1495
+ return true;
1496
+ }
1497
+ if (!state.enabled) {
1498
+ ctx.ui.notify(
1499
+ `Freeflow is disabled for this repo. Use /freeflow enable before configuring ${layerName}.`,
1500
+ "warning",
1501
+ );
1502
+ return true;
1503
+ }
1504
+ return false;
1505
+ }
1506
+
1507
+ export async function handleFreeflowCommand(
1508
+ args: string | undefined,
1509
+ ctx: any,
1510
+ afterChange: (changed: boolean) => Promise<void> | void,
1511
+ pi: any,
1512
+ ) {
1513
+ const input = (args ?? "settings").trim().toLowerCase() || "settings";
1514
+ const [action, ...rest] = input.split(/\s+/);
1515
+ const actionValue = rest.join(" ");
1516
+ const nonTui = ctx?.mode && ctx.mode !== "tui";
1517
+ if (nonTui && action === "mode" && !actionValue) {
1518
+ return nonTuiGuidance(ctx, NON_TUI_MODE_GUIDANCE);
1519
+ }
1520
+ const settingsSelector =
1521
+ action === "settings" &&
1522
+ (!actionValue || ["session", "local", "personal", "repo", "repository", "shared"].includes(actionValue));
1523
+ if (nonTui && settingsSelector) {
1524
+ return nonTuiGuidance(ctx, NON_TUI_SETTINGS_GUIDANCE);
1525
+ }
1526
+
1527
+ const [layers, state, modeState] = await Promise.all([
1528
+ readFreeflowConfigLayers(ctx.cwd),
1529
+ readCapabilityState(ctx.cwd),
1530
+ readModeState(ctx.cwd),
1531
+ ]);
1532
+ const configState = layers.repository;
1533
+ const raw = configState.valid ? configState.parsed : {};
1534
+
1535
+ if (action === "status") {
1536
+ ctx.ui.notify(freeflowStatusText(state), "info");
1537
+ return { changed: false, reloaded: false };
1538
+ }
1539
+
1540
+ if (!configState.valid) {
1541
+ ctx.ui.notify(freeflowStatusText(state), "warning");
1542
+ return { changed: false, reloaded: false, error: "not_configured" };
1543
+ }
1544
+ if (layers.local.exists && !layers.local.valid) {
1545
+ ctx.ui.notify(
1546
+ `.freeflow/local.json is invalid; repair or remove it before changing Freeflow settings. ${layers.local.parseError ?? ""}`.trim(),
1547
+ "warning",
1548
+ );
1549
+ return { changed: false, reloaded: false, error: "invalid_local_config" };
1550
+ }
1551
+
1552
+ if (action === "mode") {
1553
+ if (actionValue) {
1554
+ const result = await handleModeCommand(actionValue, ctx, pi);
1555
+ return { changed: result.changed, reloaded: false, error: result.error };
1556
+ }
1557
+ if (!state.enabled || !state.skills.effective) {
1558
+ const result = await handleModeCommand(undefined, ctx, pi);
1559
+ return { changed: false, reloaded: false, error: result.error };
1560
+ }
1561
+ if (typeof ctx?.ui?.custom !== "function") {
1562
+ const result = await handleModeCommand(undefined, ctx, pi);
1563
+ return { changed: false, reloaded: false, error: result.error };
1564
+ }
1565
+
1566
+ const item = freeflowItems(raw, modeState, {
1567
+ scope: "local",
1568
+ layers,
1569
+ }).find((candidate) => candidate.id === "freeflow.sessionMode")!;
1570
+ const session = await openSettings({
1571
+ title: "Freeflow Mode",
1572
+ items: [item],
1573
+ initialChoice: item,
1574
+ ctx,
1575
+ onChange: async (_item, value) => {
1576
+ const result = await handleModeCommand(String(value), ctx, pi);
1577
+ return { changed: result.changed, reloadRequired: false };
1578
+ },
1579
+ });
1580
+ return { changed: session.changed, reloaded: false, error: session.failed ? "write_failed" : undefined };
1581
+ }
1582
+
1583
+ if (["enable", "on", "true", "disable", "off", "false"].includes(action)) {
1584
+ const enabled = ["enable", "on", "true"].includes(action);
1585
+ const item = freeflowItems(raw, modeState, {
1586
+ scope: "repository",
1587
+ layers,
1588
+ }).find((candidate) => candidate.id === "freeflow.enabled")!;
1589
+ await updateConfig(ctx.cwd, item, enabled, "repository");
1590
+ await afterChange(true);
1591
+ ctx.ui.notify(`Freeflow ${enabled ? "enabled" : "disabled"}. Reloading Freeflow runtime...`, "info");
1592
+ if (typeof ctx.reload === "function") {
1593
+ await ctx.reload();
1594
+ return { changed: true, reloaded: true };
1595
+ }
1596
+ ctx.ui.notify("Run /reload for Freeflow changes to fully apply.", "warning");
1597
+ return { changed: true, reloaded: false };
1598
+ }
1599
+
1600
+ if (action && action !== "settings") {
1601
+ ctx.ui.notify(
1602
+ "Usage: /freeflow, /freeflow settings [local|repo], /freeflow status, /freeflow mode [conversation|workflow|strict-workflow|reset], /freeflow enable, or /freeflow disable",
1603
+ "warning",
1604
+ );
1605
+ return { changed: false, reloaded: false, error: "invalid_action" };
1606
+ }
1607
+
1608
+ let settingsScope: ConfigScope = "local";
1609
+ if (actionValue === "session") {
1610
+ settingsScope = "session";
1611
+ } else if (["repo", "repository", "shared"].includes(actionValue)) {
1612
+ settingsScope = "repository";
1613
+ } else if (actionValue && !["local", "personal"].includes(actionValue)) {
1614
+ ctx.ui.notify(
1615
+ "Usage: /freeflow settings, /freeflow settings session, /freeflow settings local, or /freeflow settings repo",
1616
+ "warning",
1617
+ );
1618
+ return { changed: false, reloaded: false, error: "invalid_scope" };
1619
+ }
1620
+
1621
+ const items =
1622
+ settingsScope === "session"
1623
+ ? sessionFreeflowItems(state, modeState)
1624
+ : freeflowItems(raw, modeState, { scope: settingsScope, layers });
1625
+ const session = await openSettings({
1626
+ title:
1627
+ settingsScope === "session"
1628
+ ? "Freeflow Settings · Session overrides"
1629
+ : settingsScope === "local"
1630
+ ? "Freeflow Settings · Personal overrides"
1631
+ : "Freeflow Repository Settings · modifies .freeflow/config.json",
1632
+ items,
1633
+ ctx,
1634
+ onChange: async (item, value) => {
1635
+ if (item.id === "freeflow.sessionMode") {
1636
+ const result = await handleModeCommand(String(value), ctx, pi);
1637
+ return { changed: result.changed, reloadRequired: false };
1638
+ }
1639
+ if (item.id === "freeflow.session.reset") {
1640
+ const result = await resetSessionOverrides(ctx, pi);
1641
+ return { changed: result.changed, reloadRequired: result.reloadRequired };
1642
+ }
1643
+ if (item.configScope === "session") {
1644
+ const keyById = {
1645
+ "freeflow.enabled": "enabled",
1646
+ "freeflow.interactionContract": "interactionContract",
1647
+ "freeflow.skills.enabled": "skillsEnabled",
1648
+ } as const;
1649
+ const key = keyById[item.id as keyof typeof keyById];
1650
+ const override = value === LOCAL_INHERIT ? null : value === "true";
1651
+ const result = await setSessionCoreOverride(key, override, ctx, pi);
1652
+ return { changed: result.changed, reloadRequired: result.reloadRequired === true };
1653
+ }
1654
+ await updateConfig(ctx.cwd, item, value, item.configScope ?? "repository");
1655
+ return { changed: true, reloadRequired: true };
1656
+ },
1657
+ });
1658
+
1659
+ if (settingsScope === "session") {
1660
+ const reloaded = await finalizeSessionSettings(session, ctx, afterChange);
1661
+ return { changed: session.changed, reloaded, error: session.failed ? "write_failed" : undefined };
1662
+ }
1663
+
1664
+ const savedTarget = settingsScope === "local" ? "personal overrides" : "repository settings";
1665
+ const reloaded = await finalizeSettingsSession(
1666
+ session,
1667
+ ctx,
1668
+ afterChange,
1669
+ `Freeflow ${savedTarget} saved. Reloading Freeflow runtime...`,
1670
+ );
1671
+ return { changed: session.changed, reloaded, error: session.failed ? "write_failed" : undefined };
1672
+ }
1673
+
1674
+ export async function handleOutputRouterCommand(
1675
+ args: string | undefined,
1676
+ ctx: any,
1677
+ afterChange: (changed: boolean) => Promise<void> | void,
1678
+ ) {
1679
+ const action = (args ?? "").trim().toLowerCase();
1680
+ const raw = await readFreeflowConfig(ctx.cwd);
1681
+
1682
+ if (await maybeBlockLayerMutation(action, ctx, "Output Router")) {
1683
+ return { changed: false, reloaded: false, error: "freeflow_inactive" };
1684
+ }
1685
+
1686
+ if (action === "status") {
1687
+ const state = await readCapabilityState(ctx.cwd);
1688
+ const prefix = !state.configured || !state.enabled ? `${freeflowStatusText(state)}; ` : "";
1689
+ ctx.ui.notify(`${prefix}${outputRouterStatusText(raw)}`, !state.configured || !state.enabled ? "warning" : "info");
1690
+ return { changed: false, reloaded: false };
1691
+ }
1692
+
1693
+ if (["enable", "on", "true", "disable", "off", "false"].includes(action)) {
1694
+ const enabled = ["enable", "on", "true"].includes(action);
1695
+ const item = outputRouterItems(raw).find((candidate) => candidate.id === "outputRouter.enabled")!;
1696
+ await updateConfig(ctx.cwd, item, enabled);
1697
+ await afterChange(true);
1698
+ ctx.ui.notify(`Output Router ${enabled ? "enabled" : "disabled"}. Reloading Freeflow runtime...`, "info");
1699
+ if (typeof ctx.reload === "function") {
1700
+ await ctx.reload();
1701
+ return { changed: true, reloaded: true };
1702
+ }
1703
+ ctx.ui.notify("Run /reload for Output Router changes to fully apply.", "warning");
1704
+ return { changed: true, reloaded: false };
1705
+ }
1706
+
1707
+ if (action && action !== "settings") {
1708
+ ctx.ui.notify("Usage: /output-router, /output-router settings, or /output-router status", "warning");
1709
+ return { changed: false, reloaded: false, error: "invalid_action" };
1710
+ }
1711
+
1712
+ const session = await openSettings({
1713
+ title: "Output Router Settings",
1714
+ items: outputRouterItems(raw),
1715
+ ctx,
1716
+ onChange: async (item, value) => {
1717
+ await updateConfig(ctx.cwd, item, value);
1718
+ return { changed: true, reloadRequired: true };
1719
+ },
1720
+ });
1721
+
1722
+ const reloaded = await finalizeSettingsSession(
1723
+ session,
1724
+ ctx,
1725
+ afterChange,
1726
+ "Output Router settings saved. Reloading Freeflow runtime...",
1727
+ "Run /reload for Output Router changes to fully apply.",
1728
+ );
1729
+ return { changed: session.changed, reloaded, error: session.failed ? "write_failed" : undefined };
1730
+ }