@hassangameryt/freeflow 0.4.0 → 0.6.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 (285) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -1
  3. package/.codex-plugin/plugin.json +2 -1
  4. package/README.md +150 -155
  5. package/capabilities/cognitive-routing/SKILL.md +229 -0
  6. package/capabilities/context-virtualization/SKILL.md +103 -0
  7. package/capabilities/conversation-history/SKILL.md +53 -0
  8. package/command-surface.json +17 -13
  9. package/hooks/freeflow-runtime-context.mjs +393 -159
  10. package/hooks/hooks.json +26 -1
  11. package/hooks/tests/check-runtime-context-hook.sh +223 -54
  12. package/package.json +25 -16
  13. package/pi-extension/dist/cognitive-routing/commands.js +96 -0
  14. package/pi-extension/dist/cognitive-routing/config.js +236 -0
  15. package/pi-extension/dist/cognitive-routing/controller.js +862 -0
  16. package/pi-extension/dist/cognitive-routing/history.js +439 -0
  17. package/pi-extension/dist/cognitive-routing/host.js +126 -0
  18. package/pi-extension/dist/cognitive-routing/runtime.js +43 -0
  19. package/pi-extension/dist/cognitive-routing/tool.js +211 -0
  20. package/pi-extension/dist/cognitive-routing/types.js +6 -0
  21. package/pi-extension/dist/context-virtualization/commands.js +89 -0
  22. package/pi-extension/dist/context-virtualization/projector.js +42 -0
  23. package/pi-extension/dist/context-virtualization/runtime.js +356 -0
  24. package/pi-extension/dist/context-virtualization/state.js +57 -0
  25. package/pi-extension/dist/context-virtualization/types.js +10 -0
  26. package/pi-extension/dist/conversation-history/passages.js +66 -0
  27. package/pi-extension/dist/conversation-history/ranking.js +105 -0
  28. package/pi-extension/dist/conversation-history/runtime.js +226 -0
  29. package/pi-extension/dist/conversation-history/search.js +56 -0
  30. package/pi-extension/dist/conversation-history/types.js +1 -0
  31. package/pi-extension/dist/freeflow-context/resolver.js +120 -0
  32. package/pi-extension/dist/freeflow-context/runtime.js +80 -0
  33. package/pi-extension/dist/freeflow-context/source-projector.js +95 -0
  34. package/pi-extension/dist/freeflow-context/tool.js +308 -0
  35. package/pi-extension/dist/freeflow-context/types.js +12 -0
  36. package/pi-extension/dist/freeflow-context/visibility.js +28 -0
  37. package/pi-extension/dist/index.js +516 -98
  38. package/pi-extension/dist/runtime/runtime-context.js +318 -271
  39. package/pi-extension/dist/runtime/runtime-identity.js +5 -0
  40. package/pi-extension/dist/settings/settings-tui.js +78 -0
  41. package/pi-extension/dist/settings/settings-ui.js +593 -563
  42. package/pi-extension/dist/ui/text-component.js +57 -0
  43. package/pi-extension/src/cognitive-routing/commands.ts +119 -0
  44. package/pi-extension/src/cognitive-routing/config.ts +307 -0
  45. package/pi-extension/src/cognitive-routing/controller.ts +1118 -0
  46. package/pi-extension/src/cognitive-routing/history.ts +638 -0
  47. package/pi-extension/src/cognitive-routing/host.ts +172 -0
  48. package/pi-extension/src/cognitive-routing/runtime.ts +57 -0
  49. package/pi-extension/src/cognitive-routing/tool.ts +272 -0
  50. package/pi-extension/src/cognitive-routing/types.ts +89 -0
  51. package/pi-extension/src/context-virtualization/commands.ts +111 -0
  52. package/pi-extension/src/context-virtualization/projector.ts +62 -0
  53. package/pi-extension/src/context-virtualization/runtime.ts +406 -0
  54. package/pi-extension/src/context-virtualization/state.ts +79 -0
  55. package/pi-extension/src/context-virtualization/types.ts +92 -0
  56. package/pi-extension/src/conversation-history/passages.ts +81 -0
  57. package/pi-extension/src/conversation-history/ranking.ts +155 -0
  58. package/pi-extension/src/conversation-history/runtime.ts +250 -0
  59. package/pi-extension/src/conversation-history/search.ts +79 -0
  60. package/pi-extension/src/conversation-history/types.ts +31 -0
  61. package/pi-extension/src/freeflow-context/resolver.ts +139 -0
  62. package/pi-extension/src/freeflow-context/runtime.ts +100 -0
  63. package/pi-extension/src/freeflow-context/source-projector.ts +106 -0
  64. package/pi-extension/src/freeflow-context/tool.ts +360 -0
  65. package/pi-extension/src/freeflow-context/types.ts +70 -0
  66. package/pi-extension/src/freeflow-context/visibility.ts +53 -0
  67. package/pi-extension/src/index.ts +553 -101
  68. package/pi-extension/src/runtime/runtime-context.ts +339 -281
  69. package/pi-extension/src/runtime/runtime-identity.ts +8 -0
  70. package/pi-extension/src/settings/settings-tui.ts +93 -0
  71. package/pi-extension/src/settings/settings-ui.ts +700 -613
  72. package/pi-extension/src/ui/text-component.ts +66 -0
  73. package/pi-extension/tests/cognitive-routing/commands.test.js +134 -0
  74. package/pi-extension/tests/cognitive-routing/controller.test.js +1099 -0
  75. package/pi-extension/tests/cognitive-routing/extension-lifecycle.test.js +1071 -0
  76. package/pi-extension/tests/cognitive-routing/history.test.js +274 -0
  77. package/pi-extension/tests/cognitive-routing/host-fixture.js +4 -0
  78. package/pi-extension/tests/cognitive-routing/host-identity.test.js +63 -0
  79. package/pi-extension/tests/cognitive-routing/profile-resolution.test.js +236 -0
  80. package/pi-extension/tests/cognitive-routing/tool.test.js +230 -0
  81. package/pi-extension/tests/context-virtualization/extension.test.js +749 -0
  82. package/pi-extension/tests/context-virtualization/projector.test.js +110 -0
  83. package/pi-extension/tests/context-virtualization/runtime.test.js +216 -0
  84. package/pi-extension/tests/conversation-history/runtime.test.js +98 -0
  85. package/pi-extension/tests/conversation-history/search.test.js +97 -0
  86. package/pi-extension/tests/integration/pi-extension.test.js +928 -2047
  87. package/pi-extension/tests/integration/prompt-architecture.test.js +305 -0
  88. package/runtime/prompts/cognitive-routing.md +9 -0
  89. package/runtime/prompts/context-virtualization.md +3 -0
  90. package/runtime/prompts/conversation-history.md +3 -0
  91. package/runtime/prompts/core.md +13 -0
  92. package/runtime/prompts/interaction-contract.md +7 -0
  93. package/runtime/prompts/skills.md +65 -0
  94. package/skills/action-selection/SKILL.md +91 -0
  95. package/skills/bypass/SKILL.md +3 -2
  96. package/skills/commit-work/SKILL.md +1 -1
  97. package/skills/commit-work/references/staging-decisions.md +1 -1
  98. package/skills/decision-gate/SKILL.md +1 -1
  99. package/skills/design-for-depth/SKILL.md +14 -33
  100. package/skills/design-for-depth/references/design-pressure-signals.md +1 -1
  101. package/skills/design-for-depth/references/interface-design-loop.md +1 -1
  102. package/skills/design-for-depth/references/software-design-philosophy.md +1 -1
  103. package/skills/diagnose-failure/SKILL.md +20 -14
  104. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +1 -3
  105. package/skills/diagnose-failure/references/{flaky-and-performance.md → flaky-and-performance-diagnosis.md} +1 -1
  106. package/skills/discuss/SKILL.md +34 -22
  107. package/skills/discuss/references/discussion-continuity.md +71 -0
  108. package/skills/evaluate-skill/SKILL.md +5 -5
  109. package/skills/evaluate-skill/references/{definition-schema.md → evaluation-definition-schema.md} +42 -2
  110. package/skills/evaluate-skill/references/execution-and-evidence.md +8 -3
  111. package/skills/evaluate-skill/scripts/lib/definitions.mjs +114 -3
  112. package/skills/evaluate-skill/scripts/lib/grade-interaction.mjs +156 -1
  113. package/skills/evaluate-skill/scripts/lib/grade.mjs +9 -1
  114. package/skills/evaluate-skill/scripts/lib/pi.mjs +147 -32
  115. package/skills/evaluate-skill/scripts/lib/rpc.mjs +4 -4
  116. package/skills/evaluate-skill/scripts/lib/runner.mjs +36 -14
  117. package/skills/evaluate-skill/scripts/lib/sandbox.mjs +92 -0
  118. package/skills/evaluate-skill/scripts/pi-guard.mjs +1 -1
  119. package/skills/evaluate-skill/scripts/pi-observer.mjs +66 -0
  120. package/skills/evaluate-skill/scripts/skill-eval.mjs +6 -5
  121. package/skills/execute-work/SKILL.md +18 -15
  122. package/skills/execute-work/references/code-practices.md +1 -1
  123. package/skills/execute-work/references/execute-work-edges.md +42 -0
  124. package/skills/finish-branch/SKILL.md +2 -2
  125. package/skills/finish-branch/references/integration-options.md +1 -1
  126. package/skills/handoff/SKILL.md +3 -3
  127. package/skills/launch-work/SKILL.md +1 -1
  128. package/skills/launch-work/references/launch-readiness.md +2 -2
  129. package/skills/migration-work/SKILL.md +2 -2
  130. package/skills/migration-work/references/migration-lifecycle.md +1 -1
  131. package/skills/mode-contract/SKILL.md +32 -10
  132. package/skills/release-work/SKILL.md +1 -1
  133. package/skills/release-work/references/release-evidence.md +1 -1
  134. package/skills/review-artifact/SKILL.md +28 -26
  135. package/skills/review-work/SKILL.md +28 -26
  136. package/skills/review-work/references/{reviewer-prompt.md → independent-work-reviewer-contract.md} +1 -1
  137. package/skills/review-work/references/security-risk-lens.md +1 -1
  138. package/skills/setup-freeflow/SKILL.md +6 -10
  139. package/skills/setup-freeflow/references/activation-contract.md +5 -10
  140. package/skills/setup-freeflow/references/host-setup.md +3 -7
  141. package/skills/simplify-code/SKILL.md +1 -1
  142. package/skills/simplify-code/references/simplification-patterns.md +1 -1
  143. package/skills/tdd/SKILL.md +18 -38
  144. package/skills/tdd/references/test-design.md +2 -2
  145. package/skills/track-work/SKILL.md +362 -58
  146. package/skills/track-work/scripts/lib/codec.mjs +570 -0
  147. package/skills/track-work/scripts/lib/commands.mjs +568 -0
  148. package/skills/track-work/scripts/lib/contract.mjs +303 -0
  149. package/skills/track-work/scripts/lib/model.mjs +506 -0
  150. package/skills/track-work/scripts/lib/operations.mjs +965 -0
  151. package/skills/track-work/scripts/lib/record-store.mjs +264 -0
  152. package/skills/track-work/scripts/lib/result.mjs +37 -0
  153. package/skills/track-work/scripts/lib/rewrites.mjs +740 -0
  154. package/skills/track-work/scripts/lib/schema.mjs +277 -0
  155. package/skills/track-work/scripts/lib/source-inventory.mjs +162 -0
  156. package/skills/track-work/scripts/lib/views.mjs +443 -0
  157. package/skills/track-work/scripts/lib/workspace.mjs +134 -0
  158. package/skills/track-work/scripts/working-record.mjs +122 -0
  159. package/skills/verify-work/SKILL.md +14 -2
  160. package/skills/verify-work/references/browser-runtime-evidence.md +1 -1
  161. package/skills/verify-work/references/integration-evidence.md +1 -1
  162. package/skills/verify-work/references/performance-evidence.md +1 -1
  163. package/skills/workflow/SKILL.md +105 -53
  164. package/skills/workflow/references/domain-skill-composition.md +22 -42
  165. package/skills/write-plan/SKILL.md +1 -1
  166. package/skills/write-skill/SKILL.md +3 -2
  167. package/skills/write-skill/references/progressive-disclosure.md +17 -3
  168. package/pi-extension/dist/output-router/host-producer-identification.js +0 -232
  169. package/pi-extension/dist/output-router/native-safety-net.js +0 -216
  170. package/pi-extension/dist/output-router/observed-tool-routing.js +0 -122
  171. package/pi-extension/dist/output-router/renderers.js +0 -1023
  172. package/pi-extension/dist/output-router/router-tools.js +0 -554
  173. package/pi-extension/dist/output-router/schemas.js +0 -383
  174. package/pi-extension/dist/output-router/status.js +0 -744
  175. package/pi-extension/dist/output-router/utils.js +0 -502
  176. package/pi-extension/src/output-router/host-producer-identification.ts +0 -256
  177. package/pi-extension/src/output-router/native-safety-net.ts +0 -248
  178. package/pi-extension/src/output-router/observed-tool-routing.ts +0 -134
  179. package/pi-extension/src/output-router/renderers.ts +0 -1108
  180. package/pi-extension/src/output-router/router-tools.ts +0 -579
  181. package/pi-extension/src/output-router/schemas.ts +0 -387
  182. package/pi-extension/src/output-router/status.ts +0 -791
  183. package/pi-extension/src/output-router/utils.ts +0 -554
  184. package/pi-extension/tests/output-router/observed-producers.test.js +0 -132
  185. package/pi-extension/tests/output-router/search.test.js +0 -722
  186. package/router/dist/benchmarks/benchmark-harness.d.ts +0 -35
  187. package/router/dist/benchmarks/benchmark-harness.js +0 -92
  188. package/router/dist/benchmarks/benchmarks.d.ts +0 -109
  189. package/router/dist/benchmarks/benchmarks.js +0 -979
  190. package/router/dist/benchmarks/codex-qa-benchmarks.d.ts +0 -95
  191. package/router/dist/benchmarks/codex-qa-benchmarks.js +0 -500
  192. package/router/dist/benchmarks/command-benchmarks.d.ts +0 -143
  193. package/router/dist/benchmarks/command-benchmarks.js +0 -698
  194. package/router/dist/benchmarks/context-mode-normalized-benchmarks.d.ts +0 -82
  195. package/router/dist/benchmarks/context-mode-normalized-benchmarks.js +0 -773
  196. package/router/dist/benchmarks/context-mode-real-deep-benchmark.d.ts +0 -132
  197. package/router/dist/benchmarks/context-mode-real-deep-benchmark.js +0 -1380
  198. package/router/dist/benchmarks/context-saving-baseline.d.ts +0 -60
  199. package/router/dist/benchmarks/context-saving-baseline.js +0 -470
  200. package/router/dist/benchmarks/index-benchmarks.d.ts +0 -103
  201. package/router/dist/benchmarks/index-benchmarks.js +0 -862
  202. package/router/dist/benchmarks/storage-policy-benchmarks.d.ts +0 -101
  203. package/router/dist/benchmarks/storage-policy-benchmarks.js +0 -618
  204. package/router/dist/benchmarks/vault-index-benchmarks.d.ts +0 -56
  205. package/router/dist/benchmarks/vault-index-benchmarks.js +0 -322
  206. package/router/dist/config/config.d.ts +0 -103
  207. package/router/dist/config/config.js +0 -632
  208. package/router/dist/config/router-contract.d.ts +0 -10
  209. package/router/dist/config/router-contract.js +0 -54
  210. package/router/dist/config/schema.d.ts +0 -27
  211. package/router/dist/config/schema.js +0 -648
  212. package/router/dist/config/types.d.ts +0 -573
  213. package/router/dist/config/types.js +0 -41
  214. package/router/dist/evidence/bounded-evidence.d.ts +0 -40
  215. package/router/dist/evidence/bounded-evidence.js +0 -323
  216. package/router/dist/evidence/evidence-range-selector.d.ts +0 -24
  217. package/router/dist/evidence/evidence-range-selector.js +0 -232
  218. package/router/dist/evidence/evidence-search.d.ts +0 -27
  219. package/router/dist/evidence/evidence-search.js +0 -490
  220. package/router/dist/evidence/evidence.d.ts +0 -38
  221. package/router/dist/evidence/evidence.js +0 -200
  222. package/router/dist/evidence/failure-contracts.d.ts +0 -37
  223. package/router/dist/evidence/failure-contracts.js +0 -166
  224. package/router/dist/evidence/line-ranges.d.ts +0 -24
  225. package/router/dist/evidence/line-ranges.js +0 -22
  226. package/router/dist/experiments/local-index.d.ts +0 -64
  227. package/router/dist/experiments/local-index.js +0 -581
  228. package/router/dist/index.d.ts +0 -30
  229. package/router/dist/index.js +0 -32
  230. package/router/dist/local/local-traversal.d.ts +0 -18
  231. package/router/dist/local/local-traversal.js +0 -334
  232. package/router/dist/processing/engine.d.ts +0 -132
  233. package/router/dist/processing/engine.js +0 -533
  234. package/router/dist/processing/reducers.d.ts +0 -280
  235. package/router/dist/processing/reducers.js +0 -1673
  236. package/router/dist/processing/renderers.d.ts +0 -31
  237. package/router/dist/processing/renderers.js +0 -195
  238. package/router/dist/processing/scripts.d.ts +0 -91
  239. package/router/dist/processing/scripts.js +0 -386
  240. package/router/dist/repo/repo-traversal.d.ts +0 -18
  241. package/router/dist/repo/repo-traversal.js +0 -245
  242. package/router/dist/routing/observed-reducers.d.ts +0 -41
  243. package/router/dist/routing/observed-reducers.js +0 -661
  244. package/router/dist/routing/observed-routing.d.ts +0 -26
  245. package/router/dist/routing/observed-routing.js +0 -378
  246. package/router/dist/routing/parsers.d.ts +0 -16
  247. package/router/dist/routing/parsers.js +0 -521
  248. package/router/dist/routing/run-filters.d.ts +0 -60
  249. package/router/dist/routing/run-filters.js +0 -238
  250. package/router/dist/routing/run-reducers.d.ts +0 -49
  251. package/router/dist/routing/run-reducers.js +0 -122
  252. package/router/dist/sandbox/adapter-roots.d.ts +0 -46
  253. package/router/dist/sandbox/adapter-roots.js +0 -66
  254. package/router/dist/sandbox/eryx-python-adapter.d.ts +0 -13
  255. package/router/dist/sandbox/eryx-python-adapter.js +0 -868
  256. package/router/dist/sandbox/jq-wasm-adapter.d.ts +0 -9
  257. package/router/dist/sandbox/jq-wasm-adapter.js +0 -429
  258. package/router/dist/sandbox/quickjs-wasi-adapter.d.ts +0 -13
  259. package/router/dist/sandbox/quickjs-wasi-adapter.js +0 -384
  260. package/router/dist/sandbox/script-sandbox.d.ts +0 -134
  261. package/router/dist/sandbox/script-sandbox.js +0 -337
  262. package/router/dist/setup/script-transform-adapters.d.ts +0 -32
  263. package/router/dist/setup/script-transform-adapters.js +0 -278
  264. package/router/dist/tools/batch.d.ts +0 -33
  265. package/router/dist/tools/batch.js +0 -586
  266. package/router/dist/tools/run.d.ts +0 -62
  267. package/router/dist/tools/run.js +0 -1304
  268. package/router/dist/tools/search.d.ts +0 -66
  269. package/router/dist/tools/search.js +0 -1364
  270. package/router/dist/transform/engine.d.ts +0 -168
  271. package/router/dist/transform/engine.js +0 -2072
  272. package/router/dist/vault/vault-index.d.ts +0 -125
  273. package/router/dist/vault/vault-index.js +0 -416
  274. package/router/dist/vault/vault.d.ts +0 -138
  275. package/router/dist/vault/vault.js +0 -630
  276. package/runtime/interaction-contract.md +0 -16
  277. package/skills/discuss/references/checkpoints.md +0 -66
  278. package/skills/execute-work/references/execution-loop.md +0 -77
  279. package/skills/output-router/SKILL.md +0 -206
  280. package/skills/output-router/references/safety-policy.md +0 -40
  281. package/skills/setup-freeflow/references/output-router-setup.md +0 -110
  282. package/skills/track-work/references/working-record-schema.md +0 -378
  283. package/skills/workflow/references/workflow-loop.md +0 -92
  284. /package/skills/handoff/references/{templates.md → handoff-templates.md} +0 -0
  285. /package/skills/review-artifact/references/{reviewer-prompt.md → independent-artifact-reviewer-contract.md} +0 -0
package/hooks/hooks.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "description": "Load Freeflow runtime context at session start.",
2
+ "description": "Load Freeflow runtime context and manage session mode.",
3
3
  "hooks": {
4
4
  "SessionStart": [
5
5
  {
@@ -13,6 +13,31 @@
13
13
  }
14
14
  ]
15
15
  }
16
+ ],
17
+ "UserPromptSubmit": [
18
+ {
19
+ "hooks": [
20
+ {
21
+ "type": "command",
22
+ "command": "node \"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT}}/hooks/freeflow-runtime-context.mjs\" UserPromptSubmit",
23
+ "timeout": 5,
24
+ "statusMessage": "Checking Freeflow mode"
25
+ }
26
+ ]
27
+ }
28
+ ],
29
+ "SessionEnd": [
30
+ {
31
+ "matcher": "clear",
32
+ "hooks": [
33
+ {
34
+ "type": "command",
35
+ "command": "node \"${CLAUDE_PLUGIN_ROOT:-${PLUGIN_ROOT}}/hooks/freeflow-runtime-context.mjs\" SessionEnd",
36
+ "timeout": 3,
37
+ "statusMessage": "Preserving Freeflow mode"
38
+ }
39
+ ]
40
+ }
16
41
  ]
17
42
  }
18
43
  }
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env bash
2
2
  set -euo pipefail
3
3
 
4
- ROOT_DIR="$(CDPATH= cd -- "$(dirname -- "$0")/../.." && pwd)"
4
+ ROOT_DIR="$(CDPATH='' cd -- "$(dirname -- "$0")/../.." && pwd)"
5
5
  HOOK_PATH="$ROOT_DIR/hooks/freeflow-runtime-context.mjs"
6
6
  HOOKS_JSON="$ROOT_DIR/hooks/hooks.json"
7
- WORKFLOW_SKILL="$ROOT_DIR/skills/workflow/SKILL.md"
7
+ CORE_PROMPT="$ROOT_DIR/runtime/prompts/core.md"
8
+ INTERACTION_PROMPT="$ROOT_DIR/runtime/prompts/interaction-contract.md"
9
+ SKILLS_PROMPT="$ROOT_DIR/runtime/prompts/skills.md"
8
10
 
9
11
  fail() {
10
12
  echo "runtime-context hook check failed: $*" >&2
@@ -29,18 +31,20 @@ assert_not_contains() {
29
31
 
30
32
  [[ -f "$HOOK_PATH" ]] || fail "missing hook script"
31
33
  [[ -f "$HOOKS_JSON" ]] || fail "missing hooks manifest"
32
- [[ -f "$WORKFLOW_SKILL" ]] || fail "missing canonical Workflow skill"
33
- workflow_owner_line="$(grep -m1 '^The active agent owns ' "$WORKFLOW_SKILL" || true)"
34
- workflow_self_review_line="$(grep -m1 '^Self-review is silent ' "$WORKFLOW_SKILL" || true)"
35
- [[ -n "$workflow_owner_line" ]] || fail "canonical Workflow owner sentinel missing"
36
- [[ -n "$workflow_self_review_line" ]] || fail "canonical Workflow self-review sentinel missing"
34
+ for prompt_file in "$CORE_PROMPT" "$INTERACTION_PROMPT" "$SKILLS_PROMPT"; do
35
+ [[ -f "$prompt_file" ]] || fail "missing runtime prompt fragment: $prompt_file"
36
+ done
37
37
  node --check "$HOOK_PATH"
38
38
  node -e '
39
39
  const fs = require("fs");
40
40
  const hooks = JSON.parse(fs.readFileSync(process.argv[1], "utf8")).hooks || {};
41
41
  if (!Array.isArray(hooks.SessionStart) || hooks.SessionStart.length === 0) process.exit(1);
42
42
  if (hooks.SessionStart[0]?.matcher !== "startup|resume|clear|compact") process.exit(1);
43
- ' "$HOOKS_JSON" || fail "hooks.json does not register the expected SessionStart lifecycle matcher"
43
+ if (!Array.isArray(hooks.UserPromptSubmit) || hooks.UserPromptSubmit.length !== 1) process.exit(1);
44
+ if (hooks.UserPromptSubmit[0]?.matcher != null) process.exit(1);
45
+ if (!Array.isArray(hooks.SessionEnd) || hooks.SessionEnd.length !== 1) process.exit(1);
46
+ if (hooks.SessionEnd[0]?.matcher !== "clear") process.exit(1);
47
+ ' "$HOOKS_JSON" || fail "hooks.json does not register the expected non-overlapping runtime events"
44
48
 
45
49
  tmp_dir="$(mktemp -d)"
46
50
  trap 'rm -rf "$tmp_dir"' EXIT
@@ -57,45 +61,207 @@ user_prompt_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$tmp_dir" | node
57
61
  [[ -z "$user_prompt_output" ]] || fail "UserPromptSubmit should not duplicate session-start runtime context"
58
62
 
59
63
  for expected in \
60
- "# Freeflow Runtime Context" \
61
- 'Setup status: configured by `.freeflow/config.json`' \
62
- "Runtime delivery: confirmed for this lifecycle-hook invocation." \
64
+ '"hookEventName":"SessionStart"' \
65
+ "# Freeflow Stable Guidance" \
66
+ "## Mode" \
67
+ "## Shared Terms" \
68
+ "## Three Nested Loops" \
69
+ "## Workflow Cue" \
70
+ "## Action Selection Cue" \
71
+ "## Supported Exit" \
63
72
  "# Freeflow Interaction Contract" \
64
- "Answer questions without inferring action." \
65
- "# Freeflow Workflow Bootstrap" \
66
- "Use feedback to choose the smallest useful next action." \
67
- "$workflow_owner_line" \
68
- "$workflow_self_review_line" \
69
- 'Current Freeflow default mode: `workflow`.' \
73
+ "Treat questions, criticism, examples, hypotheses, and tentative ideas as" \
74
+ 'Configured default: `workflow` (repository)' \
75
+ 'Effective mode: `workflow` (configured default, active)' \
70
76
  'Interaction Contract: enabled' \
71
- 'Skills: enabled' \
72
- 'Output router: disabled'; do
77
+ 'Skills: enabled'; do
73
78
  assert_contains "$codex_output" "$expected" "Codex config-only context"
74
79
  done
80
+ assert_not_contains "$codex_output" "Output Router" "Codex host context"
81
+ assert_not_contains "$codex_output" "Output router:" "Codex host context"
82
+
83
+ # The Pi-only capability key is tolerated by shared host validators but is not
84
+ # injected into Codex or Claude runtime context.
85
+ cognitive_dir="$(mktemp -d)"
86
+ mkdir -p "$cognitive_dir/.freeflow"
87
+ printf '{"defaultMode":"workflow","cognitiveRouting":{"enabled":true,"profiles":{"standard":{"provider":"faux","model":"standard","thinkingLevel":"high"},"reasoning":{"provider":"faux","model":"reasoning","thinkingLevel":"max"}}}}\n' >"$cognitive_dir/.freeflow/config.json"
88
+ cognitive_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$cognitive_dir" | node "$HOOK_PATH" SessionStart)"
89
+ assert_contains "$cognitive_output" "# Freeflow Stable Guidance" "Pi-only capability tolerance"
90
+ assert_not_contains "$cognitive_output" "Cognitive Routing" "non-Pi capability isolation"
91
+ rm -rf "$cognitive_dir"
75
92
  for excluded_heading in \
76
93
  "# Freeflow Runtime Kernel" \
77
94
  "## Freeflow Runtime Priority" \
78
95
  "## Loaded Mode Contract Skill" \
79
96
  "## Loaded Workflow Skill" \
80
97
  "## Loaded Decision Gate Skill" \
98
+ "# Freeflow Workflow Bootstrap" \
81
99
  "## Discovery-light" \
82
100
  "## Loaded Output Router Skill"; do
83
101
  assert_not_contains "$codex_output" "$excluded_heading" "default context"
84
102
  done
85
- workflow_bootstrap_count="$(grep -Fo '# Freeflow Workflow Bootstrap' <<<"$codex_output" | wc -l | tr -d ' ')"
86
- [[ "$workflow_bootstrap_count" == "1" ]] || fail "Codex SessionStart should load Workflow exactly once"
103
+ skills_prompt_count="$(grep -Fo '## Shared Terms' <<<"$codex_output" | wc -l | tr -d ' ')"
104
+ [[ "$skills_prompt_count" == "1" ]] || fail "Codex SessionStart should load the Skills prompt exactly once"
87
105
  assert_not_contains "$codex_output" "## Conversation Mode Boundary" "workflow context"
88
106
  assert_not_contains "$codex_output" "## Strict Workflow Overlay" "workflow context"
89
107
 
90
108
  claude_output="$(printf '{"hook_event_name":"SessionStart","source":"startup","cwd":"%s"}\n' "$tmp_dir" | node "$HOOK_PATH" SessionStart)"
91
109
  assert_contains "$claude_output" '"hookEventName":"SessionStart"' "Claude wrapper"
92
110
  assert_contains "$claude_output" "# Freeflow Interaction Contract" "Claude config-only context"
93
- assert_contains "$claude_output" "# Freeflow Workflow Bootstrap" "Claude first-turn context"
94
- assert_contains "$claude_output" "Answer questions without inferring action." "Claude config-only context"
111
+ assert_contains "$claude_output" "## Shared Terms" "Claude skills context"
112
+ assert_not_contains "$claude_output" "# Freeflow Workflow Bootstrap" "Claude first-turn context"
113
+ assert_contains "$claude_output" "Treat questions, criticism, examples, hypotheses, and tentative ideas as" "Claude config-only context"
114
+
115
+ # Session mode changes are host-managed state: they apply before the same model request,
116
+ # survive lifecycle restoration, remain isolated by session id, and never edit repo config.
117
+ session_data_dir="$(mktemp -d)"
118
+ session_id="session/with unsafe path characters"
119
+ config_before="$(shasum -a 256 "$tmp_dir/.freeflow/config.json" | awk '{print $1}')"
120
+ mode_change_output="$(printf '{"hook_event_name":"UserPromptSubmit","session_id":"%s","cwd":"%s","prompt":"Switch to conversation mode."}\n' "$session_id" "$tmp_dir" | PLUGIN_DATA="$session_data_dir" node "$HOOK_PATH" UserPromptSubmit)"
121
+ assert_contains "$mode_change_output" '"hookEventName":"UserPromptSubmit"' "same-turn mode change wrapper"
122
+ assert_contains "$mode_change_output" '# Freeflow Runtime State' "same-turn mode change"
123
+ assert_contains "$mode_change_output" 'Session override: `conversation`' "same-turn mode change"
124
+ assert_contains "$mode_change_output" 'Effective mode: `conversation` (session override, active)' "same-turn mode change"
125
+ assert_not_contains "$mode_change_output" "# Freeflow Mode Update" "same-turn mode change"
126
+ assert_not_contains "$mode_change_output" "## Conversation Mode Boundary" "same-turn mode change"
127
+ assert_not_contains "$mode_change_output" "Do not call write, edit, or mutating tools" "same-turn mode change"
128
+
129
+ restored_mode_output="$(printf '{"hook_event_name":"SessionStart","source":"compact","session_id":"%s","cwd":"%s","model":"gpt-5"}\n' "$session_id" "$tmp_dir" | PLUGIN_DATA="$session_data_dir" node "$HOOK_PATH" SessionStart)"
130
+ assert_contains "$restored_mode_output" 'Configured default: `workflow` (repository)' "restored session mode"
131
+ assert_contains "$restored_mode_output" 'Session override: `conversation`' "restored session mode"
132
+ assert_contains "$restored_mode_output" 'Effective mode: `conversation` (session override, active)' "restored session mode"
133
+
134
+ other_session_output="$(printf '{"hook_event_name":"SessionStart","source":"resume","session_id":"other-session","cwd":"%s","model":"gpt-5"}\n' "$tmp_dir" | PLUGIN_DATA="$session_data_dir" node "$HOOK_PATH" SessionStart)"
135
+ assert_contains "$other_session_output" 'Session override: none' "session mode isolation"
136
+ assert_contains "$other_session_output" 'Effective mode: `workflow` (configured default, active)' "session mode isolation"
137
+
138
+ ordinary_prompt_output="$(printf '{"hook_event_name":"UserPromptSubmit","session_id":"%s","cwd":"%s","prompt":"Please explain this function."}\n' "$session_id" "$tmp_dir" | PLUGIN_DATA="$session_data_dir" node "$HOOK_PATH" UserPromptSubmit)"
139
+ [[ -z "$ordinary_prompt_output" ]] || fail "ordinary prompts should not duplicate runtime context"
140
+ question_prompt_output="$(printf '{"hook_event_name":"UserPromptSubmit","session_id":"%s","cwd":"%s","prompt":"Should we switch to strict-workflow mode?"}\n' "$session_id" "$tmp_dir" | PLUGIN_DATA="$session_data_dir" node "$HOOK_PATH" UserPromptSubmit)"
141
+ [[ -z "$question_prompt_output" ]] || fail "mode questions should not mutate session state"
142
+ hypothetical_prompt_output="$(printf '{"hook_event_name":"UserPromptSubmit","session_id":"%s","cwd":"%s","prompt":"Suppose the mode were workflow."}\n' "$session_id" "$tmp_dir" | PLUGIN_DATA="$session_data_dir" node "$HOOK_PATH" UserPromptSubmit)"
143
+ [[ -z "$hypothetical_prompt_output" ]] || fail "mode hypotheticals should not mutate session state"
144
+ default_prompt_output="$(printf '{"hook_event_name":"UserPromptSubmit","session_id":"%s","cwd":"%s","prompt":"Change the default mode to strict-workflow."}\n' "$session_id" "$tmp_dir" | PLUGIN_DATA="$session_data_dir" node "$HOOK_PATH" UserPromptSubmit)"
145
+ [[ -z "$default_prompt_output" ]] || fail "default-mode requests should remain agent-routed configuration decisions"
146
+
147
+ native_change_output="$(printf '{"hook_event_name":"UserPromptSubmit","session_id":"%s","cwd":"%s","prompt":"$mode-contract strict-workflow"}\n' "$session_id" "$tmp_dir" | PLUGIN_DATA="$session_data_dir" node "$HOOK_PATH" UserPromptSubmit)"
148
+ assert_contains "$native_change_output" '# Freeflow Runtime State' "Codex native skill mode control"
149
+ assert_contains "$native_change_output" 'Session override: `strict-workflow`' "Codex native skill mode control"
150
+ assert_not_contains "$native_change_output" "# Freeflow Mode Update" "Codex native skill mode control"
151
+ assert_not_contains "$native_change_output" "## Strict Workflow Overlay" "Codex native skill mode control"
152
+
153
+ claude_data_dir="$(mktemp -d)"
154
+ claude_mode_output="$(printf '{"hook_event_name":"UserPromptSubmit","session_id":"claude-session","cwd":"%s","prompt":"/freeflow:mode-contract conversation"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" node "$HOOK_PATH" UserPromptSubmit)"
155
+ assert_contains "$claude_mode_output" '"hookEventName":"UserPromptSubmit"' "Claude native skill mode control"
156
+ assert_contains "$claude_mode_output" 'Session override: `conversation`' "Claude native skill mode control"
157
+ claude_restored_output="$(printf '{"hook_event_name":"SessionStart","source":"resume","session_id":"claude-session","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" node "$HOOK_PATH" SessionStart)"
158
+ assert_contains "$claude_restored_output" 'Effective mode: `conversation` (session override, active)' "Claude restored session mode"
159
+
160
+ # Claude /clear ends one session and starts another. SessionEnd transfers only the
161
+ # active override for this host process/workspace, and SessionStart consumes it exactly once.
162
+ claude_clear_end_output="$(printf '{"hook_event_name":"SessionEnd","reason":"clear","session_id":"claude-session","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" CLAUDE_PID="claude-process-a" node "$HOOK_PATH" SessionEnd)"
163
+ [[ -z "$claude_clear_end_output" ]] || fail "SessionEnd clear should not emit model context"
164
+ claude_clear_output="$(printf '{"hook_event_name":"SessionStart","source":"clear","session_id":"claude-session-after-clear","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" CLAUDE_PID="claude-process-a" node "$HOOK_PATH" SessionStart)"
165
+ assert_contains "$claude_clear_output" 'Session override: `conversation`' "Claude clear session transfer"
166
+ assert_contains "$claude_clear_output" 'Effective mode: `conversation` (session override, active)' "Claude clear session transfer"
167
+ claude_unrelated_output="$(printf '{"hook_event_name":"SessionStart","source":"startup","session_id":"claude-unrelated-session","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" node "$HOOK_PATH" SessionStart)"
168
+ assert_contains "$claude_unrelated_output" 'Session override: none' "Claude clear transfer isolation"
169
+ claude_second_clear_output="$(printf '{"hook_event_name":"SessionStart","source":"clear","session_id":"claude-second-clear","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" node "$HOOK_PATH" SessionStart)"
170
+ assert_contains "$claude_second_clear_output" 'Session override: none' "Claude clear transfer one-shot consumption"
171
+
172
+ # A stale clear handoff is consumed without applying it.
173
+ claude_stale_mode_output="$(printf '{"hook_event_name":"UserPromptSubmit","session_id":"claude-stale-source","cwd":"%s","prompt":"Switch to strict-workflow mode."}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" node "$HOOK_PATH" UserPromptSubmit)"
174
+ assert_contains "$claude_stale_mode_output" 'Session override: `strict-workflow`' "Claude stale clear setup"
175
+ printf '{"hook_event_name":"SessionEnd","reason":"clear","session_id":"claude-stale-source","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" CLAUDE_PID="claude-process-stale" node "$HOOK_PATH" SessionEnd >/dev/null
176
+ clear_transfer_file="$(find "$claude_data_dir/session-modes/claude-clear" -type f -name '*.json' -print -quit)"
177
+ node - "$clear_transfer_file" <<'NODE'
178
+ const fs = require("fs");
179
+ const path = process.argv[2];
180
+ const state = JSON.parse(fs.readFileSync(path, "utf8"));
181
+ state.createdAt = new Date(Date.now() - 120000).toISOString();
182
+ fs.writeFileSync(path, `${JSON.stringify(state)}\n`);
183
+ NODE
184
+ claude_stale_clear_output="$(printf '{"hook_event_name":"SessionStart","source":"clear","session_id":"claude-stale-target","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" CLAUDE_PID="claude-process-stale" node "$HOOK_PATH" SessionStart)"
185
+ assert_contains "$claude_stale_clear_output" 'Session override: none' "Claude stale clear transfer"
186
+ [[ ! -e "$clear_transfer_file" ]] || fail "stale Claude clear transfer should be consumed"
187
+
188
+ # Malformed plugin-owned transfer content fails closed without suppressing the
189
+ # normal SessionStart context and is consumed rather than retried forever.
190
+ printf '{"hook_event_name":"SessionEnd","reason":"clear","session_id":"claude-stale-source","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" CLAUDE_PID="claude-process-malformed" node "$HOOK_PATH" SessionEnd >/dev/null
191
+ malformed_transfer_file="$(find "$claude_data_dir/session-modes/claude-clear" -type f -name '*.json' -print -quit)"
192
+ printf 'null\n' >"$malformed_transfer_file"
193
+ claude_malformed_clear_output="$(printf '{"hook_event_name":"SessionStart","source":"clear","session_id":"claude-malformed-target","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" CLAUDE_PID="claude-process-malformed" node "$HOOK_PATH" SessionStart)"
194
+ assert_contains "$claude_malformed_clear_output" '# Freeflow Stable Guidance' "Claude malformed clear transfer"
195
+ assert_contains "$claude_malformed_clear_output" 'Session override: none' "Claude malformed clear transfer"
196
+ [[ ! -e "$malformed_transfer_file" ]] || fail "malformed Claude clear transfer should be consumed"
197
+
198
+ # Overlapping clears in one workspace remain isolated by the host process ID.
199
+ printf '{"hook_event_name":"UserPromptSubmit","session_id":"claude-overlap-a","cwd":"%s","prompt":"Switch to conversation mode."}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" node "$HOOK_PATH" UserPromptSubmit >/dev/null
200
+ printf '{"hook_event_name":"UserPromptSubmit","session_id":"claude-overlap-b","cwd":"%s","prompt":"Switch to strict-workflow mode."}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" node "$HOOK_PATH" UserPromptSubmit >/dev/null
201
+ printf '{"hook_event_name":"SessionEnd","reason":"clear","session_id":"claude-overlap-a","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" CLAUDE_PID="claude-process-overlap-a" node "$HOOK_PATH" SessionEnd >/dev/null
202
+ printf '{"hook_event_name":"SessionEnd","reason":"clear","session_id":"claude-overlap-b","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" CLAUDE_PID="claude-process-overlap-b" node "$HOOK_PATH" SessionEnd >/dev/null
203
+ claude_overlap_b_output="$(printf '{"hook_event_name":"SessionStart","source":"clear","session_id":"claude-overlap-b-new","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" CLAUDE_PID="claude-process-overlap-b" node "$HOOK_PATH" SessionStart)"
204
+ assert_contains "$claude_overlap_b_output" 'Session override: `strict-workflow`' "Claude overlapping clear B"
205
+ claude_overlap_a_output="$(printf '{"hook_event_name":"SessionStart","source":"clear","session_id":"claude-overlap-a-new","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" CLAUDE_PID="claude-process-overlap-a" node "$HOOK_PATH" SessionStart)"
206
+ assert_contains "$claude_overlap_a_output" 'Session override: `conversation`' "Claude overlapping clear A"
207
+
208
+ # A no-override clear removes any stale transfer for the same process/workspace.
209
+ printf '{"hook_event_name":"SessionEnd","reason":"clear","session_id":"claude-no-override","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" CLAUDE_PID="claude-process-overlap-a" node "$HOOK_PATH" SessionEnd >/dev/null
210
+ claude_no_override_clear_output="$(printf '{"hook_event_name":"SessionStart","source":"clear","session_id":"claude-no-override-new","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" CLAUDE_PID="claude-process-overlap-a" node "$HOOK_PATH" SessionStart)"
211
+ assert_contains "$claude_no_override_clear_output" 'Session override: none' "Claude no-override clear"
212
+
213
+ # Transfer consumption claims the handoff atomically: two concurrent starts can
214
+ # never both receive the same override.
215
+ printf '{"hook_event_name":"SessionEnd","reason":"clear","session_id":"claude-overlap-b-new","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" CLAUDE_PID="claude-process-race" node "$HOOK_PATH" SessionEnd >/dev/null
216
+ race_output_a="$(mktemp)"
217
+ race_output_b="$(mktemp)"
218
+ printf '{"hook_event_name":"SessionStart","source":"clear","session_id":"claude-race-a","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" CLAUDE_PID="claude-process-race" node "$HOOK_PATH" SessionStart >"$race_output_a" &
219
+ race_pid_a=$!
220
+ printf '{"hook_event_name":"SessionStart","source":"clear","session_id":"claude-race-b","cwd":"%s"}\n' "$tmp_dir" | CLAUDE_PLUGIN_DATA="$claude_data_dir" CLAUDE_PID="claude-process-race" node "$HOOK_PATH" SessionStart >"$race_output_b" &
221
+ race_pid_b=$!
222
+ wait "$race_pid_a" "$race_pid_b"
223
+ race_override_count="$(grep -Fh 'Session override: `strict-workflow`' "$race_output_a" "$race_output_b" | wc -l | tr -d ' ')"
224
+ [[ "$race_override_count" == "1" ]] || fail "Claude clear transfer should have exactly one concurrent consumer"
225
+ rm -f "$race_output_a" "$race_output_b"
226
+
227
+ # Codex currently reports SessionEnd reason=other, so it must never stage a
228
+ # Claude-style clear handoff.
229
+ codex_clear_data_dir="$(mktemp -d)"
230
+ printf '{"hook_event_name":"UserPromptSubmit","session_id":"codex-source","cwd":"%s","prompt":"Switch to conversation mode."}\n' "$tmp_dir" | PLUGIN_DATA="$codex_clear_data_dir" node "$HOOK_PATH" UserPromptSubmit >/dev/null
231
+ printf '{"hook_event_name":"SessionEnd","reason":"other","session_id":"codex-source","cwd":"%s"}\n' "$tmp_dir" | PLUGIN_DATA="$codex_clear_data_dir" node "$HOOK_PATH" SessionEnd >/dev/null
232
+ if [[ -d "$codex_clear_data_dir/session-modes/claude-clear" ]]; then
233
+ fail "Codex SessionEnd should not stage Claude clear state"
234
+ fi
235
+ rm -rf "$codex_clear_data_dir" "$claude_data_dir"
236
+
237
+ reset_output="$(printf '{"hook_event_name":"UserPromptSubmit","session_id":"%s","cwd":"%s","prompt":"Reset the session mode."}\n' "$session_id" "$tmp_dir" | PLUGIN_DATA="$session_data_dir" node "$HOOK_PATH" UserPromptSubmit)"
238
+ assert_contains "$reset_output" 'Session override: cleared' "session mode reset"
239
+ assert_contains "$reset_output" 'Effective mode: `workflow` (configured default, active)' "session mode reset"
240
+ config_after="$(shasum -a 256 "$tmp_dir/.freeflow/config.json" | awk '{print $1}')"
241
+ [[ "$config_before" == "$config_after" ]] || fail "session mode controls modified repository config"
242
+ if find "$session_data_dir" -type f -print | grep -Fq "$session_id"; then
243
+ fail "raw session id leaked into the session-state path"
244
+ fi
245
+ rm -rf "$session_data_dir"
95
246
 
96
247
  for source in startup resume clear compact; do
97
- event_output="$(printf '{"hook_event_name":"SessionStart","source":"%s","cwd":"%s","model":"gpt-5"}\n' "$source" "$tmp_dir" | node "$HOOK_PATH" SessionStart)"
98
- assert_contains "$event_output" "# Freeflow Interaction Contract" "$source SessionStart context"
248
+ codex_event_output="$(printf '{"hook_event_name":"SessionStart","source":"%s","cwd":"%s","model":"gpt-5"}\n' "$source" "$tmp_dir" | node "$HOOK_PATH" SessionStart)"
249
+ claude_event_output="$(printf '{"hook_event_name":"SessionStart","source":"%s","cwd":"%s"}\n' "$source" "$tmp_dir" | node "$HOOK_PATH" SessionStart)"
250
+ for host in codex claude; do
251
+ if [[ "$host" == "codex" ]]; then
252
+ event_output="$codex_event_output"
253
+ else
254
+ event_output="$claude_event_output"
255
+ assert_contains "$event_output" '"hookEventName":"SessionStart"' "Claude $source wrapper"
256
+ fi
257
+ assert_contains "$event_output" "# Freeflow Interaction Contract" "$host $source SessionStart context"
258
+ assert_contains "$event_output" "## Shared Terms" "$host $source SessionStart context"
259
+ contract_count="$(grep -Fo '# Freeflow Interaction Contract' <<<"$event_output" | wc -l | tr -d ' ')"
260
+ skills_count="$(grep -Fo '## Shared Terms' <<<"$event_output" | wc -l | tr -d ' ')"
261
+ [[ "$contract_count" == "1" ]] || fail "$host $source SessionStart should load the Interaction Contract exactly once"
262
+ [[ "$skills_count" == "1" ]] || fail "$host $source SessionStart should load the Skills prompt exactly once"
263
+ assert_not_contains "$event_output" "# Freeflow Workflow Bootstrap" "$host $source SessionStart context"
264
+ done
99
265
  done
100
266
 
101
267
  # Repo-owned host instructions are neither required for activation nor modified by the read-only hook.
@@ -124,23 +290,23 @@ mkdir -p "$local_dir/.freeflow"
124
290
  printf '{"defaultMode":"workflow"}\n' >"$local_dir/.freeflow/config.json"
125
291
  printf '{"defaultMode":"strict-workflow"}\n' >"$local_dir/.freeflow/local.json"
126
292
  local_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$local_dir" | node "$HOOK_PATH" SessionStart)"
127
- assert_contains "$local_output" 'Current Freeflow default mode: `strict-workflow`.' "local default override"
128
- assert_contains "$local_output" 'defaultMode `strict-workflow` from local' "local default source"
129
- assert_contains "$local_output" "## Strict Workflow Overlay" "local strict overlay"
130
- assert_contains "$local_output" "security, privacy, billing, data loss, migrations, public interfaces" "local strict overlay"
293
+ assert_contains "$local_output" 'Configured default: `strict-workflow` (personal)' "local default override"
294
+ assert_contains "$local_output" 'Effective mode: `strict-workflow` (configured default, active)' "local default source"
295
+ assert_not_contains "$local_output" "## Strict Workflow Overlay" "local strict overlay"
296
+ assert_not_contains "$local_output" "security, privacy, billing, data loss, migrations, public interfaces" "local strict overlay"
131
297
  assert_contains "$local_output" "# Freeflow Interaction Contract" "local default override"
132
- assert_contains "$local_output" "# Freeflow Workflow Bootstrap" "local default override"
298
+ assert_contains "$local_output" "## Shared Terms" "local default override"
133
299
  rm -rf "$local_dir"
134
300
 
135
301
  local_interaction_dir="$(mktemp -d)"
136
302
  mkdir -p "$local_interaction_dir/.freeflow"
137
- printf '{"defaultMode":"workflow"}\n' > "$local_interaction_dir/.freeflow/config.json"
138
- printf '{"interactionContract":false}\n' > "$local_interaction_dir/.freeflow/local.json"
303
+ printf '{"defaultMode":"workflow"}\n' >"$local_interaction_dir/.freeflow/config.json"
304
+ printf '{"interactionContract":false}\n' >"$local_interaction_dir/.freeflow/local.json"
139
305
  local_interaction_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$local_interaction_dir" | node "$HOOK_PATH" SessionStart)"
140
306
  assert_contains "$local_interaction_output" 'Interaction Contract: disabled' "independent local interaction override"
141
307
  assert_contains "$local_interaction_output" 'Skills: enabled' "independent local interaction override"
142
308
  assert_not_contains "$local_interaction_output" "# Freeflow Interaction Contract" "independent local interaction override"
143
- assert_contains "$local_interaction_output" "# Freeflow Workflow Bootstrap" "independent local interaction override"
309
+ assert_contains "$local_interaction_output" "## Shared Terms" "independent local interaction override"
144
310
  rm -rf "$local_interaction_dir"
145
311
 
146
312
  local_skills_dir="$(mktemp -d)"
@@ -148,10 +314,11 @@ mkdir -p "$local_skills_dir/.freeflow"
148
314
  printf '{"defaultMode":"workflow"}\n' >"$local_skills_dir/.freeflow/config.json"
149
315
  printf '{"defaultMode":"conversation","interactionContract":false,"skills":{"enabled":false}}\n' >"$local_skills_dir/.freeflow/local.json"
150
316
  local_skills_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$local_skills_dir" | node "$HOOK_PATH" SessionStart)"
151
- assert_contains "$local_skills_output" 'Resolved default mode: `conversation` (dormant because Skills are disabled).' "local dormant mode"
317
+ assert_contains "$local_skills_output" 'Resolved mode: `conversation` (dormant because Skills are disabled)' "local dormant mode"
152
318
  assert_contains "$local_skills_output" 'Interaction Contract: disabled' "local interaction override"
153
319
  assert_contains "$local_skills_output" 'Skills: disabled' "local skills override"
154
320
  assert_not_contains "$local_skills_output" "# Freeflow Interaction Contract" "local interaction override"
321
+ assert_not_contains "$local_skills_output" "# Freeflow Mode Update" "local skills override"
155
322
  assert_not_contains "$local_skills_output" "# Freeflow Workflow Bootstrap" "local skills override"
156
323
  rm -rf "$local_skills_dir"
157
324
 
@@ -168,56 +335,58 @@ mkdir -p "$local_disabled_dir/.freeflow"
168
335
  printf '{"enabled":true,"defaultMode":"workflow"}\n' >"$local_disabled_dir/.freeflow/config.json"
169
336
  printf '{"enabled":false,"processing":{"unsafeUnsandboxed":{"enabled":true}}}\n' >"$local_disabled_dir/.freeflow/local.json"
170
337
  local_disabled_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$local_disabled_dir" | node "$HOOK_PATH" SessionStart)"
171
- assert_contains "$local_disabled_output" "# Freeflow Disabled" "local master override"
172
- assert_contains "$local_disabled_output" "effective \`enabled\` is false from local" "local master source"
173
- assert_not_contains "$local_disabled_output" "# Freeflow Interaction Contract" "local master override"
338
+ [[ -z "$local_disabled_output" ]] || fail "local master override should suppress hook prompt delivery"
174
339
  rm -rf "$local_disabled_dir"
175
340
 
176
341
  disabled_dir="$(mktemp -d)"
177
342
  mkdir -p "$disabled_dir/.freeflow"
178
343
  printf '{"enabled":false,"defaultMode":"workflow"}\n' >"$disabled_dir/.freeflow/config.json"
179
344
  disabled_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$disabled_dir" | node "$HOOK_PATH" SessionStart)"
180
- assert_contains "$disabled_output" "# Freeflow Disabled" "disabled context"
181
- assert_not_contains "$disabled_output" "# Freeflow Runtime Kernel" "disabled context"
345
+ [[ -z "$disabled_output" ]] || fail "disabled Freeflow should suppress hook prompt delivery"
182
346
  rm -rf "$disabled_dir"
183
347
 
184
348
  router_dir="$(mktemp -d)"
185
349
  mkdir -p "$router_dir/.freeflow"
186
350
  printf '{"defaultMode":"workflow","skills":{"enabled":false},"outputRouter":{"enabled":true}}\n' >"$router_dir/.freeflow/config.json"
187
351
  router_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$router_dir" | node "$HOOK_PATH" SessionStart)"
188
- assert_contains "$router_output" "Skills: disabled" "router-only context"
189
- assert_contains "$router_output" "## Loaded Output Router Skill" "router-only context"
190
- assert_contains "$router_output" "# Freeflow Interaction Contract" "router-only context"
191
- assert_not_contains "$router_output" "# Freeflow Workflow Bootstrap" "router-only context"
352
+ assert_contains "$router_output" "Skills: disabled" "Codex context with legacy router config"
353
+ assert_contains "$router_output" "# Freeflow Interaction Contract" "Codex context with legacy router config"
354
+ assert_not_contains "$router_output" "# Freeflow Workflow Bootstrap" "Codex context with disabled skills"
355
+ assert_not_contains "$router_output" "Output Router" "Codex context with legacy router config"
356
+ assert_not_contains "$router_output" "Output router:" "Codex context with legacy router config"
192
357
  rm -rf "$router_dir"
193
358
 
194
359
  all_dir="$(mktemp -d)"
195
360
  mkdir -p "$all_dir/.freeflow"
196
361
  printf '{"defaultMode":"strict-workflow","outputRouter":{"enabled":true}}\n' >"$all_dir/.freeflow/config.json"
197
362
  all_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$all_dir" | node "$HOOK_PATH" SessionStart)"
198
- assert_contains "$all_output" "# Freeflow Interaction Contract" "all-capabilities context"
199
- assert_contains "$all_output" "## Loaded Output Router Skill" "all-capabilities context"
200
- assert_contains "$all_output" 'Current Freeflow default mode: `strict-workflow`.' "all-capabilities context"
201
- assert_contains "$all_output" "## Strict Workflow Overlay" "all-capabilities strict overlay"
363
+ assert_contains "$all_output" "# Freeflow Interaction Contract" "Codex strict context"
364
+ assert_contains "$all_output" "## Shared Terms" "Codex strict context"
365
+ assert_not_contains "$all_output" "Output Router" "Codex strict context"
366
+ assert_not_contains "$all_output" "Output router:" "Codex strict context"
367
+ assert_contains "$all_output" 'Effective mode: `strict-workflow` (configured default, active)' "Codex strict context"
368
+ assert_not_contains "$all_output" "## Strict Workflow Overlay" "all-capabilities strict overlay"
202
369
  rm -rf "$all_dir"
203
370
 
204
371
  conversation_dir="$(mktemp -d)"
205
372
  mkdir -p "$conversation_dir/.freeflow"
206
373
  printf '{"defaultMode":"conversation"}\n' >"$conversation_dir/.freeflow/config.json"
207
374
  conversation_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$conversation_dir" | node "$HOOK_PATH" SessionStart)"
208
- assert_contains "$conversation_output" 'Current Freeflow default mode: `conversation`.' "conversation context"
209
- assert_contains "$conversation_output" '`conversation`: answer, discuss, critique, and inspect read-only' "conversation context"
210
- assert_contains "$conversation_output" "## Conversation Mode Boundary" "conversation overlay"
211
- assert_contains "$conversation_output" "Do not call write, edit, or mutating tools" "conversation overlay"
212
- assert_contains "$conversation_output" "an execution skill does not override this boundary" "conversation overlay"
375
+ assert_contains "$conversation_output" 'Configured default: `conversation` (repository)' "conversation context"
376
+ assert_contains "$conversation_output" 'Effective mode: `conversation` (configured default, active)' "conversation context"
377
+ assert_not_contains "$conversation_output" "## Conversation Mode Boundary" "conversation overlay"
378
+ assert_not_contains "$conversation_output" "Do not call write, edit, or mutating tools" "conversation overlay"
379
+ assert_not_contains "$conversation_output" "an execution skill does not override this boundary" "conversation overlay"
213
380
  rm -rf "$conversation_dir"
214
381
 
215
382
  observed_dir="$(mktemp -d)"
216
383
  mkdir -p "$observed_dir/.freeflow"
217
384
  printf '{"defaultMode":"workflow","outputRouter":{"enabled":true,"observedRouting":{"enabled":true}},"scriptTransform":{"enabled":false}}\n' >"$observed_dir/.freeflow/config.json"
218
385
  observed_output="$(printf '{"cwd":"%s","model":"gpt-5"}\n' "$observed_dir" | node "$HOOK_PATH" SessionStart)"
219
- assert_contains "$observed_output" "# Freeflow Interaction Contract" "observed-routing config"
220
- assert_contains "$observed_output" "## Loaded Output Router Skill" "observed-routing config"
386
+ assert_contains "$observed_output" "# Freeflow Interaction Contract" "Codex context with observed-routing config"
387
+ assert_contains "$observed_output" "## Shared Terms" "Codex context with observed-routing config"
388
+ assert_not_contains "$observed_output" "Output Router" "Codex context with observed-routing config"
389
+ assert_not_contains "$observed_output" "Output router:" "Codex context with observed-routing config"
221
390
  rm -rf "$observed_dir"
222
391
 
223
392
  echo "runtime-context hook checks passed"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hassangameryt/freeflow",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "Feedback-based control system for coding agents.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -18,24 +18,33 @@
18
18
  "scripts": {
19
19
  "format": "prettier --write .",
20
20
  "format:check": "prettier --check .",
21
- "format:router-dist": "prettier --write router/dist",
22
21
  "format:pi-extension-dist": "prettier --write pi-extension/dist",
23
- "build:router": "tsc -p router/tsconfig.json && npm run format:router-dist",
24
22
  "build:pi-extension": "tsc -p pi-extension/tsconfig.json && npm run format:pi-extension-dist",
25
- "build": "npm run build:router && npm run build:pi-extension",
23
+ "build": "npm run build:pi-extension",
24
+ "typecheck": "tsc -p pi-extension/tsconfig.json --noEmit",
25
+ "check": "npm run typecheck && npm run check:source && npm run check:changelog && npm run check:release-workflow && npm run check:docs && npm run format:check && npm run check:release-version && npm run check:commands && npm run check:activation && npm run check:runtime && npm run check:package",
26
+ "check:source": "node scripts/validation/check-source-files.mjs",
27
+ "check:changelog": "node scripts/validation/check-changelog.mjs --structure",
28
+ "check:release-workflow": "node scripts/validation/check-release-workflow.mjs",
29
+ "check:docs": "node scripts/validation/check-docs.mjs",
30
+ "check:release-version": "node scripts/validation/check-release-version.mjs",
31
+ "check:commands": "scripts/validation/audit-command-surface.sh",
32
+ "check:activation": "scripts/validation/check-activation-contract.sh",
33
+ "check:runtime": "hooks/tests/check-runtime-context-hook.sh",
34
+ "check:package": "node scripts/validation/check-package.mjs",
35
+ "check:release-metadata": "scripts/validation/validate-release-metadata.sh --mode prepublish",
26
36
  "test:pi-extension": "npm run build && node --test $(find pi-extension/tests -name '*.test.js' | sort)",
27
- "test:router": "npm run build:router && node --test $(find router/tests -name '*.test.js' | sort)",
28
37
  "test:skill-author": "node --test $(find .skill-eval/write-skill/tests -name '*.test.mjs' | sort)",
29
38
  "test:skill-eval": "node --test $(find .skill-eval/evaluate-skill/tests -name '*.test.mjs' | sort)",
30
- "bench:router": "npm run build:router && node router/dist/benchmarks/benchmarks.js",
31
- "bench:router:commands": "npm run build:router && node router/dist/benchmarks/command-benchmarks.js",
32
- "bench:router:context-saving-baseline": "npm run build:router && node router/dist/benchmarks/context-saving-baseline.js",
33
- "bench:router:context-mode-normalized": "npm run build:router && node router/dist/benchmarks/context-mode-normalized-benchmarks.js",
34
- "bench:router:context-mode-real-deep": "npm run build:router && node router/dist/benchmarks/context-mode-real-deep-benchmark.js",
35
- "bench:router:storage-policy": "npm run build:router && node router/dist/benchmarks/storage-policy-benchmarks.js",
36
- "bench:router:index": "npm run build:router && node router/dist/benchmarks/index-benchmarks.js",
37
- "bench:router:vault-index": "npm run build:router && node router/dist/benchmarks/vault-index-benchmarks.js",
38
- "bench:router:codex-qa": "npm run build:router && node router/dist/benchmarks/codex-qa-benchmarks.js"
39
+ "test:track-work": "node --test .skill-eval/track-work/tests/*.test.mjs",
40
+ "test:freeflow-snapshot": "node --test scripts/freeflow-snapshot.test.mjs",
41
+ "test:release-utils": "node --test scripts/release-utils.test.mjs",
42
+ "test:changelog": "node --test scripts/validation/check-changelog.test.mjs",
43
+ "test:release-workflow": "node --test scripts/validation/check-release-workflow.test.mjs",
44
+ "test:docs": "node --test scripts/validation/check-docs.test.mjs",
45
+ "release:prepare": "node scripts/release.mjs",
46
+ "release:notes": "node scripts/release-notes.mjs",
47
+ "snapshot:refresh": "node scripts/freeflow-snapshot.mjs"
39
48
  },
40
49
  "keywords": [
41
50
  "pi-package",
@@ -66,11 +75,11 @@
66
75
  "LICENSE",
67
76
  "README.md",
68
77
  "assets/**",
78
+ "capabilities/**",
79
+ "runtime/**",
69
80
  "command-surface.json",
70
81
  "hooks/**",
71
82
  "pi-extension/**",
72
- "router/dist/**",
73
- "runtime/**",
74
83
  "skills/**"
75
84
  ],
76
85
  "peerDependencies": {