@hassangameryt/freeflow 0.7.3 → 0.8.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 (276) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.cursor-plugin/plugin.json +1 -1
  5. package/NOTICE +14 -0
  6. package/README.md +26 -31
  7. package/capabilities/cognitive-routing/SKILL.md +106 -161
  8. package/capabilities/cognitive-routing/references/both-mode.md +36 -0
  9. package/capabilities/cognitive-routing/references/executor-mode.md +48 -0
  10. package/capabilities/cognitive-routing/references/helper-mode.md +47 -0
  11. package/capabilities/compaction/SKILL.md +49 -0
  12. package/capabilities/compaction/references/summary-format.md +239 -0
  13. package/command-surface.json +4 -0
  14. package/gemini-extension.json +1 -1
  15. package/hooks/shared/runtime-context.mjs +28 -30
  16. package/licenses/Apache-2.0.txt +55 -0
  17. package/package.json +22 -9
  18. package/pi-extension/README.md +41 -0
  19. package/pi-extension/dist/cognitive-routing/assembler.js +393 -0
  20. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/config.js +5 -2
  21. package/pi-extension/dist/cognitive-routing/economics.js +45 -0
  22. package/pi-extension/dist/cognitive-routing/event-schema.js +388 -0
  23. package/pi-extension/dist/{session-sources/events.js → cognitive-routing/event-store.js} +103 -40
  24. package/pi-extension/dist/cognitive-routing/gate.js +241 -0
  25. package/pi-extension/dist/cognitive-routing/handlers.js +899 -0
  26. package/pi-extension/dist/cognitive-routing/handoffs.js +350 -0
  27. package/pi-extension/dist/cognitive-routing/model-control.js +467 -0
  28. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/projection.js +229 -56
  29. package/pi-extension/dist/cognitive-routing/provenance.js +81 -0
  30. package/pi-extension/dist/cognitive-routing/runtime.js +396 -0
  31. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/schemas.js +43 -7
  32. package/pi-extension/dist/cognitive-routing/session.js +221 -0
  33. package/pi-extension/dist/{session-sources → cognitive-routing}/sources.js +99 -85
  34. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/state.js +37 -378
  35. package/pi-extension/dist/cognitive-routing/status.js +80 -0
  36. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/tools.js +12 -12
  37. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/types.js +5 -11
  38. package/pi-extension/dist/compaction/carry.js +191 -0
  39. package/pi-extension/dist/compaction/config.js +22 -0
  40. package/pi-extension/dist/compaction/controller.js +471 -0
  41. package/pi-extension/dist/compaction/harness.js +77 -0
  42. package/pi-extension/dist/compaction/results.js +158 -0
  43. package/pi-extension/dist/compaction/thresholds.js +35 -0
  44. package/pi-extension/dist/compaction/tool.js +57 -0
  45. package/pi-extension/dist/host/branch.js +13 -0
  46. package/pi-extension/dist/host/canonical.js +12 -0
  47. package/pi-extension/dist/host/catalog.js +77 -0
  48. package/pi-extension/dist/host/config.js +374 -0
  49. package/pi-extension/dist/host/projection-tags.js +60 -0
  50. package/pi-extension/dist/host/prompts.js +82 -0
  51. package/pi-extension/dist/{session-sources → host}/read-only-session.js +112 -1
  52. package/pi-extension/dist/{runtime → host}/request-history.js +121 -39
  53. package/pi-extension/dist/host/runtime-state.js +189 -0
  54. package/pi-extension/dist/{settings/settings-ui.js → host/settings/freeflow-command.js} +620 -410
  55. package/pi-extension/dist/{settings/settings-tui.js → host/settings/settings-view.js} +186 -73
  56. package/pi-extension/dist/host/staging.js +28 -0
  57. package/pi-extension/dist/host/status.js +101 -0
  58. package/pi-extension/dist/index.js +423 -173
  59. package/pi-extension/dist/provider-support/cache/anchor.js +21 -0
  60. package/pi-extension/dist/provider-support/cache/anthropic.js +102 -0
  61. package/pi-extension/dist/provider-support/cache/health.js +73 -0
  62. package/pi-extension/dist/provider-support/cache/index.js +58 -0
  63. package/pi-extension/dist/provider-support/cache/keepalive.js +210 -0
  64. package/pi-extension/dist/provider-support/cache/monitor.js +17 -0
  65. package/pi-extension/dist/provider-support/cache/openai-responses.js +48 -0
  66. package/pi-extension/dist/provider-support/effort.js +13 -0
  67. package/pi-extension/dist/provider-support/index.js +44 -3
  68. package/pi-extension/dist/provider-support/{astra → openai}/adapter.js +46 -29
  69. package/pi-extension/dist/provider-support/{astra → openai}/history.js +10 -8
  70. package/pi-extension/dist/provider-support/{astra → openai}/index.js +8 -4
  71. package/pi-extension/dist/provider-support/{astra → openai}/session-state.js +29 -38
  72. package/pi-extension/dist/provider-support/routes.js +17 -0
  73. package/pi-extension/dist/tool-execution/apply-patch/grammar.js +23 -0
  74. package/pi-extension/dist/tool-execution/apply-patch/match.js +38 -0
  75. package/pi-extension/dist/tool-execution/apply-patch/parser.js +177 -0
  76. package/pi-extension/dist/tool-execution/apply-patch/plan.js +201 -0
  77. package/pi-extension/dist/tool-execution/apply-patch/tool.js +84 -0
  78. package/pi-extension/dist/tool-execution/background.js +238 -0
  79. package/pi-extension/dist/tool-execution/bash-guard.js +23 -0
  80. package/pi-extension/dist/tool-execution/config.js +22 -0
  81. package/pi-extension/dist/tool-execution/edit-messages.js +120 -0
  82. package/pi-extension/dist/tool-execution/file-state.js +138 -0
  83. package/pi-extension/dist/tool-execution/file-tracking.js +143 -0
  84. package/pi-extension/dist/tool-execution/prompt.js +54 -0
  85. package/pi-extension/dist/tool-execution/tools.js +16 -0
  86. package/pi-extension/freeflow/README.md +9 -0
  87. package/plugin.json +1 -1
  88. package/runtime/prompts/cognitive-routing.md +4 -4
  89. package/runtime/prompts/core.md +22 -75
  90. package/runtime/prompts/interaction-contract.md +15 -8
  91. package/runtime/prompts/tool-execution.md +38 -0
  92. package/runtime/prompts/working-method.md +108 -0
  93. package/skills/action-selection/SKILL.md +39 -47
  94. package/skills/action-selection/references/trajectory-stalls.md +2 -0
  95. package/skills/bypass/SKILL.md +2 -2
  96. package/skills/commit-work/SKILL.md +31 -83
  97. package/skills/commit-work/references/git-state-edges.md +1 -1
  98. package/skills/commit-work/references/simple-push.md +2 -0
  99. package/skills/commit-work/references/staging-decisions.md +1 -1
  100. package/skills/decision-gate/SKILL.md +31 -50
  101. package/skills/design-for-depth/SKILL.md +44 -53
  102. package/skills/design-for-depth/references/design-pressure-signals.md +46 -144
  103. package/skills/design-for-depth/references/implementation-decisions.md +1 -1
  104. package/skills/design-for-depth/references/interface-design-loop.md +5 -36
  105. package/skills/design-for-depth/references/module-and-dependency-design.md +2 -32
  106. package/skills/design-for-depth/references/state-and-failure-boundaries.md +2 -13
  107. package/skills/diagnose-failure/SKILL.md +39 -24
  108. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +2 -0
  109. package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +2 -2
  110. package/skills/discuss/SKILL.md +35 -11
  111. package/skills/discuss/references/discussion-continuity.md +7 -27
  112. package/skills/evaluate-skill/SKILL.md +49 -80
  113. package/skills/evaluate-skill/references/evaluation-definition-schema.md +35 -5
  114. package/skills/evaluate-skill/references/evaluation-design.md +14 -14
  115. package/skills/evaluate-skill/references/execution-and-evidence.md +26 -5
  116. package/skills/evaluate-skill/references/review-and-revision.md +3 -1
  117. package/skills/evaluate-skill/scripts/lib/definitions.mjs +49 -8
  118. package/skills/evaluate-skill/scripts/lib/pi.mjs +14 -7
  119. package/skills/evaluate-skill/scripts/lib/review.mjs +196 -0
  120. package/skills/evaluate-skill/scripts/lib/runner.mjs +45 -8
  121. package/skills/evaluate-skill/scripts/lib/sandbox.mjs +2 -0
  122. package/skills/evaluate-skill/scripts/lib/trials.mjs +103 -0
  123. package/skills/evaluate-skill/scripts/lib/view.mjs +67 -1
  124. package/skills/evaluate-skill/scripts/pi-command-tool.mjs +144 -0
  125. package/skills/evaluate-skill/scripts/pi-guard.mjs +30 -13
  126. package/skills/evaluate-skill/scripts/skill-eval.mjs +114 -34
  127. package/skills/execute-work/SKILL.md +41 -9
  128. package/skills/execute-work/references/code-practices.md +2 -0
  129. package/skills/execute-work/references/execute-work-edges.md +11 -1
  130. package/skills/handoff/SKILL.md +1 -1
  131. package/skills/handoff/references/handoff-templates.md +1 -1
  132. package/skills/review-artifact/SKILL.md +42 -41
  133. package/skills/review-artifact/references/adjudicate-artifact-review.md +1 -1
  134. package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +1 -1
  135. package/skills/review-work/SKILL.md +38 -32
  136. package/skills/review-work/references/adjudicate-work-review.md +1 -1
  137. package/skills/review-work/references/independent-work-reviewer-contract.md +1 -1
  138. package/skills/review-work/references/security-risk-lens.md +2 -4
  139. package/skills/setup-freeflow/SKILL.md +3 -1
  140. package/skills/setup-freeflow/references/activation-contract.md +7 -13
  141. package/skills/setup-freeflow/references/host-setup.md +3 -3
  142. package/skills/track-work/SKILL.md +113 -161
  143. package/skills/track-work/references/working-record-format.md +189 -350
  144. package/skills/track-work/scripts/lib/document.mjs +62 -6
  145. package/skills/track-work/scripts/lib/format.mjs +16 -1
  146. package/skills/track-work/scripts/lib/store.mjs +9 -8
  147. package/skills/track-work/scripts/lib/transitions.mjs +79 -4
  148. package/skills/track-work/scripts/working-record.mjs +34 -12
  149. package/skills/verify-work/SKILL.md +29 -20
  150. package/skills/verify-work/references/browser-runtime-evidence.md +1 -5
  151. package/skills/verify-work/references/integration-evidence.md +2 -0
  152. package/skills/verify-work/references/performance-evidence.md +3 -1
  153. package/skills/verify-work/references/test-design.md +3 -1
  154. package/skills/workflow/SKILL.md +65 -21
  155. package/skills/workflow/references/domain-skill-composition.md +5 -3
  156. package/skills/write-docs/SKILL.md +83 -0
  157. package/skills/write-docs/references/documentation-shapes.md +119 -0
  158. package/skills/write-docs/scripts/docs-check.mjs +139 -0
  159. package/skills/write-plan/SKILL.md +40 -40
  160. package/skills/write-plan/references/plan-shapes.md +1 -1
  161. package/skills/write-skill/SKILL.md +107 -147
  162. package/skills/write-skill/references/activation-boundaries.md +2 -0
  163. package/skills/write-skill/references/agent-first-instructions.md +22 -10
  164. package/skills/write-skill/references/progressive-disclosure.md +4 -2
  165. package/skills/write-skill/references/skill-dependency-graphs.md +2 -0
  166. package/skills/write-skill/scripts/lib/similarity.mjs +56 -0
  167. package/skills/write-skill/scripts/skill-author.mjs +18 -2
  168. package/skills/write-spec/SKILL.md +34 -41
  169. package/skills/write-spec/references/artifact-standards.md +1 -1
  170. package/skills/write-spec/references/decision-records.md +2 -0
  171. package/skills/write-spec/references/spec-shapes.md +1 -1
  172. package/capabilities/context-virtualization/SKILL.md +0 -103
  173. package/capabilities/conversation-history/SKILL.md +0 -53
  174. package/hooks/tests/check-host-adapters.test.mjs +0 -82
  175. package/hooks/tests/check-runtime-context-hook.sh +0 -212
  176. package/pi-extension/dist/cognitive-routing-v2/runtime.js +0 -2181
  177. package/pi-extension/dist/context-virtualization/commands.js +0 -89
  178. package/pi-extension/dist/context-virtualization/projector.js +0 -42
  179. package/pi-extension/dist/context-virtualization/runtime.js +0 -378
  180. package/pi-extension/dist/context-virtualization/state.js +0 -57
  181. package/pi-extension/dist/context-virtualization/types.js +0 -10
  182. package/pi-extension/dist/conversation-history/passages.js +0 -66
  183. package/pi-extension/dist/conversation-history/ranking.js +0 -105
  184. package/pi-extension/dist/conversation-history/runtime.js +0 -226
  185. package/pi-extension/dist/conversation-history/search.js +0 -56
  186. package/pi-extension/dist/conversation-history/types.js +0 -1
  187. package/pi-extension/dist/freeflow-context/resolver.js +0 -120
  188. package/pi-extension/dist/freeflow-context/runtime.js +0 -80
  189. package/pi-extension/dist/freeflow-context/source-projector.js +0 -95
  190. package/pi-extension/dist/freeflow-context/tool.js +0 -314
  191. package/pi-extension/dist/freeflow-context/types.js +0 -12
  192. package/pi-extension/dist/freeflow-context/visibility.js +0 -28
  193. package/pi-extension/dist/integrations/mcp-config.js +0 -29
  194. package/pi-extension/dist/runtime/runtime-context.js +0 -754
  195. package/pi-extension/dist/runtime/runtime-identity.js +0 -5
  196. package/pi-extension/dist/session-sources/provenance.js +0 -32
  197. package/pi-extension/dist/ui/text-component.js +0 -57
  198. package/pi-extension/src/cognitive-routing-v2/budget.ts +0 -42
  199. package/pi-extension/src/cognitive-routing-v2/config.ts +0 -173
  200. package/pi-extension/src/cognitive-routing-v2/projection.ts +0 -375
  201. package/pi-extension/src/cognitive-routing-v2/render.ts +0 -277
  202. package/pi-extension/src/cognitive-routing-v2/runtime.ts +0 -2241
  203. package/pi-extension/src/cognitive-routing-v2/schemas.ts +0 -180
  204. package/pi-extension/src/cognitive-routing-v2/state.ts +0 -839
  205. package/pi-extension/src/cognitive-routing-v2/tools.ts +0 -62
  206. package/pi-extension/src/cognitive-routing-v2/types.ts +0 -254
  207. package/pi-extension/src/context-virtualization/commands.ts +0 -111
  208. package/pi-extension/src/context-virtualization/projector.ts +0 -62
  209. package/pi-extension/src/context-virtualization/runtime.ts +0 -428
  210. package/pi-extension/src/context-virtualization/state.ts +0 -79
  211. package/pi-extension/src/context-virtualization/types.ts +0 -92
  212. package/pi-extension/src/conversation-history/passages.ts +0 -81
  213. package/pi-extension/src/conversation-history/ranking.ts +0 -155
  214. package/pi-extension/src/conversation-history/runtime.ts +0 -250
  215. package/pi-extension/src/conversation-history/search.ts +0 -79
  216. package/pi-extension/src/conversation-history/types.ts +0 -31
  217. package/pi-extension/src/freeflow-context/resolver.ts +0 -139
  218. package/pi-extension/src/freeflow-context/runtime.ts +0 -100
  219. package/pi-extension/src/freeflow-context/source-projector.ts +0 -106
  220. package/pi-extension/src/freeflow-context/tool.ts +0 -366
  221. package/pi-extension/src/freeflow-context/types.ts +0 -70
  222. package/pi-extension/src/freeflow-context/visibility.ts +0 -53
  223. package/pi-extension/src/index.ts +0 -390
  224. package/pi-extension/src/integrations/mcp-config.ts +0 -32
  225. package/pi-extension/src/provider-support/astra/adapter.ts +0 -108
  226. package/pi-extension/src/provider-support/astra/history.ts +0 -145
  227. package/pi-extension/src/provider-support/astra/index.ts +0 -12
  228. package/pi-extension/src/provider-support/astra/session-state.ts +0 -113
  229. package/pi-extension/src/provider-support/index.ts +0 -5
  230. package/pi-extension/src/runtime/request-history.ts +0 -243
  231. package/pi-extension/src/runtime/runtime-context.ts +0 -860
  232. package/pi-extension/src/runtime/runtime-identity.ts +0 -8
  233. package/pi-extension/src/session-sources/events.ts +0 -230
  234. package/pi-extension/src/session-sources/provenance.ts +0 -39
  235. package/pi-extension/src/session-sources/read-only-session.ts +0 -170
  236. package/pi-extension/src/session-sources/sources.ts +0 -334
  237. package/pi-extension/src/settings/settings-tui.ts +0 -501
  238. package/pi-extension/src/settings/settings-ui.ts +0 -1652
  239. package/pi-extension/src/ui/text-component.ts +0 -66
  240. package/pi-extension/tests/cache-reuse/history.test.js +0 -117
  241. package/pi-extension/tests/cache-reuse/native.test.js +0 -91
  242. package/pi-extension/tests/cache-reuse/settings.test.js +0 -211
  243. package/pi-extension/tests/cognitive-routing-v2/communication.test.js +0 -577
  244. package/pi-extension/tests/cognitive-routing-v2/compaction-input.test.js +0 -183
  245. package/pi-extension/tests/cognitive-routing-v2/delegation.test.js +0 -557
  246. package/pi-extension/tests/cognitive-routing-v2/native.test.js +0 -474
  247. package/pi-extension/tests/cognitive-routing-v2/performance.test.js +0 -87
  248. package/pi-extension/tests/cognitive-routing-v2/persistence.test.js +0 -300
  249. package/pi-extension/tests/cognitive-routing-v2/projection.test.js +0 -241
  250. package/pi-extension/tests/cognitive-routing-v2/recovery.test.js +0 -1034
  251. package/pi-extension/tests/cognitive-routing-v2/render.test.js +0 -244
  252. package/pi-extension/tests/cognitive-routing-v2/runtime.test.js +0 -423
  253. package/pi-extension/tests/cognitive-routing-v2/stabilization.test.js +0 -521
  254. package/pi-extension/tests/cognitive-routing-v2/state.test.js +0 -586
  255. package/pi-extension/tests/context-virtualization/extension.test.js +0 -775
  256. package/pi-extension/tests/context-virtualization/projector.test.js +0 -110
  257. package/pi-extension/tests/context-virtualization/runtime.test.js +0 -230
  258. package/pi-extension/tests/conversation-history/runtime.test.js +0 -98
  259. package/pi-extension/tests/conversation-history/search.test.js +0 -97
  260. package/pi-extension/tests/fixtures/pi-host.js +0 -4
  261. package/pi-extension/tests/fixtures/routing-native.js +0 -191
  262. package/pi-extension/tests/integration/mode-free-core.test.js +0 -220
  263. package/pi-extension/tests/integration/pi-extension.test.js +0 -1447
  264. package/pi-extension/tests/integration/prompt-architecture.test.js +0 -447
  265. package/pi-extension/tests/integration/unaffected-regressions.test.js +0 -478
  266. package/pi-extension/tests/provider-support/astra-boundaries.test.js +0 -75
  267. package/pi-extension/tests/provider-support/astra.test.js +0 -306
  268. package/pi-extension/tests/provider-support/cli.test.js +0 -92
  269. package/pi-extension/tests/provider-support/native.test.js +0 -183
  270. package/pi-extension/tests/settings/settings-tui.test.js +0 -243
  271. package/pi-extension/tsconfig.json +0 -16
  272. package/runtime/prompts/context-virtualization.md +0 -3
  273. package/runtime/prompts/conversation-history.md +0 -3
  274. package/skills/design-for-depth/references/software-design-philosophy.md +0 -202
  275. /package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/budget.js +0 -0
  276. /package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/render.js +0 -0
@@ -0,0 +1,120 @@
1
+ // Messages Freeflow gives around Pi's edit and write. Every error ends in one literal next action; results carry no
2
+ // reassurance. The wording was reviewed against the Beyond the Weights findings (Task 006 evidence).
3
+ const MAX_SHOWN_LINES = 20;
4
+ const MAX_LINE_CHARS = 300;
5
+ const MIN_SIMILARITY = 0.5;
6
+ /** Pi's edit fuzzy normalization (core/tools/edit-diff.ts normalizeForFuzzyMatch, not exported). */
7
+ export function normalizeForMatch(text) {
8
+ return text
9
+ .normalize("NFKC")
10
+ .split("\n")
11
+ .map((line) => line.trimEnd())
12
+ .join("\n")
13
+ .replace(/[‘’‚‛]/g, "'")
14
+ .replace(/[“”„‟]/g, '"')
15
+ .replace(/[‐‑‒–—―−]/g, "-")
16
+ .replace(/[  -    ]/g, " ");
17
+ }
18
+ /** File text as Pi's edit matches it: without a BOM and with LF line endings. */
19
+ export function editableText(raw) {
20
+ return raw.replace(/^/, "").replace(/\r\n/g, "\n").replace(/\r/g, "\n");
21
+ }
22
+ function bigrams(text) {
23
+ const counts = new Map();
24
+ for (let i = 0; i < text.length - 1; i++) {
25
+ const pair = text.slice(i, i + 2);
26
+ counts.set(pair, (counts.get(pair) ?? 0) + 1);
27
+ }
28
+ return counts;
29
+ }
30
+ function lineSimilarity(a, b) {
31
+ const x = a.trim(),
32
+ y = b.trim();
33
+ if (x === y) return 1;
34
+ if (x.length < 2 || y.length < 2) return 0;
35
+ const left = bigrams(x),
36
+ right = bigrams(y);
37
+ let shared = 0;
38
+ for (const [pair, count] of left) shared += Math.min(count, right.get(pair) ?? 0);
39
+ return (2 * shared) / (x.length - 1 + (y.length - 1));
40
+ }
41
+ /** The window of the file most similar to the old text, as 1-based inclusive lines, or undefined when none is close. */
42
+ export function closestMatch(fileText, oldText) {
43
+ const lines = fileText.split("\n");
44
+ const wanted = oldText.replace(/\n+$/, "").split("\n");
45
+ const size = Math.min(wanted.length, lines.length);
46
+ if (size === 0) return undefined;
47
+ let best;
48
+ for (let start = 0; start + size <= lines.length; start++) {
49
+ let total = 0;
50
+ for (let i = 0; i < size; i++) total += lineSimilarity(lines[start + i], wanted[i]);
51
+ const score = total / wanted.length;
52
+ if (!best || score > best.score) best = { start, score };
53
+ }
54
+ return best && best.score >= MIN_SIMILARITY ? { start: best.start + 1, end: best.start + size } : undefined;
55
+ }
56
+ /** 1-based start lines of every place the old text matches, compared as Pi's edit compares it. */
57
+ export function matchingLines(fileText, oldText) {
58
+ const haystack = normalizeForMatch(fileText),
59
+ needle = normalizeForMatch(oldText);
60
+ const found = [];
61
+ if (!needle) return found;
62
+ for (let index = haystack.indexOf(needle); index >= 0; index = haystack.indexOf(needle, index + 1))
63
+ found.push(haystack.slice(0, index).split("\n").length);
64
+ return found;
65
+ }
66
+ function shownLines(fileText, start, end) {
67
+ return fileText
68
+ .split("\n")
69
+ .slice(start - 1, Math.min(end, start - 1 + MAX_SHOWN_LINES))
70
+ .map((line) => (line.length > MAX_LINE_CHARS ? `${line.slice(0, MAX_LINE_CHARS)}…` : line))
71
+ .join("\n");
72
+ }
73
+ const NOT_FOUND = /^Could not find (?:the exact text|edits\[(\d+)\]) in (.+?)\. The old ?[tT]ext must match exactly/;
74
+ const NOT_UNIQUE = /^Found (\d+) occurrences of (?:the text|edits\[(\d+)\]) in (.+?)\. /;
75
+ /**
76
+ * Replace Pi's not-found or not-unique edit error with one literal next action. Returns undefined when the error is
77
+ * another kind (overlap, empty, no change) or Pi worded it differently, so the result stays exactly as Pi wrote it.
78
+ */
79
+ export function rewriteEditError(error, path, edits, fileText) {
80
+ const notFound = NOT_FOUND.exec(error);
81
+ if (notFound) {
82
+ const index = notFound[1] === undefined ? 0 : Number(notFound[1]);
83
+ const subject = notFound[1] === undefined ? path : `${path} edits[${index}]`;
84
+ const oldText = editableText(String(edits[index]?.oldText ?? ""));
85
+ const match = closestMatch(fileText, oldText);
86
+ if (!match)
87
+ return `${subject}: old text not found and no similar lines exist in the current file. Read the region you meant, then retry.`;
88
+ return [
89
+ `${subject}: old text not found. Closest match, lines ${match.start}–${match.end} (current file contents):`,
90
+ shownLines(fileText, match.start, match.end),
91
+ "If these are the lines you meant, retry with old text copied exactly from them. If not, read the region you meant, then retry.",
92
+ ].join("\n");
93
+ }
94
+ const notUnique = NOT_UNIQUE.exec(error);
95
+ if (notUnique) {
96
+ const index = notUnique[2] === undefined ? 0 : Number(notUnique[2]);
97
+ const subject = notUnique[2] === undefined ? path : `${path} edits[${index}]`;
98
+ const lines = matchingLines(fileText, editableText(String(edits[index]?.oldText ?? "")));
99
+ const places = lines.length > 0 ? lines.length : Number(notUnique[1]);
100
+ const where = lines.length > 0 ? ` (lines ${lines.join(", ")})` : "";
101
+ return `${subject}: old text matches ${places} places${where}. To change one place, add surrounding lines until it matches only that place. To change every place, give one edits[] entry per place, each with enough surrounding lines to match only that place.`;
102
+ }
103
+ return undefined;
104
+ }
105
+ export const writeRefusal = (path) =>
106
+ `${path} exists and has not been read in this session. Read ${path}, then retry the write.`;
107
+ export const changedAfterEdit = (path) => `${path} changed since you read it; the edit applied to the current text.`;
108
+ export const changedAfterWrite = (path) =>
109
+ `${path} changed since you read it; the write replaced the current contents.`;
110
+ export const noNeedToReread = (path) => `No need to re-read ${path} to confirm this edit.`;
111
+ /** Files the model read that changed or disappeared outside its own edits, as a fact and one next action. */
112
+ export function changedFilesNotice(changed, deleted) {
113
+ const lines = [];
114
+ if (changed.length > 0)
115
+ lines.push(
116
+ `Files you read earlier changed since you read them: ${changed.join(", ")}. Read what you need from them again before editing them.`,
117
+ );
118
+ if (deleted.length > 0) lines.push(`Files you read earlier no longer exist: ${deleted.join(", ")}.`);
119
+ return lines.length > 0 ? lines.join("\n") : undefined;
120
+ }
@@ -0,0 +1,138 @@
1
+ import { createHash } from "node:crypto";
2
+ import { existsSync, realpathSync } from "node:fs";
3
+ import { readFile, stat } from "node:fs/promises";
4
+ import { homedir } from "node:os";
5
+ import { isAbsolute, resolve } from "node:path";
6
+ // Files above this size are identified by size and modification time instead of a content hash.
7
+ export const HASH_LIMIT_BYTES = 8 * 1024 * 1024;
8
+ const UNICODE_SPACES = /[  -    ]/g;
9
+ /** Resolve a tool path the way Pi's tools do: strip `@`, normalize odd spaces, expand `~`, resolve against cwd. */
10
+ export function resolveToolPath(path, cwd) {
11
+ let candidate = path.replace(UNICODE_SPACES, " ");
12
+ if (candidate.startsWith("@")) candidate = candidate.slice(1);
13
+ if (candidate === "~") candidate = homedir();
14
+ else if (candidate.startsWith("~/")) candidate = `${homedir()}${candidate.slice(1)}`;
15
+ const resolved = isAbsolute(candidate) ? resolve(candidate) : resolve(cwd, candidate);
16
+ if (existsSync(resolved)) return resolved;
17
+ // Pi's read also finds macOS names typed differently: narrow space before AM/PM, NFD, curly apostrophe.
18
+ for (const variant of [
19
+ resolved.replace(/ (AM|PM)\./gi, " $1."),
20
+ resolved.normalize("NFD"),
21
+ resolved.replace(/'/g, "’"),
22
+ ])
23
+ if (variant !== resolved && existsSync(variant)) return variant;
24
+ return resolved;
25
+ }
26
+ function fileKey(path, cwd) {
27
+ const resolved = resolveToolPath(path, cwd);
28
+ try {
29
+ return realpathSync(resolved);
30
+ } catch {
31
+ return resolved;
32
+ }
33
+ }
34
+ async function fingerprint(file) {
35
+ let info;
36
+ try {
37
+ info = await stat(file);
38
+ } catch {
39
+ return "absent";
40
+ }
41
+ if (!info.isFile()) return "absent";
42
+ if (info.size > HASH_LIMIT_BYTES) return { size: info.size, mtimeMs: info.mtimeMs };
43
+ const bytes = await readFile(file);
44
+ return { sha256: createHash("sha256").update(bytes).digest("hex"), size: bytes.length };
45
+ }
46
+ async function statOf(file) {
47
+ try {
48
+ const info = await stat(file);
49
+ return info.isFile() ? { size: info.size, mtimeMs: info.mtimeMs } : "absent";
50
+ } catch {
51
+ return "absent";
52
+ }
53
+ }
54
+ const sameStat = (a, b) =>
55
+ a !== undefined && (a === "absent" || b === "absent" ? a === b : a.size === b.size && a.mtimeMs === b.mtimeMs);
56
+ function sameFingerprint(a, b) {
57
+ if (a === "absent" || b === "absent") return a === b;
58
+ if ("sha256" in a && "sha256" in b) return a.sha256 === b.sha256;
59
+ if ("mtimeMs" in a && "mtimeMs" in b) return a.size === b.size && a.mtimeMs === b.mtimeMs;
60
+ return false;
61
+ }
62
+ /** The files a successful tool call read or changed, from a tool call's name and arguments. */
63
+ export function touchedPaths(toolName, input) {
64
+ if (["read", "edit", "write"].includes(toolName)) return typeof input?.path === "string" ? [input.path] : [];
65
+ if (toolName === "apply_patch" && typeof input?.input === "string")
66
+ return [...input.input.matchAll(/^\s*\*\*\* (?:Add File|Update File|Delete File|Move to): (.+?)\s*$/gm)].map(
67
+ (match) => match[1],
68
+ );
69
+ return [];
70
+ }
71
+ /**
72
+ * Which files the model has read or written on the current branch, and their content at that moment. Held in memory
73
+ * and rebuilt from the branch after navigation or resume; never persisted and never consulted on the request path.
74
+ */
75
+ export class FileState {
76
+ files = new Map();
77
+ async observe(path, cwd, via) {
78
+ const key = fileKey(path, cwd);
79
+ const at = await statOf(key);
80
+ this.files.set(key, { fingerprint: await fingerprint(key), via, stat: at });
81
+ }
82
+ wasRead(path, cwd) {
83
+ return this.files.has(fileKey(path, cwd));
84
+ }
85
+ /** Whether the file differs from its last observation; false when it was never observed. */
86
+ async changedSinceObserved(path, cwd) {
87
+ const key = fileKey(path, cwd);
88
+ const known = this.files.get(key);
89
+ if (!known) return false;
90
+ return !sameFingerprint(known.fingerprint, await fingerprint(key));
91
+ }
92
+ /**
93
+ * Observed files whose content changed since the model last read or wrote them, each reported once per change.
94
+ * Costs one stat per observed file; a file is hashed only when its size or modification time moved.
95
+ */
96
+ async changedSinceNoticed() {
97
+ const changed = [];
98
+ for (const [key, known] of this.files) {
99
+ const at = await statOf(key);
100
+ if (sameStat(known.stat, at)) continue;
101
+ known.stat = at;
102
+ const now = await fingerprint(key);
103
+ if (sameFingerprint(known.fingerprint, now)) continue;
104
+ if (known.noticed !== undefined && sameFingerprint(known.noticed, now)) continue;
105
+ known.noticed = now;
106
+ changed.push({ path: key, deleted: now === "absent" });
107
+ }
108
+ return changed;
109
+ }
110
+ /**
111
+ * Mark every file a successful read, edit, write or patch on the branch touched, with today's content. A Freeflow
112
+ * compaction starts over: the model then holds only the files it carried.
113
+ */
114
+ async rebuild(branch, cwd) {
115
+ this.files.clear();
116
+ const calls = new Map();
117
+ const touched = new Set();
118
+ for (const entry of branch) {
119
+ if (entry?.type === "compaction" && entry.details?.freeflow) {
120
+ touched.clear();
121
+ for (const item of entry.details.freeflow.carried ?? [])
122
+ if (item?.kind === "file" && typeof item.path === "string" && !item.error) touched.add(item.path);
123
+ }
124
+ const message = entry?.type === "message" ? entry.message : undefined;
125
+ if (message?.role === "assistant")
126
+ for (const block of message.content ?? [])
127
+ if (block?.type === "toolCall") calls.set(block.id, { name: block.name, input: block.arguments });
128
+ if (message?.role === "toolResult" && message.isError !== true) {
129
+ const call = calls.get(message.toolCallId);
130
+ if (call) for (const path of touchedPaths(call.name, call.input)) touched.add(path);
131
+ }
132
+ }
133
+ for (const path of touched) await this.observe(path, cwd, "rebuilt");
134
+ }
135
+ reset() {
136
+ this.files.clear();
137
+ }
138
+ }
@@ -0,0 +1,143 @@
1
+ import { realpathSync, statSync } from "node:fs";
2
+ import { isAbsolute, relative } from "node:path";
3
+ import { readFile } from "node:fs/promises";
4
+ import { FileState, resolveToolPath } from "./file-state.js";
5
+ import {
6
+ changedAfterEdit,
7
+ changedAfterWrite,
8
+ changedFilesNotice,
9
+ editableText,
10
+ noNeedToReread,
11
+ rewriteEditError,
12
+ writeRefusal,
13
+ } from "./edit-messages.js";
14
+ import { cachedBranch } from "../host/branch.js";
15
+ function existingFile(path, cwd) {
16
+ try {
17
+ return statSync(resolveToolPath(path, cwd)).isFile();
18
+ } catch {
19
+ return false;
20
+ }
21
+ }
22
+ // Tools whose own targets are observed after they run; any other tool may change files behind the model's back.
23
+ const TRACKED_TOOLS = ["read", "edit", "write", "apply_patch"];
24
+ /** A tracked file (keyed by real path) as the model would name it: relative to the working directory when inside. */
25
+ function shown(path, cwd) {
26
+ let real = cwd;
27
+ try {
28
+ real = realpathSync(cwd);
29
+ } catch {}
30
+ for (const base of [real, cwd]) {
31
+ const inside = relative(base, path);
32
+ if (inside && !inside.startsWith("..") && !isAbsolute(inside)) return inside;
33
+ }
34
+ return path;
35
+ }
36
+ function withLines(content, lines) {
37
+ const text = content.map((part) => (part?.type === "text" ? part.text : "")).join("");
38
+ return [
39
+ { type: "text", text: [text, ...lines].filter(Boolean).join("\n") },
40
+ ...content.filter((part) => part?.type !== "text"),
41
+ ];
42
+ }
43
+ /**
44
+ * Claude Code's file awareness around Pi's read, edit and write: refuse a whole-file write over a file never read,
45
+ * say when an edited file changed since it was read, and turn edit errors into one next action. Nested calls
46
+ * (codemode) are handled like direct ones.
47
+ */
48
+ export class FileTracking {
49
+ state = new FileState();
50
+ changed = new Map();
51
+ built = false;
52
+ reset() {
53
+ this.state.reset();
54
+ this.changed.clear();
55
+ this.built = false;
56
+ }
57
+ /** Forget the branch's files; they are rebuilt from the new branch before the next tool call. */
58
+ invalidate() {
59
+ this.built = false;
60
+ }
61
+ clearPending() {
62
+ this.changed.clear();
63
+ }
64
+ /** After a Freeflow compaction the model holds only the carried files, read at compaction time. */
65
+ async compacted(carried, ctx) {
66
+ this.state.reset();
67
+ this.changed.clear();
68
+ for (const path of carried) await this.state.observe(path, ctx.cwd, "read");
69
+ this.built = true;
70
+ }
71
+ /** The branch's file state for apply_patch, built when needed. */
72
+ async forPatch(ctx) {
73
+ await this.ensureBuilt(ctx);
74
+ return this.state;
75
+ }
76
+ /** Record files a successful apply_patch wrote or moved, as a successful write records them. */
77
+ async written(paths, ctx) {
78
+ await this.ensureBuilt(ctx);
79
+ for (const path of paths) await this.state.observe(path, ctx.cwd, "apply_patch");
80
+ }
81
+ async ensureBuilt(ctx) {
82
+ if (this.built) return;
83
+ await this.state.rebuild(cachedBranch(ctx.sessionManager) ?? [], ctx.cwd);
84
+ this.built = true;
85
+ }
86
+ async toolCall(event, ctx) {
87
+ const name = event.toolName;
88
+ const path = event.input?.path;
89
+ if (!["edit", "write"].includes(name) || typeof path !== "string") return undefined;
90
+ await this.ensureBuilt(ctx);
91
+ if (name === "write" && existingFile(path, ctx.cwd) && !this.state.wasRead(path, ctx.cwd))
92
+ return { block: true, reason: writeRefusal(path) };
93
+ if (await this.state.changedSinceObserved(path, ctx.cwd)) this.changed.set(event.toolCallId, path);
94
+ return undefined;
95
+ }
96
+ /** Files the model read that changed since, not yet reported; undefined when there are none. */
97
+ async changedNotice(ctx, build = true) {
98
+ // Before a prompt nothing is rebuilt: a branch not yet rebuilt has no baseline to compare against.
99
+ if (!build && !this.built) return undefined;
100
+ await this.ensureBuilt(ctx);
101
+ const found = await this.state.changedSinceNoticed();
102
+ return changedFilesNotice(
103
+ found.filter((file) => !file.deleted).map((file) => shown(file.path, ctx.cwd)),
104
+ found.filter((file) => file.deleted).map((file) => shown(file.path, ctx.cwd)),
105
+ );
106
+ }
107
+ async toolResult(event, ctx) {
108
+ const name = event.toolName;
109
+ // A command (or any other tool) may have changed files the model read. Nested calls are reported on the
110
+ // result of the script that made them, which is what the model sees.
111
+ if (!TRACKED_TOOLS.includes(name)) {
112
+ if (event.parentToolCallId) return undefined;
113
+ const notice = await this.changedNotice(ctx);
114
+ return notice === undefined ? undefined : { content: withLines(event.content ?? [], [notice]) };
115
+ }
116
+ const path = event.input?.path;
117
+ if (!["read", "edit", "write"].includes(name) || typeof path !== "string") return undefined;
118
+ await this.ensureBuilt(ctx);
119
+ const changed = this.changed.get(event.toolCallId);
120
+ this.changed.delete(event.toolCallId);
121
+ if (event.isError) {
122
+ if (name !== "edit") return undefined;
123
+ let fileText;
124
+ try {
125
+ fileText = editableText(await readFile(resolveToolPath(path, ctx.cwd), "utf8"));
126
+ } catch {
127
+ return undefined;
128
+ }
129
+ const error = (event.content ?? []).map((part) => (part?.type === "text" ? part.text : "")).join("");
130
+ const rewritten = rewriteEditError(error, path, event.input?.edits ?? [], fileText);
131
+ return rewritten === undefined ? undefined : { content: [{ type: "text", text: rewritten }] };
132
+ }
133
+ await this.state.observe(path, ctx.cwd, name);
134
+ if (name === "read") return undefined;
135
+ const lines =
136
+ name === "edit"
137
+ ? [...(changed ? [changedAfterEdit(path)] : []), noNeedToReread(path)]
138
+ : changed
139
+ ? [changedAfterWrite(path)]
140
+ : [];
141
+ return lines.length > 0 ? { content: withLines(event.content ?? [], lines) } : undefined;
142
+ }
143
+ }
@@ -0,0 +1,54 @@
1
+ import { homedir, platform, release } from "node:os";
2
+ import { getShellConfig } from "@earendil-works/pi-coding-agent";
3
+ // Pi's generic rule lines that contradict the taught bash-first working style or repeat Freeflow's interaction
4
+ // contract. Every other rule, including each tool's and extension's guidelines, stays as Pi renders it.
5
+ const REMOVED_RULES = new Set([
6
+ "- Use read to examine files instead of cat or sed.",
7
+ "- Be concise in your responses",
8
+ "- Show file paths clearly when working with files",
9
+ ]);
10
+ function renderedSection(systemPrompt, name) {
11
+ const match = new RegExp(`<${name}>\\n([\\s\\S]*?)\\n</${name}>`).exec(systemPrompt);
12
+ return match?.[1];
13
+ }
14
+ function rules(systemPrompt) {
15
+ const rendered = renderedSection(systemPrompt, "rules");
16
+ if (rendered === undefined) return undefined;
17
+ const lines = rendered.split("\n");
18
+ const kept = lines.filter((line) => !REMOVED_RULES.has(line));
19
+ return kept.length === lines.length || kept.length === 0 ? undefined : kept.join("\n");
20
+ }
21
+ function docs(systemPrompt) {
22
+ const rendered = renderedSection(systemPrompt, "docs");
23
+ if (rendered === undefined) return undefined;
24
+ const path = (label) => new RegExp(`^- ${label}: (.+?)(?: \\(|$)`, "m").exec(rendered)?.[1];
25
+ const [readme, docsPath, examples] = [path("Main documentation"), path("Additional docs"), path("Examples")];
26
+ if (!readme || !docsPath || !examples) return undefined;
27
+ return `Pi documentation (read only when the user asks about pi itself): ${readme}, ${docsPath}, ${examples}.`;
28
+ }
29
+ function environment(shellPath) {
30
+ let shell;
31
+ try {
32
+ shell = getShellConfig(shellPath?.startsWith("~/") ? `${homedir()}${shellPath.slice(1)}` : shellPath).shell;
33
+ } catch {
34
+ shell = "unavailable";
35
+ }
36
+ return `Platform: ${platform()} ${release()}. Shell for bash: ${shell}.`;
37
+ }
38
+ /**
39
+ * Pi prompt sections Freeflow owns while Tool Execution is effective. Every value derives from inputs fixed for the
40
+ * configuration and machine, so the system prompt stays identical across requests and sessions. Pi's preamble is
41
+ * never touched: the Claude subscription plugin shapes it only when Pi's own preamble is present.
42
+ */
43
+ export function toolExecutionSections(systemPrompt, shellPath) {
44
+ const sections = {};
45
+ const ownRules = rules(systemPrompt);
46
+ if (ownRules !== undefined) sections.rules = ownRules;
47
+ const ownDocs = docs(systemPrompt);
48
+ if (ownDocs !== undefined) sections.docs = ownDocs;
49
+ return sections;
50
+ }
51
+ /** Sections Tool Execution adds after Freeflow's guidance: the environment facts, then its guidance when available. */
52
+ export function toolExecutionTail(shellPath, guidance) {
53
+ return { environment: environment(shellPath), ...(guidance ? { tool_execution: guidance } : {}) };
54
+ }
@@ -0,0 +1,16 @@
1
+ import { APPLY_PATCH } from "./apply-patch/tool.js";
2
+ import { BASH_BACKGROUND, STOP_BACKGROUND } from "./background.js";
3
+ /** Freeflow's own Tool Execution tools; registered once and declared exactly while Tool Execution is effective. */
4
+ export const TOOL_EXECUTION_TOOLS = [APPLY_PATCH, BASH_BACKGROUND, STOP_BACKGROUND];
5
+ /**
6
+ * Declare or withdraw Freeflow's tools when Tool Execution's effective state changes. Missing tools are appended and
7
+ * existing ones keep their place, so repeated calls leave the tool list unchanged within a configuration.
8
+ */
9
+ export function applyToolExecutionTools(pi, effective) {
10
+ if (typeof pi.getActiveTools !== "function" || typeof pi.setActiveTools !== "function") return;
11
+ const current = pi.getActiveTools();
12
+ const next = effective
13
+ ? [...current, ...TOOL_EXECUTION_TOOLS.filter((name) => !current.includes(name))]
14
+ : current.filter((name) => !TOOL_EXECUTION_TOOLS.includes(name));
15
+ if (next.length !== current.length) pi.setActiveTools(next);
16
+ }
@@ -0,0 +1,9 @@
1
+ # Extension entry point
2
+
3
+ `index.js` is the file Pi loads: the root `package.json` lists it under `pi.extensions`. It only re-exports the compiled extension:
4
+
5
+ ```js
6
+ export { default } from "../dist/index.js";
7
+ ```
8
+
9
+ Keeping this stable path separate from `dist/` lets the build delete and recreate `dist/` without changing what the package manifest points to. Do not add logic here; the extension starts in [`src/index.ts`](../src/index.ts).
package/plugin.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
3
3
  "name": "freeflow",
4
- "version": "0.7.3",
4
+ "version": "0.8.0",
5
5
  "description": "Feedback-based control system for coding agents.",
6
6
  "author": {
7
7
  "name": "Hassan Mohiddin"
@@ -1,9 +1,9 @@
1
1
  ## Cognitive Routing Cue
2
2
 
3
- Cognitive Routing places work across configured profiles in one agent/session to improve quality, continuity, responsiveness, and total cost together. Coordinator owns user interaction, governing direction, delegation, and acceptance. When enabled, Helper is the normal delegate for supporting work; Executor is commissioned for substantive or consequential results, including implementation and artifacts. Helper and Executor share ordinary active history; only Coordinator delegates and only one profile executes at a time.
3
+ Cognitive Routing places work across configured profiles in one agent/session to improve quality and total cost together. Coordinator owns user interaction, planning, governing decisions, delegation, assessment, and acceptance. Helper and Executor are workers: only Coordinator delegates, only one profile executes at a time, and the workers share ordinary active history.
4
4
 
5
- Use the latest Runtime State for control, profile, delegation mode, and current responsibility. In Automatic control, follow the selected mode: helper-only permits Coordinator substantive production with Helper support; executor-only delegates environment work to Executor; both uses Helper by default for support and Executor for substantive work. A mode change does not retarget an accepted assignment or its recovery. Under Manual control, the held Coordinator, Helper, or Executor follows ordinary unsplit Workflow without Automatic role restrictions. Do not release the user's hold. When routing is inactive, stop applying this split.
5
+ Use the latest Runtime State for control, profile, delegation mode, and current responsibility. In Automatic control, the delegation mode sets who implements: `helper` has Coordinator implement while Helper takes everything that does not need Coordinator-level reasoning; `executor` has Executor do the environment work from Coordinator's contracts; `both` has Executor do consequential work, including code, and Helper do support. A mode change does not retarget an accepted assignment or its recovery. Completed or superseded contracts and reports are historical context and grant no new permission; follow the current assignment and the user's current restrictions. Under Manual control, the held Coordinator, Helper, or Executor follows ordinary unsplit Workflow without Automatic role restrictions. Do not release the user's hold. When routing is inactive, stop applying this split.
6
6
 
7
- Before relying on Automatic routing, read the complete cognitive-routing skill if its body is absent. This bootstrap read is the only environment call in that response. If unavailable, stop and report the missing method. Reload it after context loss before applying remembered routing rules.
7
+ Before relying on Automatic routing, read the complete cognitive-routing skill if its body is absent. This bootstrap read is the only environment call in that response. If unavailable, stop and report the missing method. The skill then directs you to its reference for the current delegation mode. Reload it after context loss before applying remembered routing rules.
8
8
 
9
- When projection is on, the active worker must project every completed skill and instructional-reference read, including this bootstrap read. Follow the skill for work placement, environment access, evidence, handoffs, and recorded-worker recovery. Under Automatic routing, a worker return or passing check is not Coordinator acceptance; completed Slice closure requires Coordinator's supported judgment and explicit closure instruction. Manual control follows ordinary Workflow and Track Work instead.
9
+ When projection is on, the active worker must project every completed skill and instructional-reference read, including this bootstrap read. Follow the skill for work placement, environment access, evidence, handoffs, and recorded-worker recovery. Under Automatic routing, a worker return or passing check is not Coordinator acceptance; completed Slice closure requires Coordinator's assessment and explicit closure direction. Manual control follows ordinary Workflow and Track Work instead.
@@ -1,90 +1,37 @@
1
1
  # Freeflow Stable Guidance
2
2
 
3
- Freeflow is a workflow layer for one active coding agent. It does not override user instructions, repository policy, host safety, or tool permissions, and it creates no authority. Core guidance and the separately editable Interaction Contract are delivered together whenever Freeflow is enabled.
3
+ Freeflow is a workflow layer for one coding agent. It never overrides user instructions, repository instructions, host safety, or tool permissions, and it grants no authority of its own.
4
4
 
5
- Use the latest Freeflow Runtime State and only the guidance, skills, and tools exposed for this request. State refreshes at session start, after context loss, or when displayed facts change; an unchanged snapshot may remain visible. Earlier snapshots are history.
5
+ Use the latest Freeflow Runtime State and only the guidance, skills, and tools exposed for this request. The state refreshes at session start, after context loss, and when its facts change; earlier snapshots are history.
6
6
 
7
- ## Shared Terms
7
+ ## Terms
8
8
 
9
- - **Authority envelope:** the requested outcome, permitted effects, covered active evidence generation, and stop condition established by a direct request or still-valid approval.
10
- - **Work agreement:** the user-established outcome, scope, and user-facing return condition. It may include figuring out the approach; it is not a mandatory artifact or another authority source.
11
- - **Passive observation:** inspecting existing sources or evidence without exercising target behavior or intentionally changing task state.
12
- - **Active evidence generation:** exercising behavior to produce evidence, including tests, reproductions, benchmarks, prototypes, instrumentation, and runtime probes.
13
- - **Mutation or delivery:** changing repository, durable task/session, or external state.
14
- - **Bounded activity:** coherent discussion, preservation, execution, evidence, or judgment ending in one assessable result.
15
- - **Current owner:** the one activity responsible for that result. Supporting methods, lenses, tools, references, and reviewers do not automatically take ownership.
16
- - **Slice:** one coherent outcome that may span several bounded activities. Track Work gives it durable identity when task memory is needed.
17
- - **Evidence boundary:** the strongest claim directly supported by the observing mechanism—not by intent, authority, or confidence.
18
- - **Self-review:** the producer silently checks an initially supported result for alignment, correctness, suitability, and unnecessary complexity before accepting or reusing it. Corrected work receives only the affected recheck.
19
- - **Independent review:** separately selected judgment from a context that did not produce the reviewed state. It reports without editing or authorizing correction.
20
- - **Checkpoint:** a deliberately selected boundary that dependent work must not cross unresolved. An activity ending or pausing does not create one automatically.
21
- - **Re-entry:** return only to the owner whose responsibility changed, preserving unaffected work, decisions, and evidence.
22
- - **Supported Exit:** an answer, wait, pause, handoff, deferment, controlled boundary, stop, or completion justified by current evidence and authority.
9
+ - **Work agreement:** the outcome, scope, and return point the user agreed to. Only the user's request or still-valid approval establishes it.
10
+ - **Active evidence generation:** exercising behavior to produce evidence, such as tests, reproductions, experiments, or probes. Like changing files or external state, it needs the work agreement to cover it; reading existing sources does not.
11
+ - **Current owner:** the one skill responsible for the current question or result.
12
+ - **Working Record:** the task's durable memory file, maintained through Track Work.
13
+ - **Slice:** one coherent unit of work in a Working Record.
14
+ - **Checkpoint:** a boundary on the task's route, such as a commit, publication, review, user decision, or planned handoff. Once selected, dependent work must not cross it unresolved.
15
+ - **Evidence boundary:** the strongest claim an observation directly supports.
16
+ - **Self-review:** the producer's own check of a supported result before accepting it. **Independent review:** a separately selected judgment from a context that did not produce the work.
23
17
 
24
18
  ## Load The Selected Method
25
19
 
26
- Before applying a selected skill, read its current body when its exact method is absent from context. Descriptions select methods; they do not replace them. Reuse still-visible guidance instead of rereading each turn.
27
-
28
- Read dependencies at their declared conditions, not the whole catalogue. Loading guidance neither changes ownership nor authorizes effects. Required reads still follow active capability bootstrap and routing rules.
20
+ Before applying a selected skill, read its current body when it is not already in context; a description selects a skill but is not its method. Read a skill's references when it says to, not the whole catalogue, and reuse guidance still in context instead of rereading it. Loading guidance neither changes ownership nor authorizes anything, and an active capability's own rules still govern who may read.
29
21
 
30
22
  ## Recover After Context Loss
31
23
 
32
- After compaction, context-replacing summarization, clear, session resume/navigation, transfer into another context, or uncertain continuity, pause task work. Use only necessary recovery reads until state is coherent; recovery is not a bypass of authority, capabilities, or host permissions.
33
-
34
- - Require the latest Freeflow Runtime State; do not infer missing or contradictory state.
35
- - Reload relevant active capability methods before relying on them.
36
- - When a Working Record exists or may exist, load Track Work and read the complete `full` record. A summary or `resume` projection is not full recovery. Stop affected work if recovery is incomplete or unavailable.
37
- - Identify the current owner and reload its method if absent. Use Workflow when authority, ownership, or continuation is unclear.
38
- - Reconcile the agreement, current work, dependencies, evidence limits, partial effects, and stop conditions with current user direction and relevant live sources.
39
-
40
- Summaries and records may preserve evidence of prior approval; check that the approval still applies. They do not create authority, prove live state, or keep an absent method active. Current user direction, live source truth, and current Runtime State take precedence over conflicting memory. With intact context, use bounded record reads only when needed; another turn or ordinary pause is not context loss.
41
-
42
- ## Three Nested Loops
43
-
44
- The **Interaction Lifecycle** runs from Entry through the Feedback Loop when needed to a Supported Exit. A later user turn or new evidence begins another lifecycle. One lifecycle may contain several bounded activities.
45
-
46
- For each bounded activity, the **Feedback Loop** is:
47
-
48
- ```text
49
- Orient or reconstruct state
50
- -> choose or retain the current owner
51
- -> establish the required result and a supported approach
52
- -> apply its method and gather or produce evidence
53
- -> determine what the evidence supports
54
- -> self-review the supported result
55
- -> continue, correct, re-enter, ask, defer, stop, or exit
56
- ```
57
-
58
- Reuse settled understanding. For concrete work, resolve what is required and how to produce the next result before production changes; these need not be separate investigations or user turns. A learning action answers its bounded question, not an expanding production ambition.
59
-
60
- When the owner needs the environment, the **Environment Interaction Loop** is:
61
-
62
- ```text
63
- Need evidence or a covered effect
64
- -> reuse adequate context or identify what is missing
65
- -> select and bound the action and tool
66
- -> execute once
67
- -> observe what changed
68
- -> apply active capability guidance where relevant
69
- -> return to the current owner
70
- ```
71
-
72
- This inner loop may run zero or more times per activity. It does not change authority or ownership.
73
-
74
- ## Evidence And Judgment
75
-
76
- Verification establishes what direct evidence proves at the observed boundary. Review judges whether work or an artifact is aligned, correct, suitable, and sufficiently evidenced; judgment does not replace verification. A passing check may leave a broader claim unsupported. Preserve missing, contradictory, and inconclusive evidence rather than converting it into success.
77
-
78
- ## Workflow Cue
79
-
80
- Use Workflow when authority, readiness, ownership, re-entry, checkpoints, continuity, or Supported Exit needs coordination. Continue covered work to the agreed return boundary; internal method returns do not end the user agreement.
81
-
82
- Before an uncovered effect or separately controlled action, explain its purpose, action, expected result, and stop condition; ask once and wait. Skills, artifacts, reviews, memory, and useful new evidence do not grant authority.
24
+ After compaction, a summary that replaces earlier context, clear, session resume or navigation, transfer into another context, or uncertain continuity, pause task work and recover first. Use only the reads recovery needs; recovery is not a bypass of authority, capabilities, or host permissions.
83
25
 
84
- ## Action Selection Cue
26
+ - Use the latest Freeflow Runtime State; do not infer missing or contradictory state.
27
+ - Reload the methods the current work needs.
28
+ - When a Working Record exists or may exist for the current work, load Track Work, confirm the record belongs to that work, and read the complete `full` record, then the artifacts listed under its `What defines this task` and its `Recovery sources`. A summary or `resume` view is not full recovery. If recovery is incomplete, report that and stop affected work.
29
+ - Reconcile the record with current user direction, live state, and the current Runtime State, which override it where they conflict. A summary or record may preserve evidence of prior approval; check that the approval still applies. Neither creates authority.
85
30
 
86
- Use Action Selection before an uncertain, broad, or repeated environment interaction. Seek the smallest sufficient observation or effect for the current question, bound its output, and return what changed to the owner. Use the fast path for an obvious covered mechanical action; do not manufacture alternatives or collect context after the question is settled.
31
+ Then continue the covered work toward the agreed end. Context loss is not a reason to stop or to ask for permission again; stop only at the agreed end, a real blocker, or a decision the user owns. With intact context, another turn or an ordinary pause is not context loss.
87
32
 
88
- ## Supported Exit
33
+ ## Cues
89
34
 
90
- Exit only when evidence and authority support it, applicable self-review and selected checkpoints are resolved, and required task memory and artifacts are accurate. Make material gaps, contradictions, deferrals, and user-owned decisions explicit. Report the outcome, evidence, limits, and current route.
35
+ - Use Workflow when the work agreement, the current owner, readiness to act, a changed route, continuity, or the end of the work needs coordinating.
36
+ - Use Action Selection before an uncertain, broad, or repeated environment interaction. Take the direct action when it is obvious.
37
+ - Use Decision Gate when proceeding would make a choice the user owns.