@hassangameryt/freeflow 0.7.3 → 0.9.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 (426) hide show
  1. package/.agents/plugins/marketplace.json +2 -2
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/.claude-plugin/plugin.json +2 -2
  4. package/.codex-plugin/plugin.json +3 -3
  5. package/.cursor-plugin/plugin.json +1 -1
  6. package/NOTICE +14 -0
  7. package/README.md +101 -159
  8. package/capabilities/cognitive-routing/SKILL.md +47 -210
  9. package/capabilities/cognitive-routing/references/coordinator.md +163 -0
  10. package/capabilities/cognitive-routing/references/worker.md +38 -0
  11. package/capabilities/compaction/SKILL.md +49 -0
  12. package/capabilities/compaction/references/summary-format.md +250 -0
  13. package/capabilities/messaging/SKILL.md +49 -0
  14. package/capabilities/task-memory/SKILL.md +87 -0
  15. package/command-surface.json +8 -52
  16. package/gemini-extension.json +1 -1
  17. package/hooks/claude/hooks.json +13 -3
  18. package/hooks/codex/hooks.json +2 -1
  19. package/hooks/shared/runtime-context.mjs +73 -83
  20. package/licenses/Apache-2.0.txt +55 -0
  21. package/package.json +42 -12
  22. package/pi-extension/README.md +41 -0
  23. package/pi-extension/dist/ask-user/ask.js +108 -0
  24. package/pi-extension/dist/ask-user/form.js +250 -0
  25. package/pi-extension/dist/ask-user/questions.js +66 -0
  26. package/pi-extension/dist/ask-user/tool.js +124 -0
  27. package/pi-extension/dist/cognitive-routing/assembler.js +417 -0
  28. package/pi-extension/dist/cognitive-routing/bootstrap.js +90 -0
  29. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/config.js +8 -6
  30. package/pi-extension/dist/cognitive-routing/economics.js +45 -0
  31. package/pi-extension/dist/cognitive-routing/event-schema.js +388 -0
  32. package/pi-extension/dist/{session-sources/events.js → cognitive-routing/event-store.js} +107 -41
  33. package/pi-extension/dist/cognitive-routing/gate.js +256 -0
  34. package/pi-extension/dist/cognitive-routing/handlers.js +901 -0
  35. package/pi-extension/dist/cognitive-routing/handoffs.js +350 -0
  36. package/pi-extension/dist/cognitive-routing/model-control.js +476 -0
  37. package/pi-extension/dist/cognitive-routing/projection.js +520 -0
  38. package/pi-extension/dist/cognitive-routing/provenance.js +83 -0
  39. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/render.js +15 -15
  40. package/pi-extension/dist/cognitive-routing/runtime.js +398 -0
  41. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/schemas.js +47 -11
  42. package/pi-extension/dist/cognitive-routing/session.js +223 -0
  43. package/pi-extension/dist/{session-sources → cognitive-routing}/sources.js +126 -87
  44. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/state.js +55 -386
  45. package/pi-extension/dist/cognitive-routing/status.js +81 -0
  46. package/pi-extension/dist/cognitive-routing/tools.js +74 -0
  47. package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/types.js +14 -11
  48. package/pi-extension/dist/compaction/carry.js +205 -0
  49. package/pi-extension/dist/compaction/config.js +24 -0
  50. package/pi-extension/dist/compaction/controller.js +494 -0
  51. package/pi-extension/dist/compaction/harness.js +93 -0
  52. package/pi-extension/dist/compaction/results.js +159 -0
  53. package/pi-extension/dist/compaction/thresholds.js +35 -0
  54. package/pi-extension/dist/compaction/tool.js +57 -0
  55. package/pi-extension/dist/host/branch.js +13 -0
  56. package/pi-extension/dist/host/canonical.js +12 -0
  57. package/pi-extension/dist/host/catalog.js +62 -0
  58. package/pi-extension/dist/host/config-validation.js +233 -0
  59. package/pi-extension/dist/host/config.js +361 -0
  60. package/pi-extension/dist/host/inbound.js +142 -0
  61. package/pi-extension/dist/host/projection-tags.js +60 -0
  62. package/pi-extension/dist/host/prompts.js +93 -0
  63. package/pi-extension/dist/{session-sources → host}/read-only-session.js +117 -1
  64. package/pi-extension/dist/{runtime → host}/request-history.js +125 -39
  65. package/pi-extension/dist/host/rows.js +25 -0
  66. package/pi-extension/dist/host/runtime-state.js +204 -0
  67. package/pi-extension/dist/host/section-keeper.js +85 -0
  68. package/pi-extension/dist/host/settings/freeflow-command.js +2323 -0
  69. package/pi-extension/dist/{settings/settings-tui.js → host/settings/settings-view.js} +205 -73
  70. package/pi-extension/dist/host/staging.js +28 -0
  71. package/pi-extension/dist/host/status.js +115 -0
  72. package/pi-extension/dist/index.js +1004 -188
  73. package/pi-extension/dist/messaging/config.js +27 -0
  74. package/pi-extension/dist/messaging/messaging.js +398 -0
  75. package/pi-extension/dist/messaging/registry.js +57 -0
  76. package/pi-extension/dist/messaging/render.js +90 -0
  77. package/pi-extension/dist/messaging/tools.js +109 -0
  78. package/pi-extension/dist/messaging/transport.js +101 -0
  79. package/pi-extension/dist/provider-support/cache/anchor.js +21 -0
  80. package/pi-extension/dist/provider-support/cache/anthropic.js +102 -0
  81. package/pi-extension/dist/provider-support/cache/health.js +73 -0
  82. package/pi-extension/dist/provider-support/cache/index.js +58 -0
  83. package/pi-extension/dist/provider-support/cache/keepalive.js +222 -0
  84. package/pi-extension/dist/provider-support/cache/monitor.js +17 -0
  85. package/pi-extension/dist/provider-support/cache/openai-responses.js +48 -0
  86. package/pi-extension/dist/provider-support/effort.js +13 -0
  87. package/pi-extension/dist/provider-support/index.js +44 -3
  88. package/pi-extension/dist/provider-support/{astra → openai}/adapter.js +46 -29
  89. package/pi-extension/dist/provider-support/{astra → openai}/history.js +10 -8
  90. package/pi-extension/dist/provider-support/{astra → openai}/index.js +8 -4
  91. package/pi-extension/dist/provider-support/{astra → openai}/session-state.js +29 -38
  92. package/pi-extension/dist/provider-support/routes.js +17 -0
  93. package/pi-extension/dist/task-memory/config.js +49 -0
  94. package/pi-extension/dist/task-memory/registry.js +119 -0
  95. package/pi-extension/dist/task-memory/render.js +107 -0
  96. package/pi-extension/dist/task-memory/runtime.js +994 -0
  97. package/pi-extension/dist/task-memory/tool.js +233 -0
  98. package/pi-extension/dist/tool-execution/apply-patch/grammar.js +23 -0
  99. package/pi-extension/dist/tool-execution/apply-patch/match.js +38 -0
  100. package/pi-extension/dist/tool-execution/apply-patch/parser.js +177 -0
  101. package/pi-extension/dist/tool-execution/apply-patch/plan.js +201 -0
  102. package/pi-extension/dist/tool-execution/apply-patch/tool.js +84 -0
  103. package/pi-extension/dist/tool-execution/background.js +430 -0
  104. package/pi-extension/dist/tool-execution/bash-guard.js +23 -0
  105. package/pi-extension/dist/tool-execution/config.js +33 -0
  106. package/pi-extension/dist/tool-execution/edit-messages.js +120 -0
  107. package/pi-extension/dist/tool-execution/file-state.js +138 -0
  108. package/pi-extension/dist/tool-execution/file-tracking.js +163 -0
  109. package/pi-extension/dist/tool-execution/guidance-notices.js +124 -0
  110. package/pi-extension/dist/tool-execution/prompt.js +54 -0
  111. package/pi-extension/dist/tool-execution/skill-reads.js +143 -0
  112. package/pi-extension/dist/tool-execution/tools.js +16 -0
  113. package/pi-extension/freeflow/README.md +9 -0
  114. package/runtime/prompts/cognitive-routing.md +4 -4
  115. package/runtime/prompts/core.md +29 -72
  116. package/runtime/prompts/interaction-contract.md +18 -8
  117. package/runtime/prompts/tool-execution.md +25 -0
  118. package/runtime/prompts/working-method.md +25 -0
  119. package/skills/deliver-work/SKILL.md +76 -0
  120. package/skills/deliver-work/references/branch-closeout.md +134 -0
  121. package/skills/deliver-work/references/commit.md +83 -0
  122. package/skills/deliver-work/references/launch.md +94 -0
  123. package/skills/deliver-work/references/release.md +85 -0
  124. package/skills/deliver-work/scripts/commit-state.mjs +139 -0
  125. package/skills/deliver-work/scripts/delivery-state.mjs +107 -0
  126. package/skills/deliver-work/scripts/lib/git-state.mjs +178 -0
  127. package/skills/deliver-work/scripts/push-route.mjs +122 -0
  128. package/skills/deliver-work/scripts/release-preflight.mjs +177 -0
  129. package/skills/discuss-work/SKILL.md +148 -0
  130. package/skills/{discuss → discuss-work}/references/discussion-continuity.md +13 -50
  131. package/skills/discuss-work/references/learning-action.md +36 -0
  132. package/skills/discuss-work/scripts/choices.mjs +101 -0
  133. package/skills/discuss-work/scripts/lib/choices.mjs +159 -0
  134. package/skills/evaluate-behavior/SKILL.md +107 -0
  135. package/skills/evaluate-behavior/references/authoring.md +381 -0
  136. package/skills/evaluate-behavior/references/coding-tasks.md +145 -0
  137. package/skills/evaluate-behavior/references/conversations-pipelines-judges.md +177 -0
  138. package/skills/evaluate-behavior/references/design-and-measurement.md +273 -0
  139. package/skills/evaluate-behavior/references/running-and-evidence.md +245 -0
  140. package/skills/evaluate-behavior/scripts/claude-guard.mjs +76 -0
  141. package/skills/evaluate-behavior/scripts/guard-rules.mjs +309 -0
  142. package/skills/evaluate-behavior/scripts/lib/agreement.mjs +90 -0
  143. package/skills/evaluate-behavior/scripts/lib/arms.mjs +55 -0
  144. package/skills/evaluate-behavior/scripts/lib/budget.mjs +87 -0
  145. package/skills/evaluate-behavior/scripts/lib/build-source.mjs +287 -0
  146. package/skills/evaluate-behavior/scripts/lib/clear.mjs +74 -0
  147. package/skills/evaluate-behavior/scripts/lib/container.mjs +395 -0
  148. package/skills/evaluate-behavior/scripts/lib/decision.mjs +418 -0
  149. package/skills/evaluate-behavior/scripts/lib/definitions.mjs +1072 -0
  150. package/skills/evaluate-behavior/scripts/lib/freeflow.mjs +103 -0
  151. package/skills/evaluate-behavior/scripts/lib/grade-command.mjs +591 -0
  152. package/skills/evaluate-behavior/scripts/lib/grade-comparison.mjs +90 -0
  153. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-interaction.mjs +19 -18
  154. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-support.mjs +32 -5
  155. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-workspace.mjs +28 -12
  156. package/skills/evaluate-behavior/scripts/lib/grade.mjs +101 -0
  157. package/skills/evaluate-behavior/scripts/lib/hooks.mjs +98 -0
  158. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/confinement.mjs +196 -0
  159. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/constraints.mjs +111 -0
  160. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/index.mjs +48 -0
  161. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/isolation.mjs +74 -0
  162. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/launch.mjs +123 -0
  163. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/model-call.mjs +73 -0
  164. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/recorder.mjs +178 -0
  165. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/session.mjs +494 -0
  166. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/skills.mjs +81 -0
  167. package/skills/evaluate-behavior/scripts/lib/hosts/index.mjs +31 -0
  168. package/skills/evaluate-behavior/scripts/lib/hosts/names.mjs +3 -0
  169. package/skills/evaluate-behavior/scripts/lib/hosts/pi/confinement.mjs +173 -0
  170. package/skills/evaluate-behavior/scripts/lib/hosts/pi/host.mjs +148 -0
  171. package/skills/evaluate-behavior/scripts/lib/hosts/pi/index.mjs +70 -0
  172. package/skills/evaluate-behavior/scripts/lib/hosts/pi/isolation.mjs +81 -0
  173. package/skills/evaluate-behavior/scripts/lib/hosts/pi/model-call.mjs +79 -0
  174. package/skills/evaluate-behavior/scripts/lib/hosts/pi/process.mjs +241 -0
  175. package/skills/{evaluate-skill/scripts/lib → evaluate-behavior/scripts/lib/hosts/pi}/rpc.mjs +144 -26
  176. package/skills/evaluate-behavior/scripts/lib/identity.mjs +82 -0
  177. package/skills/evaluate-behavior/scripts/lib/judge.mjs +688 -0
  178. package/skills/evaluate-behavior/scripts/lib/metrics.mjs +332 -0
  179. package/skills/evaluate-behavior/scripts/lib/model-call.mjs +17 -0
  180. package/skills/evaluate-behavior/scripts/lib/participants.mjs +616 -0
  181. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/process.mjs +22 -0
  182. package/skills/evaluate-behavior/scripts/lib/queue.mjs +54 -0
  183. package/skills/evaluate-behavior/scripts/lib/reference.mjs +343 -0
  184. package/skills/evaluate-behavior/scripts/lib/reuse.mjs +277 -0
  185. package/skills/evaluate-behavior/scripts/lib/review.mjs +933 -0
  186. package/skills/evaluate-behavior/scripts/lib/runner.mjs +732 -0
  187. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/sandbox.mjs +121 -14
  188. package/skills/evaluate-behavior/scripts/lib/scan-shell.mjs +457 -0
  189. package/skills/evaluate-behavior/scripts/lib/scan.mjs +479 -0
  190. package/skills/evaluate-behavior/scripts/lib/seal.mjs +91 -0
  191. package/skills/evaluate-behavior/scripts/lib/shell.mjs +560 -0
  192. package/skills/evaluate-behavior/scripts/lib/source-override.mjs +64 -0
  193. package/skills/evaluate-behavior/scripts/lib/subject-file.mjs +88 -0
  194. package/skills/{evaluate-skill/scripts/lib/pi.mjs → evaluate-behavior/scripts/lib/subject.mjs} +239 -262
  195. package/skills/evaluate-behavior/scripts/lib/task-copy.mjs +124 -0
  196. package/skills/evaluate-behavior/scripts/lib/trials.mjs +524 -0
  197. package/skills/evaluate-behavior/scripts/lib/validity.mjs +68 -0
  198. package/skills/evaluate-behavior/scripts/lib/view-html.mjs +454 -0
  199. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/view.mjs +218 -11
  200. package/skills/evaluate-behavior/scripts/pi-command-tool.mjs +159 -0
  201. package/skills/evaluate-behavior/scripts/pi-container-route.mjs +23 -0
  202. package/skills/evaluate-behavior/scripts/pi-guard.mjs +77 -0
  203. package/skills/evaluate-behavior/scripts/pi-login-refresh.mjs +31 -0
  204. package/skills/evaluate-behavior/scripts/skill-eval.mjs +586 -0
  205. package/skills/execute-work/SKILL.md +96 -72
  206. package/skills/execute-work/references/code-practices.md +27 -49
  207. package/skills/execute-work/references/design.md +271 -0
  208. package/skills/execute-work/references/diagnose.md +117 -0
  209. package/skills/execute-work/references/migration.md +108 -0
  210. package/skills/execute-work/references/simplify.md +65 -0
  211. package/skills/execute-work/scripts/self-review.mjs +422 -0
  212. package/skills/execute-work/scripts/touch-map.mjs +290 -0
  213. package/skills/review-work/SKILL.md +94 -64
  214. package/skills/review-work/references/adjudication.md +94 -0
  215. package/skills/review-work/references/artifact.md +198 -0
  216. package/skills/review-work/references/evals-and-judges.md +66 -0
  217. package/skills/review-work/references/work.md +177 -0
  218. package/skills/review-work/scripts/check-brief.mjs +128 -0
  219. package/skills/review-work/scripts/review-packet.mjs +233 -0
  220. package/skills/setup-freeflow/SKILL.md +69 -16
  221. package/skills/track-work/SKILL.md +135 -151
  222. package/skills/track-work/references/handoff.md +190 -0
  223. package/skills/track-work/references/projects.md +51 -0
  224. package/skills/track-work/references/working-record-format.md +201 -344
  225. package/skills/track-work/scripts/lib/document.mjs +79 -19
  226. package/skills/track-work/scripts/lib/format.mjs +18 -1
  227. package/skills/track-work/scripts/lib/recover.mjs +254 -0
  228. package/skills/track-work/scripts/lib/registry.mjs +656 -0
  229. package/skills/track-work/scripts/lib/search.mjs +40 -0
  230. package/skills/track-work/scripts/lib/store.mjs +53 -21
  231. package/skills/track-work/scripts/lib/transitions.mjs +79 -4
  232. package/skills/track-work/scripts/lib/views.mjs +50 -1
  233. package/skills/track-work/scripts/working-record.mjs +263 -22
  234. package/skills/verify-work/SKILL.md +64 -47
  235. package/skills/verify-work/references/feature-map.md +120 -0
  236. package/skills/verify-work/references/runtime-evidence.md +203 -0
  237. package/skills/verify-work/references/test-design.md +27 -6
  238. package/skills/verify-work/scripts/test-changes.mjs +246 -0
  239. package/skills/write-docs/SKILL.md +171 -0
  240. package/skills/write-docs/references/documentation-shapes.md +184 -0
  241. package/skills/write-docs/references/spec-and-plan-shapes.md +308 -0
  242. package/skills/write-docs/scripts/artifact-check.mjs +148 -0
  243. package/skills/write-docs/scripts/docs-check.mjs +229 -0
  244. package/skills/write-docs/scripts/plan-lint.mjs +123 -0
  245. package/skills/write-skill/SKILL.md +165 -165
  246. package/skills/write-skill/references/activation-and-wording.md +143 -0
  247. package/skills/write-skill/references/placement-and-dependencies.md +154 -0
  248. package/skills/write-skill/scripts/lib/compare.mjs +701 -0
  249. package/skills/write-skill/scripts/lib/similarity.mjs +56 -0
  250. package/skills/write-skill/scripts/lib/skill-author.mjs +197 -3
  251. package/skills/write-skill/scripts/skill-author.mjs +67 -8
  252. package/capabilities/context-virtualization/SKILL.md +0 -103
  253. package/capabilities/conversation-history/SKILL.md +0 -53
  254. package/hooks/tests/check-host-adapters.test.mjs +0 -82
  255. package/hooks/tests/check-runtime-context-hook.sh +0 -212
  256. package/pi-extension/dist/cognitive-routing-v2/projection.js +0 -330
  257. package/pi-extension/dist/cognitive-routing-v2/runtime.js +0 -2181
  258. package/pi-extension/dist/cognitive-routing-v2/tools.js +0 -60
  259. package/pi-extension/dist/context-virtualization/commands.js +0 -89
  260. package/pi-extension/dist/context-virtualization/projector.js +0 -42
  261. package/pi-extension/dist/context-virtualization/runtime.js +0 -378
  262. package/pi-extension/dist/context-virtualization/state.js +0 -57
  263. package/pi-extension/dist/context-virtualization/types.js +0 -10
  264. package/pi-extension/dist/conversation-history/passages.js +0 -66
  265. package/pi-extension/dist/conversation-history/ranking.js +0 -105
  266. package/pi-extension/dist/conversation-history/runtime.js +0 -226
  267. package/pi-extension/dist/conversation-history/search.js +0 -56
  268. package/pi-extension/dist/conversation-history/types.js +0 -1
  269. package/pi-extension/dist/freeflow-context/resolver.js +0 -120
  270. package/pi-extension/dist/freeflow-context/runtime.js +0 -80
  271. package/pi-extension/dist/freeflow-context/source-projector.js +0 -95
  272. package/pi-extension/dist/freeflow-context/tool.js +0 -314
  273. package/pi-extension/dist/freeflow-context/types.js +0 -12
  274. package/pi-extension/dist/freeflow-context/visibility.js +0 -28
  275. package/pi-extension/dist/integrations/mcp-config.js +0 -29
  276. package/pi-extension/dist/runtime/runtime-context.js +0 -754
  277. package/pi-extension/dist/runtime/runtime-identity.js +0 -5
  278. package/pi-extension/dist/session-sources/provenance.js +0 -32
  279. package/pi-extension/dist/settings/settings-ui.js +0 -1360
  280. package/pi-extension/dist/ui/text-component.js +0 -57
  281. package/pi-extension/src/cognitive-routing-v2/budget.ts +0 -42
  282. package/pi-extension/src/cognitive-routing-v2/config.ts +0 -173
  283. package/pi-extension/src/cognitive-routing-v2/projection.ts +0 -375
  284. package/pi-extension/src/cognitive-routing-v2/render.ts +0 -277
  285. package/pi-extension/src/cognitive-routing-v2/runtime.ts +0 -2241
  286. package/pi-extension/src/cognitive-routing-v2/schemas.ts +0 -180
  287. package/pi-extension/src/cognitive-routing-v2/state.ts +0 -839
  288. package/pi-extension/src/cognitive-routing-v2/tools.ts +0 -62
  289. package/pi-extension/src/cognitive-routing-v2/types.ts +0 -254
  290. package/pi-extension/src/context-virtualization/commands.ts +0 -111
  291. package/pi-extension/src/context-virtualization/projector.ts +0 -62
  292. package/pi-extension/src/context-virtualization/runtime.ts +0 -428
  293. package/pi-extension/src/context-virtualization/state.ts +0 -79
  294. package/pi-extension/src/context-virtualization/types.ts +0 -92
  295. package/pi-extension/src/conversation-history/passages.ts +0 -81
  296. package/pi-extension/src/conversation-history/ranking.ts +0 -155
  297. package/pi-extension/src/conversation-history/runtime.ts +0 -250
  298. package/pi-extension/src/conversation-history/search.ts +0 -79
  299. package/pi-extension/src/conversation-history/types.ts +0 -31
  300. package/pi-extension/src/freeflow-context/resolver.ts +0 -139
  301. package/pi-extension/src/freeflow-context/runtime.ts +0 -100
  302. package/pi-extension/src/freeflow-context/source-projector.ts +0 -106
  303. package/pi-extension/src/freeflow-context/tool.ts +0 -366
  304. package/pi-extension/src/freeflow-context/types.ts +0 -70
  305. package/pi-extension/src/freeflow-context/visibility.ts +0 -53
  306. package/pi-extension/src/index.ts +0 -390
  307. package/pi-extension/src/integrations/mcp-config.ts +0 -32
  308. package/pi-extension/src/provider-support/astra/adapter.ts +0 -108
  309. package/pi-extension/src/provider-support/astra/history.ts +0 -145
  310. package/pi-extension/src/provider-support/astra/index.ts +0 -12
  311. package/pi-extension/src/provider-support/astra/session-state.ts +0 -113
  312. package/pi-extension/src/provider-support/index.ts +0 -5
  313. package/pi-extension/src/runtime/request-history.ts +0 -243
  314. package/pi-extension/src/runtime/runtime-context.ts +0 -860
  315. package/pi-extension/src/runtime/runtime-identity.ts +0 -8
  316. package/pi-extension/src/session-sources/events.ts +0 -230
  317. package/pi-extension/src/session-sources/provenance.ts +0 -39
  318. package/pi-extension/src/session-sources/read-only-session.ts +0 -170
  319. package/pi-extension/src/session-sources/sources.ts +0 -334
  320. package/pi-extension/src/settings/settings-tui.ts +0 -501
  321. package/pi-extension/src/settings/settings-ui.ts +0 -1652
  322. package/pi-extension/src/ui/text-component.ts +0 -66
  323. package/pi-extension/tests/cache-reuse/history.test.js +0 -117
  324. package/pi-extension/tests/cache-reuse/native.test.js +0 -91
  325. package/pi-extension/tests/cache-reuse/settings.test.js +0 -211
  326. package/pi-extension/tests/cognitive-routing-v2/communication.test.js +0 -577
  327. package/pi-extension/tests/cognitive-routing-v2/compaction-input.test.js +0 -183
  328. package/pi-extension/tests/cognitive-routing-v2/delegation.test.js +0 -557
  329. package/pi-extension/tests/cognitive-routing-v2/native.test.js +0 -474
  330. package/pi-extension/tests/cognitive-routing-v2/performance.test.js +0 -87
  331. package/pi-extension/tests/cognitive-routing-v2/persistence.test.js +0 -300
  332. package/pi-extension/tests/cognitive-routing-v2/projection.test.js +0 -241
  333. package/pi-extension/tests/cognitive-routing-v2/recovery.test.js +0 -1034
  334. package/pi-extension/tests/cognitive-routing-v2/render.test.js +0 -244
  335. package/pi-extension/tests/cognitive-routing-v2/runtime.test.js +0 -423
  336. package/pi-extension/tests/cognitive-routing-v2/stabilization.test.js +0 -521
  337. package/pi-extension/tests/cognitive-routing-v2/state.test.js +0 -586
  338. package/pi-extension/tests/context-virtualization/extension.test.js +0 -775
  339. package/pi-extension/tests/context-virtualization/projector.test.js +0 -110
  340. package/pi-extension/tests/context-virtualization/runtime.test.js +0 -230
  341. package/pi-extension/tests/conversation-history/runtime.test.js +0 -98
  342. package/pi-extension/tests/conversation-history/search.test.js +0 -97
  343. package/pi-extension/tests/fixtures/pi-host.js +0 -4
  344. package/pi-extension/tests/fixtures/routing-native.js +0 -191
  345. package/pi-extension/tests/integration/mode-free-core.test.js +0 -220
  346. package/pi-extension/tests/integration/pi-extension.test.js +0 -1447
  347. package/pi-extension/tests/integration/prompt-architecture.test.js +0 -447
  348. package/pi-extension/tests/integration/unaffected-regressions.test.js +0 -478
  349. package/pi-extension/tests/provider-support/astra-boundaries.test.js +0 -75
  350. package/pi-extension/tests/provider-support/astra.test.js +0 -306
  351. package/pi-extension/tests/provider-support/cli.test.js +0 -92
  352. package/pi-extension/tests/provider-support/native.test.js +0 -183
  353. package/pi-extension/tests/settings/settings-tui.test.js +0 -243
  354. package/pi-extension/tsconfig.json +0 -16
  355. package/plugin.json +0 -33
  356. package/runtime/prompts/context-virtualization.md +0 -3
  357. package/runtime/prompts/conversation-history.md +0 -3
  358. package/skills/action-selection/SKILL.md +0 -100
  359. package/skills/action-selection/references/trajectory-stalls.md +0 -71
  360. package/skills/bypass/SKILL.md +0 -153
  361. package/skills/commit-work/SKILL.md +0 -145
  362. package/skills/commit-work/references/git-state-edges.md +0 -94
  363. package/skills/commit-work/references/simple-push.md +0 -92
  364. package/skills/commit-work/references/staging-decisions.md +0 -106
  365. package/skills/decision-gate/SKILL.md +0 -106
  366. package/skills/design-for-depth/SKILL.md +0 -117
  367. package/skills/design-for-depth/references/design-pressure-signals.md +0 -182
  368. package/skills/design-for-depth/references/implementation-decisions.md +0 -76
  369. package/skills/design-for-depth/references/interface-design-loop.md +0 -136
  370. package/skills/design-for-depth/references/module-and-dependency-design.md +0 -147
  371. package/skills/design-for-depth/references/software-design-philosophy.md +0 -202
  372. package/skills/design-for-depth/references/state-and-failure-boundaries.md +0 -136
  373. package/skills/diagnose-failure/SKILL.md +0 -125
  374. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +0 -48
  375. package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +0 -51
  376. package/skills/discuss/SKILL.md +0 -116
  377. package/skills/evaluate-skill/SKILL.md +0 -219
  378. package/skills/evaluate-skill/references/evaluation-definition-schema.md +0 -385
  379. package/skills/evaluate-skill/references/evaluation-design.md +0 -112
  380. package/skills/evaluate-skill/references/execution-and-evidence.md +0 -90
  381. package/skills/evaluate-skill/references/review-and-revision.md +0 -92
  382. package/skills/evaluate-skill/scripts/lib/definitions.mjs +0 -407
  383. package/skills/evaluate-skill/scripts/lib/grade-comparison.mjs +0 -60
  384. package/skills/evaluate-skill/scripts/lib/grade.mjs +0 -64
  385. package/skills/evaluate-skill/scripts/lib/runner.mjs +0 -368
  386. package/skills/evaluate-skill/scripts/pi-guard.mjs +0 -164
  387. package/skills/evaluate-skill/scripts/skill-eval.mjs +0 -102
  388. package/skills/execute-work/references/execute-work-edges.md +0 -68
  389. package/skills/finish-branch/SKILL.md +0 -117
  390. package/skills/finish-branch/references/integration-options.md +0 -80
  391. package/skills/handoff/SKILL.md +0 -82
  392. package/skills/handoff/references/handoff-templates.md +0 -150
  393. package/skills/launch-work/SKILL.md +0 -116
  394. package/skills/launch-work/references/launch-readiness.md +0 -85
  395. package/skills/migration-work/SKILL.md +0 -115
  396. package/skills/migration-work/references/migration-lifecycle.md +0 -82
  397. package/skills/release-work/SKILL.md +0 -118
  398. package/skills/release-work/references/release-evidence.md +0 -78
  399. package/skills/review-artifact/SKILL.md +0 -142
  400. package/skills/review-artifact/references/adjudicate-artifact-review.md +0 -101
  401. package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +0 -159
  402. package/skills/review-work/references/adjudicate-work-review.md +0 -97
  403. package/skills/review-work/references/independent-work-reviewer-contract.md +0 -139
  404. package/skills/review-work/references/security-risk-lens.md +0 -69
  405. package/skills/setup-freeflow/references/activation-contract.md +0 -109
  406. package/skills/setup-freeflow/references/host-setup.md +0 -67
  407. package/skills/simplify-code/SKILL.md +0 -85
  408. package/skills/simplify-code/references/simplification-patterns.md +0 -70
  409. package/skills/verify-work/references/browser-runtime-evidence.md +0 -55
  410. package/skills/verify-work/references/integration-evidence.md +0 -47
  411. package/skills/verify-work/references/performance-evidence.md +0 -57
  412. package/skills/workflow/SKILL.md +0 -127
  413. package/skills/workflow/references/domain-skill-composition.md +0 -43
  414. package/skills/write-plan/SKILL.md +0 -92
  415. package/skills/write-plan/references/plan-shapes.md +0 -156
  416. package/skills/write-skill/references/activation-boundaries.md +0 -104
  417. package/skills/write-skill/references/agent-first-instructions.md +0 -121
  418. package/skills/write-skill/references/progressive-disclosure.md +0 -127
  419. package/skills/write-skill/references/skill-dependency-graphs.md +0 -190
  420. package/skills/write-spec/SKILL.md +0 -92
  421. package/skills/write-spec/references/artifact-standards.md +0 -86
  422. package/skills/write-spec/references/decision-records.md +0 -78
  423. package/skills/write-spec/references/spec-shapes.md +0 -143
  424. /package/pi-extension/dist/{cognitive-routing-v2 → cognitive-routing}/budget.js +0 -0
  425. /package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/evidence.mjs +0 -0
  426. /package/skills/{evaluate-skill → evaluate-behavior}/scripts/pi-observer.mjs +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,163 @@
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 { SkillReads } from "./skill-reads.js";
15
+ import { cachedBranch } from "../host/branch.js";
16
+ function existingFile(path, cwd) {
17
+ try {
18
+ return statSync(resolveToolPath(path, cwd)).isFile();
19
+ } catch {
20
+ return false;
21
+ }
22
+ }
23
+ // Tools whose own targets are observed after they run; any other tool may change files behind the model's back.
24
+ const TRACKED_TOOLS = ["read", "edit", "write", "apply_patch"];
25
+ /** A tracked file (keyed by real path) as the model would name it: relative to the working directory when inside. */
26
+ function shown(path, cwd) {
27
+ let real = cwd;
28
+ try {
29
+ real = realpathSync(cwd);
30
+ } catch {}
31
+ for (const base of [real, cwd]) {
32
+ const inside = relative(base, path);
33
+ if (inside && !inside.startsWith("..") && !isAbsolute(inside)) return inside;
34
+ }
35
+ return path;
36
+ }
37
+ /** A tool result's content with lines appended to its text (before delivery: nothing already sent changes). */
38
+ export function withLines(content, lines) {
39
+ const text = content.map((part) => (part?.type === "text" ? part.text : "")).join("");
40
+ return [
41
+ { type: "text", text: [text, ...lines].filter(Boolean).join("\n") },
42
+ ...content.filter((part) => part?.type !== "text"),
43
+ ];
44
+ }
45
+ /**
46
+ * Claude Code's file awareness around Pi's read, edit and write: refuse a whole-file write over a file never read,
47
+ * say when an edited file changed since it was read, turn edit errors into one next action, and answer a repeat
48
+ * read of a Freeflow instruction file the model already holds with one line. Nested calls (codemode) are handled like
49
+ * direct ones, except that a nested read is never answered for the model: the script consumes its text.
50
+ */
51
+ export class FileTracking {
52
+ state = new FileState();
53
+ changed = new Map();
54
+ skillReads = new SkillReads();
55
+ built = false;
56
+ reset() {
57
+ this.state.reset();
58
+ this.changed.clear();
59
+ this.skillReads.reset();
60
+ this.built = false;
61
+ }
62
+ /** Forget the branch's files; they are rebuilt from the new branch before the next tool call. */
63
+ invalidate() {
64
+ this.built = false;
65
+ this.skillReads.invalidate();
66
+ }
67
+ /** After Pi's own compaction the files are unchanged, but the instruction files read before it are out of view. */
68
+ piCompacted() {
69
+ this.skillReads.clear();
70
+ }
71
+ clearPending() {
72
+ this.changed.clear();
73
+ }
74
+ /** After a Freeflow compaction the model holds only the carried files, read at compaction time. */
75
+ async compacted(carried, ctx) {
76
+ this.state.reset();
77
+ this.changed.clear();
78
+ for (const path of carried) await this.state.observe(path, ctx.cwd, "read");
79
+ this.built = true;
80
+ this.skillReads.clear();
81
+ }
82
+ /** Freeflow skills the model holds in this context, by name; reads are rebuilt from the branch when needed. */
83
+ loadedSkills(ctx) {
84
+ if (!this.skillReads.built) this.skillReads.rebuild(cachedBranch(ctx.sessionManager) ?? [], ctx.cwd);
85
+ return this.skillReads.skills();
86
+ }
87
+ /** The branch's file state for apply_patch, built when needed. */
88
+ async forPatch(ctx) {
89
+ await this.ensureBuilt(ctx);
90
+ return this.state;
91
+ }
92
+ /** Record files a successful apply_patch wrote or moved, as a successful write records them. */
93
+ async written(paths, ctx) {
94
+ await this.ensureBuilt(ctx);
95
+ for (const path of paths) await this.state.observe(path, ctx.cwd, "apply_patch");
96
+ }
97
+ async ensureBuilt(ctx) {
98
+ if (!this.skillReads.built) this.skillReads.rebuild(cachedBranch(ctx.sessionManager) ?? [], ctx.cwd);
99
+ if (this.built) return;
100
+ await this.state.rebuild(cachedBranch(ctx.sessionManager) ?? [], ctx.cwd);
101
+ this.built = true;
102
+ }
103
+ async toolCall(event, ctx) {
104
+ const name = event.toolName;
105
+ const path = event.input?.path;
106
+ if (!["edit", "write"].includes(name) || typeof path !== "string") return undefined;
107
+ await this.ensureBuilt(ctx);
108
+ if (name === "write" && existingFile(path, ctx.cwd) && !this.state.wasRead(path, ctx.cwd))
109
+ return { block: true, reason: writeRefusal(path) };
110
+ if (await this.state.changedSinceObserved(path, ctx.cwd)) this.changed.set(event.toolCallId, path);
111
+ return undefined;
112
+ }
113
+ /** Files the model read that changed since, not yet reported; undefined when there are none. */
114
+ async changedNotice(ctx, build = true) {
115
+ // Before a prompt nothing is rebuilt: a branch not yet rebuilt has no baseline to compare against.
116
+ if (!build && !this.built) return undefined;
117
+ await this.ensureBuilt(ctx);
118
+ const found = await this.state.changedSinceNoticed();
119
+ return changedFilesNotice(
120
+ found.filter((file) => !file.deleted).map((file) => shown(file.path, ctx.cwd)),
121
+ found.filter((file) => file.deleted).map((file) => shown(file.path, ctx.cwd)),
122
+ );
123
+ }
124
+ async toolResult(event, ctx) {
125
+ const name = event.toolName;
126
+ // A command (or any other tool) may have changed files the model read. Nested calls are reported on the
127
+ // result of the script that made them, which is what the model sees.
128
+ if (!TRACKED_TOOLS.includes(name)) {
129
+ if (event.parentToolCallId) return undefined;
130
+ const notice = await this.changedNotice(ctx);
131
+ return notice === undefined ? undefined : { content: withLines(event.content ?? [], [notice]) };
132
+ }
133
+ const path = event.input?.path;
134
+ if (!["read", "edit", "write"].includes(name) || typeof path !== "string") return undefined;
135
+ await this.ensureBuilt(ctx);
136
+ const changed = this.changed.get(event.toolCallId);
137
+ this.changed.delete(event.toolCallId);
138
+ if (event.isError) {
139
+ if (name !== "edit") return undefined;
140
+ let fileText;
141
+ try {
142
+ fileText = editableText(await readFile(resolveToolPath(path, ctx.cwd), "utf8"));
143
+ } catch {
144
+ return undefined;
145
+ }
146
+ const error = (event.content ?? []).map((part) => (part?.type === "text" ? part.text : "")).join("");
147
+ const rewritten = rewriteEditError(error, path, event.input?.edits ?? [], fileText);
148
+ return rewritten === undefined ? undefined : { content: [{ type: "text", text: rewritten }] };
149
+ }
150
+ await this.state.observe(path, ctx.cwd, name);
151
+ if (name === "read") {
152
+ this.skillReads.observe(event, ctx.cwd);
153
+ return undefined;
154
+ }
155
+ const lines =
156
+ name === "edit"
157
+ ? [...(changed ? [changedAfterEdit(path)] : []), noNeedToReread(path)]
158
+ : changed
159
+ ? [changedAfterWrite(path)]
160
+ : [];
161
+ return lines.length > 0 ? { content: withLines(event.content ?? [], lines) } : undefined;
162
+ }
163
+ }
@@ -0,0 +1,124 @@
1
+ import { execFile } from "node:child_process";
2
+ import { join } from "node:path";
3
+ import { freeflowNotice } from "../host/inbound.js";
4
+ import { isFreeflowInstruction } from "./skill-reads.js";
5
+ /**
6
+ * Guidance pushed at the moment it is needed (the push retriever, Task 004 D-003): a short Freeflow notice appended to
7
+ * the result of the tool call that makes it relevant, so it reaches the agent whether or not it read the skill that
8
+ * owns it.
9
+ *
10
+ * - A `git commit` result: Deliver Work's commit essentials, plus Write Skill's loss check when the commit staged
11
+ * model-facing instruction text (read from Git at that result). Once per run.
12
+ * - The second failure of the same test command: diagnose's two key lines. Once per command until it passes.
13
+ * - A test command that passes after the run changed a file: run Execute Work's self-review before the final report.
14
+ * Once per run, and not after self-review ran.
15
+ * - Any tool result while Execute Work's body is not in the model's context (never read, or dropped by a compaction):
16
+ * read it before more tool calls. Once per run, and not on a read of Freeflow's own instruction files, so reading
17
+ * Discuss Work first is not interrupted.
18
+ *
19
+ * Cache: a notice is part of a tool result that has not been delivered yet, the same path as Tool Execution's
20
+ * changed-files line; nothing already sent changes. Request path: none of this runs per request. State is in memory,
21
+ * O(1) per tool result, bounded (at most FAILURE_KEYS test commands), and reset per run where it says so.
22
+ */
23
+ const FAILURE_KEYS = 32;
24
+ const TEST_COMMAND =
25
+ /(^|[\s;&|(])((npm|pnpm|yarn|bun)\b[^;&|]*\btest(:[\w:-]+)?\b|node\s+(\S+\s+)*--test\b|(pytest|jest|vitest|mocha|rspec|phpunit|ctest|tox)\b|go\s+test\b|cargo\s+(test|nextest)\b|(mvn|gradle|\.\/gradlew)\b[^;&|]*\btest\b|make\s+(test|check)\b|dotnet\s+test\b|bundle\s+exec\s+rspec\b|python3?\s+-m\s+(pytest|unittest)\b)/;
26
+ const GIT_COMMIT = /(^|[\s;&|(])git(\s+(-C|-c)\s+\S+)*\s+commit\b(?![^;&|]*--help)/;
27
+ const CHANGES_FILES = new Set(["edit", "write", "apply_patch"]);
28
+ // Model-facing instruction text, as Write Skill's loss check reads it: skill packages, capabilities, runtime prompts
29
+ // and instruction files.
30
+ const INSTRUCTION_TEXT =
31
+ /(^|\/)(SKILL\.md|AGENTS\.md|CLAUDE\.md)$|^(skills|capabilities)\/.+\.md$|(^|\/)prompts\/[^/]+\.md$/;
32
+ export const COMMIT_NOTICE =
33
+ "Committing (Deliver Work): check what the commit holds with `git show --stat --oneline HEAD` and `git status --short --branch`: only the paths this claim needs, staged explicitly, since staged does not mean yours. Never commit Working Records (`.freeflow/tasks/`), secrets or local environment files. No --amend, --no-verify, --allow-empty or push unless the user asked for exactly that; after a failed commit or hook, inspect what changed before retrying.";
34
+ export const INSTRUCTION_TEXT_NOTICE =
35
+ "This commit stages model-facing instruction text: for a substantial revision (a merge, split, move or rewrite), run Write Skill's `skill-author compare --all-units` and settle every unmatched unit before it goes further.";
36
+ export const DIAGNOSE_NOTICE =
37
+ "This test command failed twice (Execute Work's Diagnose Before Fixing; its diagnose reference has the full loop): two failed fixes mean the diagnosis is wrong. Before the next change, find an observation that would come out differently if your explanation were wrong: change the question, change the kind of observation, or check the cheapest assumption (did it build; does the test import what you think?).";
38
+ export const executeWorkNotice = (skill) =>
39
+ `Read Execute Work (${skill}) before more tool calls: it holds how to read the request, find what a change touches and diagnose a failure.`;
40
+ /** Text without the guidance notices joined to it: moment guidance, not evidence, so it is not carried on. */
41
+ export const withoutGuidanceNotices = (text) =>
42
+ text.replace(/\n?<freeflow-notice type="guidance">\n[\s\S]*?\n<\/freeflow-notice>/g, "");
43
+ export const selfReviewNotice = (script) =>
44
+ `Before your final report (Execute Work's step before reporting; read Execute Work if its body is not in your context): run \`node ${script} --base <ref>\` and read its report: changed tests and fixtures with their hunks, new tests and whether they fail on the base, and whether each requirement's check ran after the last edit.`;
45
+ const textOf = (content) => (content ?? []).map((part) => (part?.type === "text" ? part.text : "")).join("");
46
+ /**
47
+ * The paths a commit command dealt with, read once at its result: what the new commit holds when it succeeded, what is
48
+ * still staged when it failed. Null when Git cannot tell.
49
+ */
50
+ function committedPaths(cwd, failed) {
51
+ const args = failed
52
+ ? ["diff", "--cached", "--name-only"]
53
+ : ["diff-tree", "--no-commit-id", "--name-only", "-r", "--root", "HEAD"];
54
+ return new Promise((resolve) =>
55
+ execFile("git", args, { cwd, timeout: 2000, maxBuffer: 4 * 1024 * 1024 }, (error, stdout) =>
56
+ resolve(error ? null : stdout.split("\n").filter(Boolean)),
57
+ ),
58
+ );
59
+ }
60
+ export class GuidanceNotices {
61
+ run = { committed: false, changed: false, reviewed: false, reminded: false, executeWork: false };
62
+ failures = new Map();
63
+ selfReview;
64
+ executeWork;
65
+ constructor(packageRoot) {
66
+ this.selfReview = join(packageRoot, "skills", "execute-work", "scripts", "self-review.mjs");
67
+ this.executeWork = join(packageRoot, "skills", "execute-work", "SKILL.md");
68
+ }
69
+ /** A new agent run: per-run notices may come again. */
70
+ runStarted() {
71
+ this.run = { committed: false, changed: false, reviewed: false, reminded: false, executeWork: false };
72
+ }
73
+ /**
74
+ * The notice lines to append to a tool result; empty when none applies. `executeWorkUnread` says whether Execute
75
+ * Work is a skill this session lists and its body is not in the model's context; omitted, the notice never shows.
76
+ */
77
+ async toolResult(event, cwd, executeWorkUnread) {
78
+ if (event?.parentToolCallId) return [];
79
+ const name = event?.toolName;
80
+ const lines = [];
81
+ if (
82
+ !this.run.executeWork &&
83
+ executeWorkUnread !== undefined &&
84
+ !isFreeflowInstruction(name, event.input, cwd) &&
85
+ executeWorkUnread()
86
+ ) {
87
+ this.run.executeWork = true;
88
+ lines.push(freeflowNotice("guidance", executeWorkNotice(this.executeWork)));
89
+ }
90
+ if (CHANGES_FILES.has(name)) {
91
+ if (!event.isError) this.run.changed = true;
92
+ return lines;
93
+ }
94
+ if (name !== "bash") return lines;
95
+ const command = String(event.input?.command ?? "").trim();
96
+ if (command.includes("self-review.mjs")) this.run.reviewed = true;
97
+ if (GIT_COMMIT.test(command) && !this.run.committed) {
98
+ this.run.committed = true;
99
+ const paths = await committedPaths(cwd, event.isError === true);
100
+ const body = [COMMIT_NOTICE];
101
+ if (paths?.some((path) => INSTRUCTION_TEXT.test(path))) body.push(INSTRUCTION_TEXT_NOTICE);
102
+ lines.push(freeflowNotice("guidance", body.join("\n")));
103
+ }
104
+ if (TEST_COMMAND.test(command)) {
105
+ const key = command.replace(/\s+/g, " ");
106
+ // Pi's bash reports a nonzero exit as an error result ("Command exited with code N").
107
+ const failed = event.isError === true || /Command exited with code [1-9]\d*\s*$/.test(textOf(event.content));
108
+ if (failed) {
109
+ const count = (this.failures.get(key) ?? 0) + 1;
110
+ this.failures.delete(key);
111
+ this.failures.set(key, count);
112
+ while (this.failures.size > FAILURE_KEYS) this.failures.delete(this.failures.keys().next().value);
113
+ if (count === 2) lines.push(freeflowNotice("guidance", DIAGNOSE_NOTICE));
114
+ } else {
115
+ this.failures.delete(key);
116
+ if (this.run.changed && !this.run.reviewed && !this.run.reminded) {
117
+ this.run.reminded = true;
118
+ lines.push(freeflowNotice("guidance", selfReviewNotice(this.selfReview)));
119
+ }
120
+ }
121
+ }
122
+ return lines;
123
+ }
124
+ }
@@ -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
+ }