@hassangameryt/freeflow 0.8.0 → 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 (290) 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/README.md +96 -149
  7. package/capabilities/cognitive-routing/SKILL.md +21 -129
  8. package/capabilities/cognitive-routing/references/coordinator.md +163 -0
  9. package/capabilities/cognitive-routing/references/worker.md +38 -0
  10. package/capabilities/compaction/SKILL.md +3 -3
  11. package/capabilities/compaction/references/summary-format.md +26 -15
  12. package/capabilities/messaging/SKILL.md +49 -0
  13. package/capabilities/task-memory/SKILL.md +87 -0
  14. package/command-surface.json +6 -54
  15. package/gemini-extension.json +1 -1
  16. package/hooks/claude/hooks.json +13 -3
  17. package/hooks/codex/hooks.json +2 -1
  18. package/hooks/shared/runtime-context.mjs +69 -77
  19. package/package.json +25 -8
  20. package/pi-extension/dist/ask-user/ask.js +108 -0
  21. package/pi-extension/dist/ask-user/form.js +250 -0
  22. package/pi-extension/dist/ask-user/questions.js +66 -0
  23. package/pi-extension/dist/ask-user/tool.js +124 -0
  24. package/pi-extension/dist/cognitive-routing/assembler.js +35 -11
  25. package/pi-extension/dist/cognitive-routing/bootstrap.js +90 -0
  26. package/pi-extension/dist/cognitive-routing/config.js +3 -4
  27. package/pi-extension/dist/cognitive-routing/event-store.js +4 -1
  28. package/pi-extension/dist/cognitive-routing/gate.js +20 -5
  29. package/pi-extension/dist/cognitive-routing/handlers.js +15 -13
  30. package/pi-extension/dist/cognitive-routing/model-control.js +17 -8
  31. package/pi-extension/dist/cognitive-routing/projection.js +23 -6
  32. package/pi-extension/dist/cognitive-routing/provenance.js +3 -1
  33. package/pi-extension/dist/cognitive-routing/render.js +15 -15
  34. package/pi-extension/dist/cognitive-routing/runtime.js +4 -2
  35. package/pi-extension/dist/cognitive-routing/schemas.js +4 -4
  36. package/pi-extension/dist/cognitive-routing/session.js +3 -1
  37. package/pi-extension/dist/cognitive-routing/sources.js +29 -4
  38. package/pi-extension/dist/cognitive-routing/state.js +18 -8
  39. package/pi-extension/dist/cognitive-routing/status.js +4 -3
  40. package/pi-extension/dist/cognitive-routing/tools.js +29 -15
  41. package/pi-extension/dist/cognitive-routing/types.js +10 -1
  42. package/pi-extension/dist/compaction/carry.js +36 -22
  43. package/pi-extension/dist/compaction/config.js +4 -2
  44. package/pi-extension/dist/compaction/controller.js +52 -29
  45. package/pi-extension/dist/compaction/harness.js +19 -3
  46. package/pi-extension/dist/compaction/results.js +14 -13
  47. package/pi-extension/dist/compaction/tool.js +6 -6
  48. package/pi-extension/dist/host/catalog.js +20 -35
  49. package/pi-extension/dist/host/config-validation.js +233 -0
  50. package/pi-extension/dist/host/config.js +78 -91
  51. package/pi-extension/dist/host/inbound.js +142 -0
  52. package/pi-extension/dist/host/projection-tags.js +1 -1
  53. package/pi-extension/dist/host/prompts.js +24 -13
  54. package/pi-extension/dist/host/read-only-session.js +6 -1
  55. package/pi-extension/dist/host/request-history.js +7 -3
  56. package/pi-extension/dist/host/rows.js +25 -0
  57. package/pi-extension/dist/host/runtime-state.js +17 -2
  58. package/pi-extension/dist/host/section-keeper.js +85 -0
  59. package/pi-extension/dist/host/settings/freeflow-command.js +846 -93
  60. package/pi-extension/dist/host/settings/settings-view.js +27 -8
  61. package/pi-extension/dist/host/status.js +19 -5
  62. package/pi-extension/dist/index.js +638 -72
  63. package/pi-extension/dist/messaging/config.js +27 -0
  64. package/pi-extension/dist/messaging/messaging.js +398 -0
  65. package/pi-extension/dist/messaging/registry.js +57 -0
  66. package/pi-extension/dist/messaging/render.js +90 -0
  67. package/pi-extension/dist/messaging/tools.js +109 -0
  68. package/pi-extension/dist/messaging/transport.js +101 -0
  69. package/pi-extension/dist/provider-support/cache/keepalive.js +16 -4
  70. package/pi-extension/dist/task-memory/config.js +49 -0
  71. package/pi-extension/dist/task-memory/registry.js +119 -0
  72. package/pi-extension/dist/task-memory/render.js +107 -0
  73. package/pi-extension/dist/task-memory/runtime.js +994 -0
  74. package/pi-extension/dist/task-memory/tool.js +233 -0
  75. package/pi-extension/dist/tool-execution/apply-patch/tool.js +1 -1
  76. package/pi-extension/dist/tool-execution/background.js +220 -28
  77. package/pi-extension/dist/tool-execution/config.js +14 -3
  78. package/pi-extension/dist/tool-execution/file-tracking.js +24 -4
  79. package/pi-extension/dist/tool-execution/guidance-notices.js +124 -0
  80. package/pi-extension/dist/tool-execution/skill-reads.js +143 -0
  81. package/pi-extension/dist/tool-execution/tools.js +2 -2
  82. package/runtime/prompts/cognitive-routing.md +4 -4
  83. package/runtime/prompts/core.md +24 -14
  84. package/runtime/prompts/interaction-contract.md +7 -4
  85. package/runtime/prompts/tool-execution.md +3 -16
  86. package/runtime/prompts/working-method.md +16 -99
  87. package/skills/deliver-work/SKILL.md +76 -0
  88. package/skills/deliver-work/references/branch-closeout.md +134 -0
  89. package/skills/deliver-work/references/commit.md +83 -0
  90. package/skills/deliver-work/references/launch.md +94 -0
  91. package/skills/deliver-work/references/release.md +85 -0
  92. package/skills/deliver-work/scripts/commit-state.mjs +139 -0
  93. package/skills/deliver-work/scripts/delivery-state.mjs +107 -0
  94. package/skills/deliver-work/scripts/lib/git-state.mjs +178 -0
  95. package/skills/deliver-work/scripts/push-route.mjs +122 -0
  96. package/skills/deliver-work/scripts/release-preflight.mjs +177 -0
  97. package/skills/discuss-work/SKILL.md +148 -0
  98. package/skills/{discuss → discuss-work}/references/discussion-continuity.md +10 -27
  99. package/skills/discuss-work/references/learning-action.md +36 -0
  100. package/skills/discuss-work/scripts/choices.mjs +101 -0
  101. package/skills/discuss-work/scripts/lib/choices.mjs +159 -0
  102. package/skills/evaluate-behavior/SKILL.md +107 -0
  103. package/skills/evaluate-behavior/references/authoring.md +381 -0
  104. package/skills/evaluate-behavior/references/coding-tasks.md +145 -0
  105. package/skills/evaluate-behavior/references/conversations-pipelines-judges.md +177 -0
  106. package/skills/evaluate-behavior/references/design-and-measurement.md +273 -0
  107. package/skills/evaluate-behavior/references/running-and-evidence.md +245 -0
  108. package/skills/evaluate-behavior/scripts/claude-guard.mjs +76 -0
  109. package/skills/evaluate-behavior/scripts/guard-rules.mjs +309 -0
  110. package/skills/evaluate-behavior/scripts/lib/agreement.mjs +90 -0
  111. package/skills/evaluate-behavior/scripts/lib/arms.mjs +55 -0
  112. package/skills/evaluate-behavior/scripts/lib/budget.mjs +87 -0
  113. package/skills/evaluate-behavior/scripts/lib/build-source.mjs +287 -0
  114. package/skills/evaluate-behavior/scripts/lib/clear.mjs +74 -0
  115. package/skills/evaluate-behavior/scripts/lib/container.mjs +395 -0
  116. package/skills/evaluate-behavior/scripts/lib/decision.mjs +418 -0
  117. package/skills/evaluate-behavior/scripts/lib/definitions.mjs +1072 -0
  118. package/skills/evaluate-behavior/scripts/lib/freeflow.mjs +103 -0
  119. package/skills/evaluate-behavior/scripts/lib/grade-command.mjs +591 -0
  120. package/skills/evaluate-behavior/scripts/lib/grade-comparison.mjs +90 -0
  121. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-interaction.mjs +19 -18
  122. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-support.mjs +32 -5
  123. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/grade-workspace.mjs +28 -12
  124. package/skills/evaluate-behavior/scripts/lib/grade.mjs +101 -0
  125. package/skills/evaluate-behavior/scripts/lib/hooks.mjs +98 -0
  126. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/confinement.mjs +196 -0
  127. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/constraints.mjs +111 -0
  128. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/index.mjs +48 -0
  129. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/isolation.mjs +74 -0
  130. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/launch.mjs +123 -0
  131. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/model-call.mjs +73 -0
  132. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/recorder.mjs +178 -0
  133. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/session.mjs +494 -0
  134. package/skills/evaluate-behavior/scripts/lib/hosts/claude-code/skills.mjs +81 -0
  135. package/skills/evaluate-behavior/scripts/lib/hosts/index.mjs +31 -0
  136. package/skills/evaluate-behavior/scripts/lib/hosts/names.mjs +3 -0
  137. package/skills/evaluate-behavior/scripts/lib/hosts/pi/confinement.mjs +173 -0
  138. package/skills/evaluate-behavior/scripts/lib/hosts/pi/host.mjs +148 -0
  139. package/skills/evaluate-behavior/scripts/lib/hosts/pi/index.mjs +70 -0
  140. package/skills/evaluate-behavior/scripts/lib/hosts/pi/isolation.mjs +81 -0
  141. package/skills/evaluate-behavior/scripts/lib/hosts/pi/model-call.mjs +79 -0
  142. package/skills/evaluate-behavior/scripts/lib/hosts/pi/process.mjs +241 -0
  143. package/skills/{evaluate-skill/scripts/lib → evaluate-behavior/scripts/lib/hosts/pi}/rpc.mjs +144 -26
  144. package/skills/evaluate-behavior/scripts/lib/identity.mjs +82 -0
  145. package/skills/evaluate-behavior/scripts/lib/judge.mjs +688 -0
  146. package/skills/evaluate-behavior/scripts/lib/metrics.mjs +332 -0
  147. package/skills/evaluate-behavior/scripts/lib/model-call.mjs +17 -0
  148. package/skills/evaluate-behavior/scripts/lib/participants.mjs +616 -0
  149. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/process.mjs +22 -0
  150. package/skills/evaluate-behavior/scripts/lib/queue.mjs +54 -0
  151. package/skills/evaluate-behavior/scripts/lib/reference.mjs +343 -0
  152. package/skills/evaluate-behavior/scripts/lib/reuse.mjs +277 -0
  153. package/skills/evaluate-behavior/scripts/lib/review.mjs +933 -0
  154. package/skills/evaluate-behavior/scripts/lib/runner.mjs +732 -0
  155. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/sandbox.mjs +119 -14
  156. package/skills/evaluate-behavior/scripts/lib/scan-shell.mjs +457 -0
  157. package/skills/evaluate-behavior/scripts/lib/scan.mjs +479 -0
  158. package/skills/evaluate-behavior/scripts/lib/seal.mjs +91 -0
  159. package/skills/evaluate-behavior/scripts/lib/shell.mjs +560 -0
  160. package/skills/evaluate-behavior/scripts/lib/source-override.mjs +64 -0
  161. package/skills/evaluate-behavior/scripts/lib/subject-file.mjs +88 -0
  162. package/skills/{evaluate-skill/scripts/lib/pi.mjs → evaluate-behavior/scripts/lib/subject.mjs} +235 -265
  163. package/skills/evaluate-behavior/scripts/lib/task-copy.mjs +124 -0
  164. package/skills/evaluate-behavior/scripts/lib/trials.mjs +524 -0
  165. package/skills/evaluate-behavior/scripts/lib/validity.mjs +68 -0
  166. package/skills/evaluate-behavior/scripts/lib/view-html.mjs +454 -0
  167. package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/view.mjs +159 -18
  168. package/skills/{evaluate-skill → evaluate-behavior}/scripts/pi-command-tool.mjs +21 -6
  169. package/skills/evaluate-behavior/scripts/pi-container-route.mjs +23 -0
  170. package/skills/evaluate-behavior/scripts/pi-guard.mjs +77 -0
  171. package/skills/evaluate-behavior/scripts/pi-login-refresh.mjs +31 -0
  172. package/skills/evaluate-behavior/scripts/skill-eval.mjs +586 -0
  173. package/skills/execute-work/SKILL.md +96 -104
  174. package/skills/execute-work/references/code-practices.md +27 -51
  175. package/skills/execute-work/references/design.md +271 -0
  176. package/skills/execute-work/references/diagnose.md +117 -0
  177. package/skills/execute-work/references/migration.md +108 -0
  178. package/skills/execute-work/references/simplify.md +65 -0
  179. package/skills/execute-work/scripts/self-review.mjs +422 -0
  180. package/skills/execute-work/scripts/touch-map.mjs +290 -0
  181. package/skills/review-work/SKILL.md +89 -65
  182. package/skills/review-work/references/adjudication.md +94 -0
  183. package/skills/review-work/references/artifact.md +198 -0
  184. package/skills/review-work/references/evals-and-judges.md +66 -0
  185. package/skills/review-work/references/work.md +177 -0
  186. package/skills/review-work/scripts/check-brief.mjs +128 -0
  187. package/skills/review-work/scripts/review-packet.mjs +233 -0
  188. package/skills/setup-freeflow/SKILL.md +68 -17
  189. package/skills/track-work/SKILL.md +52 -20
  190. package/skills/track-work/references/handoff.md +190 -0
  191. package/skills/track-work/references/projects.md +51 -0
  192. package/skills/track-work/references/working-record-format.md +36 -18
  193. package/skills/track-work/scripts/lib/document.mjs +17 -13
  194. package/skills/track-work/scripts/lib/format.mjs +2 -0
  195. package/skills/track-work/scripts/lib/recover.mjs +254 -0
  196. package/skills/track-work/scripts/lib/registry.mjs +656 -0
  197. package/skills/track-work/scripts/lib/search.mjs +40 -0
  198. package/skills/track-work/scripts/lib/store.mjs +46 -15
  199. package/skills/track-work/scripts/lib/views.mjs +50 -1
  200. package/skills/track-work/scripts/working-record.mjs +232 -13
  201. package/skills/verify-work/SKILL.md +45 -37
  202. package/skills/verify-work/references/feature-map.md +120 -0
  203. package/skills/verify-work/references/runtime-evidence.md +203 -0
  204. package/skills/verify-work/references/test-design.md +25 -6
  205. package/skills/verify-work/scripts/test-changes.mjs +246 -0
  206. package/skills/write-docs/SKILL.md +113 -25
  207. package/skills/write-docs/references/documentation-shapes.md +68 -3
  208. package/skills/write-docs/references/spec-and-plan-shapes.md +308 -0
  209. package/skills/write-docs/scripts/artifact-check.mjs +148 -0
  210. package/skills/write-docs/scripts/docs-check.mjs +96 -6
  211. package/skills/write-docs/scripts/plan-lint.mjs +123 -0
  212. package/skills/write-skill/SKILL.md +92 -52
  213. package/skills/write-skill/references/activation-and-wording.md +143 -0
  214. package/skills/write-skill/references/placement-and-dependencies.md +154 -0
  215. package/skills/write-skill/scripts/lib/compare.mjs +701 -0
  216. package/skills/write-skill/scripts/lib/skill-author.mjs +197 -3
  217. package/skills/write-skill/scripts/skill-author.mjs +51 -8
  218. package/capabilities/cognitive-routing/references/both-mode.md +0 -36
  219. package/capabilities/cognitive-routing/references/executor-mode.md +0 -48
  220. package/capabilities/cognitive-routing/references/helper-mode.md +0 -47
  221. package/plugin.json +0 -33
  222. package/skills/action-selection/SKILL.md +0 -92
  223. package/skills/action-selection/references/trajectory-stalls.md +0 -73
  224. package/skills/bypass/SKILL.md +0 -153
  225. package/skills/commit-work/SKILL.md +0 -93
  226. package/skills/commit-work/references/git-state-edges.md +0 -94
  227. package/skills/commit-work/references/simple-push.md +0 -94
  228. package/skills/commit-work/references/staging-decisions.md +0 -106
  229. package/skills/decision-gate/SKILL.md +0 -87
  230. package/skills/design-for-depth/SKILL.md +0 -108
  231. package/skills/design-for-depth/references/design-pressure-signals.md +0 -84
  232. package/skills/design-for-depth/references/implementation-decisions.md +0 -76
  233. package/skills/design-for-depth/references/interface-design-loop.md +0 -105
  234. package/skills/design-for-depth/references/module-and-dependency-design.md +0 -117
  235. package/skills/design-for-depth/references/state-and-failure-boundaries.md +0 -125
  236. package/skills/diagnose-failure/SKILL.md +0 -140
  237. package/skills/diagnose-failure/references/diagnostic-loop-catalog.md +0 -50
  238. package/skills/diagnose-failure/references/flaky-and-performance-diagnosis.md +0 -51
  239. package/skills/discuss/SKILL.md +0 -140
  240. package/skills/evaluate-skill/SKILL.md +0 -188
  241. package/skills/evaluate-skill/references/evaluation-definition-schema.md +0 -415
  242. package/skills/evaluate-skill/references/evaluation-design.md +0 -112
  243. package/skills/evaluate-skill/references/execution-and-evidence.md +0 -111
  244. package/skills/evaluate-skill/references/review-and-revision.md +0 -94
  245. package/skills/evaluate-skill/scripts/lib/definitions.mjs +0 -448
  246. package/skills/evaluate-skill/scripts/lib/grade-comparison.mjs +0 -60
  247. package/skills/evaluate-skill/scripts/lib/grade.mjs +0 -64
  248. package/skills/evaluate-skill/scripts/lib/review.mjs +0 -196
  249. package/skills/evaluate-skill/scripts/lib/runner.mjs +0 -405
  250. package/skills/evaluate-skill/scripts/lib/trials.mjs +0 -103
  251. package/skills/evaluate-skill/scripts/pi-guard.mjs +0 -181
  252. package/skills/evaluate-skill/scripts/skill-eval.mjs +0 -182
  253. package/skills/execute-work/references/execute-work-edges.md +0 -78
  254. package/skills/finish-branch/SKILL.md +0 -117
  255. package/skills/finish-branch/references/integration-options.md +0 -80
  256. package/skills/handoff/SKILL.md +0 -82
  257. package/skills/handoff/references/handoff-templates.md +0 -150
  258. package/skills/launch-work/SKILL.md +0 -116
  259. package/skills/launch-work/references/launch-readiness.md +0 -85
  260. package/skills/migration-work/SKILL.md +0 -115
  261. package/skills/migration-work/references/migration-lifecycle.md +0 -82
  262. package/skills/release-work/SKILL.md +0 -118
  263. package/skills/release-work/references/release-evidence.md +0 -78
  264. package/skills/review-artifact/SKILL.md +0 -143
  265. package/skills/review-artifact/references/adjudicate-artifact-review.md +0 -101
  266. package/skills/review-artifact/references/independent-artifact-reviewer-contract.md +0 -159
  267. package/skills/review-work/references/adjudicate-work-review.md +0 -97
  268. package/skills/review-work/references/independent-work-reviewer-contract.md +0 -139
  269. package/skills/review-work/references/security-risk-lens.md +0 -67
  270. package/skills/setup-freeflow/references/activation-contract.md +0 -103
  271. package/skills/setup-freeflow/references/host-setup.md +0 -67
  272. package/skills/simplify-code/SKILL.md +0 -85
  273. package/skills/simplify-code/references/simplification-patterns.md +0 -70
  274. package/skills/verify-work/references/browser-runtime-evidence.md +0 -51
  275. package/skills/verify-work/references/integration-evidence.md +0 -49
  276. package/skills/verify-work/references/performance-evidence.md +0 -59
  277. package/skills/workflow/SKILL.md +0 -171
  278. package/skills/workflow/references/domain-skill-composition.md +0 -45
  279. package/skills/write-plan/SKILL.md +0 -92
  280. package/skills/write-plan/references/plan-shapes.md +0 -156
  281. package/skills/write-skill/references/activation-boundaries.md +0 -106
  282. package/skills/write-skill/references/agent-first-instructions.md +0 -133
  283. package/skills/write-skill/references/progressive-disclosure.md +0 -129
  284. package/skills/write-skill/references/skill-dependency-graphs.md +0 -192
  285. package/skills/write-spec/SKILL.md +0 -85
  286. package/skills/write-spec/references/artifact-standards.md +0 -86
  287. package/skills/write-spec/references/decision-records.md +0 -80
  288. package/skills/write-spec/references/spec-shapes.md +0 -143
  289. /package/skills/{evaluate-skill → evaluate-behavior}/scripts/lib/evidence.mjs +0 -0
  290. /package/skills/{evaluate-skill → evaluate-behavior}/scripts/pi-observer.mjs +0 -0
@@ -1,15 +1,25 @@
1
1
  {
2
- "description": "Load Freeflow core runtime context in Claude Code.",
2
+ "description": "Load Freeflow core runtime context in Claude Code, one hook per prompt: Claude Code caps each hook's context at 10,000 characters.",
3
3
  "hooks": {
4
4
  "SessionStart": [
5
5
  {
6
- "matcher": "startup|resume|clear|compact",
6
+ "matcher": "startup|resume|clear|compact|fork",
7
7
  "hooks": [
8
8
  {
9
9
  "type": "command",
10
- "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/adapters/claude-session-start.mjs\" SessionStart",
10
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/adapters/claude-session-start.mjs\" SessionStart core",
11
11
  "timeout": 5,
12
12
  "statusMessage": "Loading Freeflow context"
13
+ },
14
+ {
15
+ "type": "command",
16
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/adapters/claude-session-start.mjs\" SessionStart interactionContract",
17
+ "timeout": 5
18
+ },
19
+ {
20
+ "type": "command",
21
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/hooks/adapters/claude-session-start.mjs\" SessionStart workingMethod",
22
+ "timeout": 5
13
23
  }
14
24
  ]
15
25
  }
@@ -9,7 +9,8 @@
9
9
  "type": "command",
10
10
  "command": "node \"${PLUGIN_ROOT}/hooks/adapters/codex-session-start.mjs\" SessionStart",
11
11
  "timeout": 5,
12
- "statusMessage": "Loading Freeflow context"
12
+ "statusMessage": "Loading Freeflow context",
13
+ "additionalContextLimit": 8000
13
14
  }
14
15
  ]
15
16
  }
@@ -2,6 +2,16 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import process from "node:process";
4
4
  import { fileURLToPath, pathToFileURL } from "node:url";
5
+ // The validator Pi uses, so a config is valid or invalid on every host alike.
6
+ import {
7
+ resolveReviewSettings,
8
+ reviewStateLine,
9
+ selectedRuntimePrompts,
10
+ skillsOff,
11
+ skillsStateLine,
12
+ validateLocalConfig,
13
+ validateRepositoryConfig,
14
+ } from "../../pi-extension/dist/host/config-validation.js";
5
15
 
6
16
  const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
7
17
  export const PLUGIN_ROOT = path.resolve(SCRIPT_DIR, "../..");
@@ -62,57 +72,6 @@ function readConfigLayer(filePath, validate) {
62
72
  }
63
73
  }
64
74
 
65
- function isRecord(value) {
66
- return Boolean(value) && typeof value === "object" && !Array.isArray(value);
67
- }
68
-
69
- function removedContextConfigError(value, filePath) {
70
- const removedKeys = ["contextVirtualization", "conversationHistory"].filter((key) => Object.hasOwn(value, key));
71
- return removedKeys.length > 0
72
- ? `Removed Freeflow context setting(s) in ${filePath}: ${removedKeys.join(", ")}. Delete those keys before using Freeflow.`
73
- : null;
74
- }
75
-
76
- function validateCoreConfigFields(value) {
77
- if (Object.hasOwn(value, "enabled") && typeof value.enabled !== "boolean") {
78
- return "enabled must be a boolean";
79
- }
80
- return null;
81
- }
82
-
83
- function isValidSetupConfig(value) {
84
- if (!isRecord(value)) return "repository config must be a JSON object";
85
-
86
- const removedContextError = removedContextConfigError(value, ".freeflow/config.json");
87
- if (removedContextError) return removedContextError;
88
-
89
- const allowedKeys = new Set(["enabled", "outputRouter", "observedRouting", "scriptTransform", "cognitiveRouting"]);
90
- if (!Object.keys(value).every((key) => allowedKeys.has(key))) {
91
- return "repository config contains unsupported top-level keys";
92
- }
93
-
94
- const coreError = validateCoreConfigFields(value);
95
- if (coreError) return coreError;
96
-
97
- for (const key of ["outputRouter", "observedRouting", "scriptTransform"]) {
98
- if (Object.hasOwn(value, key) && !isRecord(value[key])) return `${key} must be an object`;
99
- }
100
- return null;
101
- }
102
-
103
- function isValidLocalConfig(value) {
104
- if (!isRecord(value)) return "local config must be a JSON object";
105
-
106
- const removedContextError = removedContextConfigError(value, ".freeflow/local.json");
107
- if (removedContextError) return removedContextError;
108
-
109
- const allowedKeys = new Set(["enabled", "processing", "cognitiveRouting"]);
110
- if (!Object.keys(value).every((key) => allowedKeys.has(key))) {
111
- return "local config contains unsupported top-level keys";
112
- }
113
- return validateCoreConfigFields(value);
114
- }
115
-
116
75
  function resolveLayeredValue(repository, local, key, fallback) {
117
76
  if (Object.hasOwn(local, key)) return local[key];
118
77
  if (Object.hasOwn(repository, key)) return repository[key];
@@ -124,8 +83,8 @@ function resolveCoreConfig(repository, local) {
124
83
  }
125
84
 
126
85
  export function readConfig(root) {
127
- const repository = readConfigLayer(path.join(root, ".freeflow", "config.json"), isValidSetupConfig);
128
- const local = readConfigLayer(path.join(root, ".freeflow", "local.json"), isValidLocalConfig);
86
+ const repository = readConfigLayer(path.join(root, ".freeflow", "config.json"), validateRepositoryConfig);
87
+ const local = readConfigLayer(path.join(root, ".freeflow", "local.json"), validateLocalConfig);
129
88
  const localValid = !local.exists || local.valid;
130
89
  const configured = repository.valid && localValid;
131
90
  const core = resolveCoreConfig(repository.valid ? repository.parsed : {}, local.valid ? local.parsed : {});
@@ -139,42 +98,67 @@ export function readConfig(root) {
139
98
  localExists: local.exists,
140
99
  localValid,
141
100
  error: repository.valid ? local.error : repository.error,
101
+ errorPath: repository.valid ? ".freeflow/local.json" : ".freeflow/config.json",
142
102
  enabled,
103
+ prompts: selectedRuntimePrompts(repository.parsed, local.parsed),
104
+ review: resolveReviewSettings(repository.parsed, local.parsed),
105
+ skillsOff: skillsOff(repository.parsed, local.parsed),
143
106
  };
144
107
  }
145
108
 
146
- function loadRuntimeContext() {
147
- const corePrompt = readText(path.join(PLUGIN_ROOT, "runtime", "prompts", "core.md"));
148
- const interactionContract = readText(path.join(PLUGIN_ROOT, "runtime", "prompts", "interaction-contract.md"));
149
-
150
- if (!corePrompt || !interactionContract) {
151
- throw new Error("Freeflow core runtime prompt files are missing.");
109
+ const PROMPT_FILES = {
110
+ core: "core.md",
111
+ interactionContract: "interaction-contract.md",
112
+ workingMethod: "working-method.md",
113
+ };
114
+
115
+ /**
116
+ * The selected prompts in Pi's order, or only `part` when a host delivers each prompt through its own hook. A selected
117
+ * core or Interaction Contract must exist; the Working Method is optional, as on Pi.
118
+ */
119
+ function loadRuntimeContext(selection, part) {
120
+ const prompts = [];
121
+ for (const [key, file] of Object.entries(PROMPT_FILES)) {
122
+ if (!selection[key] || (part && key !== part)) continue;
123
+ const text = readText(path.join(PLUGIN_ROOT, "runtime", "prompts", file));
124
+ if (!text && key !== "workingMethod") throw new Error("Freeflow core runtime prompt files are missing.");
125
+ if (text) prompts.push(text);
152
126
  }
153
-
154
- return { corePrompt, interactionContract };
127
+ return prompts;
155
128
  }
156
129
 
157
- function runtimeStateContext() {
158
- return ["# Freeflow Runtime State", "", "Freeflow: active"].join("\n");
130
+ // The review and skills lines are Pi's, and as on Pi only a choice the user made adds them. This host loads plugin
131
+ // skills itself, so the skills line is how an off skill reaches the agent here.
132
+ function runtimeStateContext(config) {
133
+ const lines = [reviewStateLine(config.review), skillsStateLine(config.skillsOff)].filter(Boolean);
134
+ return ["# Freeflow Runtime State", "", "Freeflow: active", ...lines].join("\n");
159
135
  }
160
136
 
161
- export function renderRuntimeContext(root) {
137
+ /**
138
+ * The session-start context for the repository at `root`: every selected prompt, or one `part` of it (`core`,
139
+ * `interactionContract`, `workingMethod`). Claude Code caps each hook's context at 10,000 characters, so it runs one
140
+ * hook per part. A configuration error and the Runtime State, which the core prompt refers to, travel with the core.
141
+ */
142
+ export function renderRuntimeContext(root, part = undefined) {
143
+ if (part !== undefined && !Object.hasOwn(PROMPT_FILES, part)) return "";
162
144
  const config = readConfig(root);
163
145
  if (!config.valid) {
164
- if (config.error?.startsWith("Removed Freeflow context setting(s) in ")) {
165
- return [
166
- "# Freeflow Configuration Error",
167
- "",
168
- `Freeflow is inactive. ${config.error}`,
169
- "Remove the named key(s), then restart the session.",
170
- ].join("\n");
171
- }
172
- return "";
146
+ // A missing repository config means Freeflow is not set up here: stay silent. An existing invalid file is reported,
147
+ // as Pi's status does, so Freeflow is not silently missing.
148
+ if (!config.error || (part && part !== "core")) return "";
149
+ return [
150
+ "# Freeflow Configuration Error",
151
+ "",
152
+ `Freeflow is inactive because ${config.errorPath} is invalid: ${config.error}`,
153
+ `Fix ${config.errorPath} or use the setup-freeflow skill, then start a new session.`,
154
+ ].join("\n");
173
155
  }
174
156
  if (!config.enabled) return "";
175
157
 
176
- const { corePrompt, interactionContract } = loadRuntimeContext();
177
- return [corePrompt, interactionContract, runtimeStateContext()].filter(Boolean).join("\n\n");
158
+ // The user chooses the prompts on hosts other than Pi; with none selected there is nothing to deliver.
159
+ const prompts = loadRuntimeContext(config.prompts, part);
160
+ if (config.prompts.core && (!part || part === "core")) prompts.push(runtimeStateContext(config));
161
+ return prompts.join("\n\n");
178
162
  }
179
163
 
180
164
  export function emitHookOutput(output) {
@@ -188,7 +172,8 @@ export function runSessionStartAdapter({ eventNames, getWorkspaceCwd, formatOutp
188
172
  if (!eventNames.includes(eventName)) return;
189
173
 
190
174
  const root = findWorkspaceRoot(getWorkspaceCwd(input));
191
- const additionalContext = renderRuntimeContext(root);
175
+ // An optional third argument names the one prompt this hook delivers.
176
+ const additionalContext = renderRuntimeContext(root, process.argv[3]);
192
177
  if (additionalContext.trim()) {
193
178
  emitHookOutput(formatOutput({ eventName, input, additionalContext }));
194
179
  }
@@ -204,5 +189,12 @@ export function runSafely(run) {
204
189
  }
205
190
 
206
191
  export function isMainModule(metaUrl) {
207
- return process.argv[1] && pathToFileURL(path.resolve(process.argv[1])).href === metaUrl;
192
+ if (!process.argv[1]) return false;
193
+ // Compare real paths: Node resolves symlinks in import.meta.url but not in argv, and a host may run the plugin from a
194
+ // symlinked directory (macOS /var is /private/var).
195
+ try {
196
+ return fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(metaUrl));
197
+ } catch {
198
+ return pathToFileURL(path.resolve(process.argv[1])).href === metaUrl;
199
+ }
208
200
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hassangameryt/freeflow",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "Feedback-based control system for coding agents.",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -22,31 +22,46 @@
22
22
  "build:pi-extension": "node -e \"require('node:fs').rmSync('pi-extension/dist', { recursive: true, force: true })\" && tsc -p pi-extension/tsconfig.json && npm run format:pi-extension-dist",
23
23
  "build": "npm run build:pi-extension",
24
24
  "typecheck": "tsc -p pi-extension/tsconfig.json --noEmit",
25
- "check": "npm run typecheck && npm run check:source && npm run check:host-manifests && npm run check:changelog && npm run check:release-workflow && npm run check:docs && npm run format:check && npm run check:release-version && npm run check:commands && npm run check:activation && npm run check:runtime && npm run check:package && npm run check:eval-coverage",
25
+ "lint": "eslint pi-extension/src",
26
+ "check": "npm run typecheck && npm run lint && npm run check:source && npm run check:host-manifests && npm run check:changelog && npm run check:release-workflow && npm run check:docs && npm run check:dev-docs && npm run format:check && npm run check:release-version && npm run check:commands && npm run check:activation && npm run check:runtime && npm run check:package && npm run check:eval-coverage && npm run check:eval-definitions && npm run check:coverage-matrix && npm run check:skill-routing && npm run check:incident-fixtures",
26
27
  "check:source": "node scripts/validation/check-source-files.mjs",
27
28
  "check:host-manifests": "node scripts/validation/check-host-manifests.mjs",
28
29
  "check:changelog": "node scripts/validation/check-changelog.mjs --structure",
29
30
  "check:release-workflow": "node scripts/validation/check-release-workflow.mjs",
30
31
  "check:docs": "node scripts/validation/check-docs.mjs",
32
+ "check:dev-docs": "node skills/write-docs/scripts/docs-check.mjs --strict dev-docs",
31
33
  "check:release-version": "node scripts/validation/check-release-version.mjs",
32
34
  "check:commands": "scripts/validation/audit-command-surface.sh",
33
35
  "check:activation": "scripts/validation/check-activation-contract.sh",
34
36
  "check:runtime": "hooks/tests/check-runtime-context-hook.sh",
35
37
  "check:package": "node scripts/validation/check-package.mjs",
36
- "check:eval-coverage": "node scripts/validation/check-eval-coverage.mjs",
38
+ "check:eval-coverage": "node scripts/validation/check-eval-coverage.mjs --strict",
39
+ "check:eval-definitions": "node scripts/validation/check-eval-definitions.mjs",
40
+ "check:coverage-matrix": "node .skill-eval/scripts/verify-coverage-matrix.mjs",
41
+ "check:skill-routing": "node scripts/validation/check-skill-routing-doc.mjs",
42
+ "check:incident-fixtures": "node .skill-eval/workflow/scripts/verify-incident-fixtures.mjs",
37
43
  "check:release-metadata": "scripts/validation/validate-release-metadata.sh --mode prepublish",
38
44
  "perf:request": "node scripts/perf/request-overhead.mjs",
45
+ "eval:gate": "node .skill-eval/scripts/gate.mjs",
46
+ "test": "npm run test:pi-extension && npm run test:host-manifests && npm run test:skill-author && npm run test:skill-eval && npm run test:track-work && npm run test:discuss-work && npm run test:execute-work && npm run test:deliver-work && npm run test:review-work && npm run test:verify-work && npm run test:write-docs && npm run test:freeflow-snapshot && npm run test:release-utils && npm run test:release-workflow && npm run test:changelog && npm run test:docs && npm run test:eval-coverage && npm run test:guards",
39
47
  "test:pi-extension": "npm run build && node --test $(find pi-extension/tests -name '*.test.js' | sort)",
40
48
  "test:host-manifests": "node --test scripts/validation/check-host-manifests.test.mjs hooks/tests/check-host-adapters.test.mjs",
41
49
  "test:skill-author": "node --test $(find .skill-eval/write-skill/tests -name '*.test.mjs' | sort)",
42
- "test:skill-eval": "node --test $(find .skill-eval/evaluate-skill/tests -name '*.test.mjs' | sort)",
50
+ "test:skill-eval": "node --test $(find .skill-eval/evaluate-behavior/tests -name '*.test.mjs' | sort)",
43
51
  "test:track-work": "node --test .skill-eval/track-work/tests/*.test.mjs",
52
+ "test:discuss-work": "node --test .skill-eval/discuss-work/tests/*.test.mjs",
53
+ "test:execute-work": "node --test .skill-eval/execute-work/tests/*.test.mjs",
54
+ "test:deliver-work": "node --test .skill-eval/deliver-work/tests/*.test.mjs",
55
+ "test:review-work": "node --test .skill-eval/review-work/tests/*.test.mjs",
56
+ "test:verify-work": "node --test .skill-eval/verify-work/tests/*.test.mjs",
57
+ "test:write-docs": "node --test .skill-eval/write-docs/tests/*.test.mjs",
44
58
  "test:freeflow-snapshot": "node --test scripts/freeflow-snapshot.test.mjs",
45
59
  "test:release-utils": "node --test scripts/release-utils.test.mjs",
46
60
  "test:changelog": "node --test scripts/validation/check-changelog.test.mjs",
47
61
  "test:release-workflow": "node --test scripts/validation/check-release-workflow.test.mjs",
48
62
  "test:docs": "node --test scripts/validation/check-docs.test.mjs",
49
63
  "test:eval-coverage": "node --test scripts/validation/check-eval-coverage.test.mjs",
64
+ "test:guards": "node --test scripts/validation/guards.test.mjs",
50
65
  "release:prepare": "node scripts/release.mjs",
51
66
  "release:notes": "node scripts/release-notes.mjs",
52
67
  "snapshot:refresh": "node scripts/freeflow-snapshot.mjs"
@@ -82,7 +97,6 @@
82
97
  "image": "https://raw.githubusercontent.com/hassan-mohiddin/freeflow/main/assets/freeflow-plugin-icon-transparent.png"
83
98
  },
84
99
  "files": [
85
- "plugin.json",
86
100
  "gemini-extension.json",
87
101
  ".agents/**",
88
102
  ".claude-plugin/**",
@@ -113,11 +127,14 @@
113
127
  "@earendil-works/pi-tui": ">=0.99.1"
114
128
  },
115
129
  "devDependencies": {
116
- "@earendil-works/pi-coding-agent": "1.0.1",
117
- "@earendil-works/pi-tui": "1.0.1",
130
+ "@earendil-works/pi-coding-agent": "1.4.0",
131
+ "@earendil-works/pi-tui": "1.4.0",
132
+ "@eslint/js": "10.0.1",
118
133
  "@types/node": "^22.15.30",
134
+ "eslint": "10.12.0",
119
135
  "prettier": "3.9.5",
120
- "typescript": "^5.8.3"
136
+ "typescript": "^5.8.3",
137
+ "typescript-eslint": "8.71.1"
121
138
  },
122
139
  "dependencies": {}
123
140
  }
@@ -0,0 +1,108 @@
1
+ import { QuestionForm } from "./form.js";
2
+ /*
3
+ * Putting questions to the user in whatever UI the session has:
4
+ * - the terminal: one QuestionForm (descriptions, own answer, notes, multi-select);
5
+ * - RPC clients: one `select` per question (option and description on one line) and an `input` for the user's own
6
+ * answer; multi-select repeats `select` until the user picks Done;
7
+ * - no UI (print, JSON): unavailable, so the caller asks in its reply.
8
+ * Closing any dialog without answering dismisses the whole question.
9
+ */
10
+ const OWN = "Type my own answer";
11
+ const DONE = "Done";
12
+ export async function askUser(ctx, questions, signal, form = {}) {
13
+ if (!ctx?.hasUI || !ctx.ui) return { status: "unavailable", reason: "No one can answer a dialog in this session." };
14
+ if (ctx.mode === "tui" && typeof ctx.ui.custom === "function") {
15
+ if (signal?.aborted) return { status: "dismissed" };
16
+ let timer;
17
+ let onAbort;
18
+ const outcome = await ctx.ui.custom((tui, theme, _keybindings, done) => {
19
+ const view = new QuestionForm(questions, theme, done, () => tui?.requestRender?.(), form);
20
+ // Pi's custom dialog takes no signal: an aborted call (the user pressed Escape on the turn) closes it here.
21
+ if (signal) {
22
+ onAbort = () => done({ status: "dismissed" });
23
+ signal.addEventListener("abort", onAbort, { once: true });
24
+ }
25
+ // A dialog with a deadline counts down and closes itself unanswered when it passes.
26
+ if (form.deadline !== undefined)
27
+ timer = setInterval(() => {
28
+ if (Date.now() >= form.deadline) done({ status: "dismissed" });
29
+ else {
30
+ view.invalidate();
31
+ tui?.requestRender?.();
32
+ }
33
+ }, 1000);
34
+ return view;
35
+ });
36
+ if (timer) clearInterval(timer);
37
+ if (onAbort) signal?.removeEventListener("abort", onAbort);
38
+ return outcome ?? { status: "dismissed" };
39
+ }
40
+ const answers = [];
41
+ for (const question of questions) {
42
+ const answer = await askOne(ctx, question, signal);
43
+ if (!answer) return { status: "dismissed" };
44
+ answers.push(answer);
45
+ }
46
+ return { status: "answered", answers };
47
+ }
48
+ async function askOne(ctx, question, signal) {
49
+ const title = `${question.header} · ${question.question}`;
50
+ const labels = question.options.map((option) =>
51
+ option.description ? `${option.label} — ${option.description}` : option.label,
52
+ );
53
+ const base = { header: question.header, question: question.question };
54
+ const own = async () => {
55
+ const text = (await ctx.ui.input(title, "Your answer", signal ? { signal } : undefined))?.trim();
56
+ return text ? { ...base, selected: [], text } : undefined;
57
+ };
58
+ if (!question.multiSelect) {
59
+ const choice = await ctx.ui.select(title, [...labels, OWN], signal ? { signal } : undefined);
60
+ if (choice === undefined) return undefined;
61
+ if (choice === OWN) return own();
62
+ return { ...base, selected: [question.options[labels.indexOf(choice)].label] };
63
+ }
64
+ const picked = new Set();
65
+ for (;;) {
66
+ const shown = labels.map((label, at) => `${picked.has(at) ? "[x]" : "[ ]"} ${label}`);
67
+ const choice = await ctx.ui.select(
68
+ `${title} (choose one or more, then ${DONE})`,
69
+ [...shown, OWN, ...(picked.size ? [DONE] : [])],
70
+ signal ? { signal } : undefined,
71
+ );
72
+ if (choice === undefined) return undefined;
73
+ if (choice === OWN) return own();
74
+ if (choice === DONE)
75
+ return { ...base, selected: [...picked].sort((a, b) => a - b).map((at) => question.options[at].label) };
76
+ const at = shown.indexOf(choice);
77
+ if (picked.has(at)) picked.delete(at);
78
+ else picked.add(at);
79
+ }
80
+ }
81
+ /**
82
+ * A yes-or-no question in the same dialog as ask_user, for Freeflow's own confirmations (task memory's proposals).
83
+ * Outside the terminal it is Pi's confirm, unchanged; without a UI it is false.
84
+ */
85
+ export async function confirmWithUser(ctx, title, message, options = {}) {
86
+ if (!ctx?.hasUI || !ctx.ui) return false;
87
+ if (ctx.mode !== "tui" || typeof ctx.ui.custom !== "function")
88
+ return Boolean(
89
+ await ctx.ui.confirm(
90
+ title,
91
+ message,
92
+ ...(options.timeoutMs !== undefined ? [{ timeout: options.timeoutMs }] : []),
93
+ ),
94
+ );
95
+ const header = title.replace(/\?$/, "").slice(0, 40);
96
+ const yes = options.yes ?? "Yes";
97
+ const outcome = await askUser(
98
+ ctx,
99
+ [{ question: message, header, options: [{ label: yes }, { label: options.no ?? "No" }] }],
100
+ undefined,
101
+ {
102
+ ownAnswer: false,
103
+ notes: false,
104
+ ...(options.timeoutMs !== undefined ? { deadline: Date.now() + options.timeoutMs } : {}),
105
+ },
106
+ );
107
+ return outcome.status === "answered" && outcome.answers[0]?.selected[0] === yes;
108
+ }