jfl 0.4.3 → 0.5.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 (428) hide show
  1. package/README.md +15 -5
  2. package/dist/commands/context-hub.d.ts.map +1 -1
  3. package/dist/commands/context-hub.js +818 -39
  4. package/dist/commands/context-hub.js.map +1 -1
  5. package/dist/commands/eval.d.ts +1 -1
  6. package/dist/commands/eval.d.ts.map +1 -1
  7. package/dist/commands/eval.js +192 -1
  8. package/dist/commands/eval.js.map +1 -1
  9. package/dist/commands/findings.d.ts +6 -0
  10. package/dist/commands/findings.d.ts.map +1 -0
  11. package/dist/commands/findings.js +203 -0
  12. package/dist/commands/findings.js.map +1 -0
  13. package/dist/commands/hud.d.ts.map +1 -1
  14. package/dist/commands/hud.js +47 -9
  15. package/dist/commands/hud.js.map +1 -1
  16. package/dist/commands/ide.d.ts +27 -0
  17. package/dist/commands/ide.d.ts.map +1 -0
  18. package/dist/commands/ide.js +546 -0
  19. package/dist/commands/ide.js.map +1 -0
  20. package/dist/commands/onboard.d.ts.map +1 -1
  21. package/dist/commands/onboard.js +212 -2
  22. package/dist/commands/onboard.js.map +1 -1
  23. package/dist/commands/openclaw.d.ts +3 -0
  24. package/dist/commands/openclaw.d.ts.map +1 -1
  25. package/dist/commands/openclaw.js +76 -2
  26. package/dist/commands/openclaw.js.map +1 -1
  27. package/dist/commands/peter.d.ts +3 -0
  28. package/dist/commands/peter.d.ts.map +1 -1
  29. package/dist/commands/peter.js +1218 -2
  30. package/dist/commands/peter.js.map +1 -1
  31. package/dist/commands/pi-fleet.d.ts +18 -0
  32. package/dist/commands/pi-fleet.d.ts.map +1 -0
  33. package/dist/commands/pi-fleet.js +382 -0
  34. package/dist/commands/pi-fleet.js.map +1 -0
  35. package/dist/commands/pi.d.ts.map +1 -1
  36. package/dist/commands/pi.js +18 -3
  37. package/dist/commands/pi.js.map +1 -1
  38. package/dist/commands/scope.d.ts.map +1 -1
  39. package/dist/commands/scope.js +90 -1
  40. package/dist/commands/scope.js.map +1 -1
  41. package/dist/commands/services.d.ts.map +1 -1
  42. package/dist/commands/services.js +18 -0
  43. package/dist/commands/services.js.map +1 -1
  44. package/dist/commands/status.d.ts.map +1 -1
  45. package/dist/commands/status.js +22 -4
  46. package/dist/commands/status.js.map +1 -1
  47. package/dist/commands/viz.d.ts.map +1 -1
  48. package/dist/commands/viz.js +417 -0
  49. package/dist/commands/viz.js.map +1 -1
  50. package/dist/dashboard-static/assets/index-B6b867Pv.js +121 -0
  51. package/dist/dashboard-static/assets/index-Y4BrqxV-.css +1 -0
  52. package/dist/dashboard-static/index.html +2 -2
  53. package/dist/index.js +228 -62
  54. package/dist/index.js.map +1 -1
  55. package/dist/lib/agent-config.d.ts +52 -0
  56. package/dist/lib/agent-config.d.ts.map +1 -0
  57. package/dist/lib/agent-config.js +231 -0
  58. package/dist/lib/agent-config.js.map +1 -0
  59. package/dist/lib/agent-generator.d.ts +10 -0
  60. package/dist/lib/agent-generator.d.ts.map +1 -1
  61. package/dist/lib/agent-generator.js +64 -10
  62. package/dist/lib/agent-generator.js.map +1 -1
  63. package/dist/lib/agent-session.d.ts +104 -0
  64. package/dist/lib/agent-session.d.ts.map +1 -0
  65. package/dist/lib/agent-session.js +627 -0
  66. package/dist/lib/agent-session.js.map +1 -0
  67. package/dist/lib/eval-snapshot.d.ts +47 -0
  68. package/dist/lib/eval-snapshot.d.ts.map +1 -0
  69. package/dist/lib/eval-snapshot.js +315 -0
  70. package/dist/lib/eval-snapshot.js.map +1 -0
  71. package/dist/lib/eval-store.d.ts +5 -0
  72. package/dist/lib/eval-store.d.ts.map +1 -1
  73. package/dist/lib/eval-store.js +33 -3
  74. package/dist/lib/eval-store.js.map +1 -1
  75. package/dist/lib/findings-engine.d.ts +51 -0
  76. package/dist/lib/findings-engine.d.ts.map +1 -0
  77. package/dist/lib/findings-engine.js +338 -0
  78. package/dist/lib/findings-engine.js.map +1 -0
  79. package/dist/lib/flow-engine.d.ts +8 -0
  80. package/dist/lib/flow-engine.d.ts.map +1 -1
  81. package/dist/lib/flow-engine.js +84 -2
  82. package/dist/lib/flow-engine.js.map +1 -1
  83. package/dist/lib/hub-client.d.ts +1 -0
  84. package/dist/lib/hub-client.d.ts.map +1 -1
  85. package/dist/lib/hub-client.js +33 -6
  86. package/dist/lib/hub-client.js.map +1 -1
  87. package/dist/lib/ide-panes.d.ts +58 -0
  88. package/dist/lib/ide-panes.d.ts.map +1 -0
  89. package/dist/lib/ide-panes.js +508 -0
  90. package/dist/lib/ide-panes.js.map +1 -0
  91. package/dist/lib/memory-db.js +4 -4
  92. package/dist/lib/memory-db.js.map +1 -1
  93. package/dist/lib/memory-indexer.d.ts.map +1 -1
  94. package/dist/lib/memory-indexer.js +3 -0
  95. package/dist/lib/memory-indexer.js.map +1 -1
  96. package/dist/lib/memory-search.d.ts +148 -4
  97. package/dist/lib/memory-search.d.ts.map +1 -1
  98. package/dist/lib/memory-search.js +496 -58
  99. package/dist/lib/memory-search.js.map +1 -1
  100. package/dist/lib/meta-orchestrator.d.ts +104 -0
  101. package/dist/lib/meta-orchestrator.d.ts.map +1 -0
  102. package/dist/lib/meta-orchestrator.js +373 -0
  103. package/dist/lib/meta-orchestrator.js.map +1 -0
  104. package/dist/lib/peer-agent-generator.d.ts.map +1 -1
  105. package/dist/lib/peer-agent-generator.js +43 -19
  106. package/dist/lib/peer-agent-generator.js.map +1 -1
  107. package/dist/lib/policy-head.d.ts +25 -0
  108. package/dist/lib/policy-head.d.ts.map +1 -0
  109. package/dist/lib/policy-head.js +136 -0
  110. package/dist/lib/policy-head.js.map +1 -0
  111. package/dist/lib/replay-buffer.d.ts +93 -0
  112. package/dist/lib/replay-buffer.d.ts.map +1 -0
  113. package/dist/lib/replay-buffer.js +302 -0
  114. package/dist/lib/replay-buffer.js.map +1 -0
  115. package/dist/lib/sentinel-rl.d.ts +97 -0
  116. package/dist/lib/sentinel-rl.d.ts.map +1 -0
  117. package/dist/lib/sentinel-rl.js +430 -0
  118. package/dist/lib/sentinel-rl.js.map +1 -0
  119. package/dist/lib/session-lock.d.ts +61 -0
  120. package/dist/lib/session-lock.d.ts.map +1 -0
  121. package/dist/lib/session-lock.js +438 -0
  122. package/dist/lib/session-lock.js.map +1 -0
  123. package/dist/lib/stratus-client.d.ts +1 -0
  124. package/dist/lib/stratus-client.d.ts.map +1 -1
  125. package/dist/lib/stratus-client.js +24 -2
  126. package/dist/lib/stratus-client.js.map +1 -1
  127. package/dist/lib/telemetry-agent-v2.d.ts +128 -0
  128. package/dist/lib/telemetry-agent-v2.d.ts.map +1 -0
  129. package/dist/lib/telemetry-agent-v2.js +1042 -0
  130. package/dist/lib/telemetry-agent-v2.js.map +1 -0
  131. package/dist/lib/telemetry-agent.d.ts.map +1 -1
  132. package/dist/lib/telemetry-agent.js +27 -6
  133. package/dist/lib/telemetry-agent.js.map +1 -1
  134. package/dist/lib/telemetry-digest.d.ts.map +1 -1
  135. package/dist/lib/telemetry-digest.js +27 -5
  136. package/dist/lib/telemetry-digest.js.map +1 -1
  137. package/dist/lib/telemetry.d.ts.map +1 -1
  138. package/dist/lib/telemetry.js +29 -4
  139. package/dist/lib/telemetry.js.map +1 -1
  140. package/dist/lib/text-preprocessing.d.ts +83 -0
  141. package/dist/lib/text-preprocessing.d.ts.map +1 -0
  142. package/dist/lib/text-preprocessing.js +261 -0
  143. package/dist/lib/text-preprocessing.js.map +1 -0
  144. package/dist/lib/training-buffer.d.ts +86 -0
  145. package/dist/lib/training-buffer.d.ts.map +1 -0
  146. package/dist/lib/training-buffer.js +139 -0
  147. package/dist/lib/training-buffer.js.map +1 -0
  148. package/dist/lib/tuple-miner.d.ts +30 -0
  149. package/dist/lib/tuple-miner.d.ts.map +1 -0
  150. package/dist/lib/tuple-miner.js +427 -0
  151. package/dist/lib/tuple-miner.js.map +1 -0
  152. package/dist/lib/vm-backend.d.ts +72 -0
  153. package/dist/lib/vm-backend.d.ts.map +1 -0
  154. package/dist/lib/vm-backend.js +175 -0
  155. package/dist/lib/vm-backend.js.map +1 -0
  156. package/dist/lib/workspace/backend.d.ts +53 -0
  157. package/dist/lib/workspace/backend.d.ts.map +1 -0
  158. package/dist/lib/workspace/backend.js +37 -0
  159. package/dist/lib/workspace/backend.js.map +1 -0
  160. package/dist/lib/workspace/cmux-adapter.d.ts +46 -0
  161. package/dist/lib/workspace/cmux-adapter.d.ts.map +1 -0
  162. package/dist/lib/workspace/cmux-adapter.js +261 -0
  163. package/dist/lib/workspace/cmux-adapter.js.map +1 -0
  164. package/dist/lib/workspace/data-pipeline.d.ts +35 -0
  165. package/dist/lib/workspace/data-pipeline.d.ts.map +1 -0
  166. package/dist/lib/workspace/data-pipeline.js +463 -0
  167. package/dist/lib/workspace/data-pipeline.js.map +1 -0
  168. package/dist/lib/workspace/engine.d.ts +64 -0
  169. package/dist/lib/workspace/engine.d.ts.map +1 -0
  170. package/dist/lib/workspace/engine.js +397 -0
  171. package/dist/lib/workspace/engine.js.map +1 -0
  172. package/dist/lib/workspace/notifications.d.ts +14 -0
  173. package/dist/lib/workspace/notifications.d.ts.map +1 -0
  174. package/dist/lib/workspace/notifications.js +41 -0
  175. package/dist/lib/workspace/notifications.js.map +1 -0
  176. package/dist/lib/workspace/surface-registry.d.ts +49 -0
  177. package/dist/lib/workspace/surface-registry.d.ts.map +1 -0
  178. package/dist/lib/workspace/surface-registry.js +217 -0
  179. package/dist/lib/workspace/surface-registry.js.map +1 -0
  180. package/dist/lib/workspace/surface-type.d.ts +153 -0
  181. package/dist/lib/workspace/surface-type.d.ts.map +1 -0
  182. package/dist/lib/workspace/surface-type.js +9 -0
  183. package/dist/lib/workspace/surface-type.js.map +1 -0
  184. package/dist/lib/workspace/surfaces/agent-overview.d.ts +16 -0
  185. package/dist/lib/workspace/surfaces/agent-overview.d.ts.map +1 -0
  186. package/dist/lib/workspace/surfaces/agent-overview.js +116 -0
  187. package/dist/lib/workspace/surfaces/agent-overview.js.map +1 -0
  188. package/dist/lib/workspace/surfaces/agent.d.ts +16 -0
  189. package/dist/lib/workspace/surfaces/agent.d.ts.map +1 -0
  190. package/dist/lib/workspace/surfaces/agent.js +112 -0
  191. package/dist/lib/workspace/surfaces/agent.js.map +1 -0
  192. package/dist/lib/workspace/surfaces/claude.d.ts +15 -0
  193. package/dist/lib/workspace/surfaces/claude.d.ts.map +1 -0
  194. package/dist/lib/workspace/surfaces/claude.js +23 -0
  195. package/dist/lib/workspace/surfaces/claude.js.map +1 -0
  196. package/dist/lib/workspace/surfaces/dashboard.d.ts +21 -0
  197. package/dist/lib/workspace/surfaces/dashboard.d.ts.map +1 -0
  198. package/dist/lib/workspace/surfaces/dashboard.js +32 -0
  199. package/dist/lib/workspace/surfaces/dashboard.js.map +1 -0
  200. package/dist/lib/workspace/surfaces/eval.d.ts +15 -0
  201. package/dist/lib/workspace/surfaces/eval.d.ts.map +1 -0
  202. package/dist/lib/workspace/surfaces/eval.js +42 -0
  203. package/dist/lib/workspace/surfaces/eval.js.map +1 -0
  204. package/dist/lib/workspace/surfaces/event-stream.d.ts +16 -0
  205. package/dist/lib/workspace/surfaces/event-stream.d.ts.map +1 -0
  206. package/dist/lib/workspace/surfaces/event-stream.js +40 -0
  207. package/dist/lib/workspace/surfaces/event-stream.js.map +1 -0
  208. package/dist/lib/workspace/surfaces/flow.d.ts +16 -0
  209. package/dist/lib/workspace/surfaces/flow.d.ts.map +1 -0
  210. package/dist/lib/workspace/surfaces/flow.js +49 -0
  211. package/dist/lib/workspace/surfaces/flow.js.map +1 -0
  212. package/dist/lib/workspace/surfaces/index.d.ts +16 -0
  213. package/dist/lib/workspace/surfaces/index.d.ts.map +1 -0
  214. package/dist/lib/workspace/surfaces/index.js +16 -0
  215. package/dist/lib/workspace/surfaces/index.js.map +1 -0
  216. package/dist/lib/workspace/surfaces/portfolio.d.ts +16 -0
  217. package/dist/lib/workspace/surfaces/portfolio.d.ts.map +1 -0
  218. package/dist/lib/workspace/surfaces/portfolio.js +102 -0
  219. package/dist/lib/workspace/surfaces/portfolio.js.map +1 -0
  220. package/dist/lib/workspace/surfaces/service.d.ts +16 -0
  221. package/dist/lib/workspace/surfaces/service.d.ts.map +1 -0
  222. package/dist/lib/workspace/surfaces/service.js +45 -0
  223. package/dist/lib/workspace/surfaces/service.js.map +1 -0
  224. package/dist/lib/workspace/surfaces/shell.d.ts +15 -0
  225. package/dist/lib/workspace/surfaces/shell.d.ts.map +1 -0
  226. package/dist/lib/workspace/surfaces/shell.js +19 -0
  227. package/dist/lib/workspace/surfaces/shell.js.map +1 -0
  228. package/dist/lib/workspace/surfaces/telemetry.d.ts +16 -0
  229. package/dist/lib/workspace/surfaces/telemetry.d.ts.map +1 -0
  230. package/dist/lib/workspace/surfaces/telemetry.js +48 -0
  231. package/dist/lib/workspace/surfaces/telemetry.js.map +1 -0
  232. package/dist/lib/workspace/surfaces/topology.d.ts +15 -0
  233. package/dist/lib/workspace/surfaces/topology.d.ts.map +1 -0
  234. package/dist/lib/workspace/surfaces/topology.js +19 -0
  235. package/dist/lib/workspace/surfaces/topology.js.map +1 -0
  236. package/dist/lib/workspace/surfaces/training.d.ts +16 -0
  237. package/dist/lib/workspace/surfaces/training.d.ts.map +1 -0
  238. package/dist/lib/workspace/surfaces/training.js +22 -0
  239. package/dist/lib/workspace/surfaces/training.js.map +1 -0
  240. package/dist/lib/workspace/tmux-adapter.d.ts +27 -0
  241. package/dist/lib/workspace/tmux-adapter.d.ts.map +1 -0
  242. package/dist/lib/workspace/tmux-adapter.js +106 -0
  243. package/dist/lib/workspace/tmux-adapter.js.map +1 -0
  244. package/dist/mcp/context-hub-mcp.js +7 -24
  245. package/dist/mcp/context-hub-mcp.js.map +1 -1
  246. package/dist/types/flows.d.ts +2 -0
  247. package/dist/types/flows.d.ts.map +1 -1
  248. package/dist/types/ide.d.ts +49 -0
  249. package/dist/types/ide.d.ts.map +1 -0
  250. package/dist/types/ide.js +5 -0
  251. package/dist/types/ide.js.map +1 -0
  252. package/dist/types/platform-digest.d.ts +228 -0
  253. package/dist/types/platform-digest.d.ts.map +1 -0
  254. package/dist/types/platform-digest.js +5 -0
  255. package/dist/types/platform-digest.js.map +1 -0
  256. package/dist/types/telemetry-digest.d.ts +2 -0
  257. package/dist/types/telemetry-digest.d.ts.map +1 -1
  258. package/dist/utils/ensure-project.d.ts +1 -0
  259. package/dist/utils/ensure-project.d.ts.map +1 -1
  260. package/dist/utils/ensure-project.js +19 -7
  261. package/dist/utils/ensure-project.js.map +1 -1
  262. package/dist/utils/jfl-config.d.ts +1 -0
  263. package/dist/utils/jfl-config.d.ts.map +1 -1
  264. package/dist/utils/jfl-config.js +19 -1
  265. package/dist/utils/jfl-config.js.map +1 -1
  266. package/dist/utils/jfl-paths.d.ts +5 -0
  267. package/dist/utils/jfl-paths.d.ts.map +1 -1
  268. package/dist/utils/jfl-paths.js +25 -3
  269. package/dist/utils/jfl-paths.js.map +1 -1
  270. package/package.json +3 -2
  271. package/packages/pi/AGENTS.md +112 -0
  272. package/packages/pi/extensions/agent-grid.ts +191 -0
  273. package/packages/pi/extensions/agent-names.ts +178 -0
  274. package/packages/pi/extensions/autoresearch.ts +427 -0
  275. package/packages/pi/extensions/bookmarks.ts +85 -0
  276. package/packages/pi/extensions/context.ts +151 -0
  277. package/packages/pi/extensions/crm-tool.ts +61 -0
  278. package/packages/pi/extensions/eval-tool.ts +224 -0
  279. package/packages/pi/extensions/eval.ts +60 -0
  280. package/packages/pi/extensions/footer.ts +239 -0
  281. package/packages/pi/extensions/hud-tool.ts +145 -0
  282. package/packages/pi/extensions/index.ts +392 -0
  283. package/packages/pi/extensions/journal.ts +224 -0
  284. package/packages/pi/extensions/map-bridge.ts +178 -0
  285. package/packages/pi/extensions/memory-tool.ts +68 -0
  286. package/packages/pi/extensions/notifications.ts +73 -0
  287. package/packages/pi/extensions/peter-parker.ts +202 -0
  288. package/packages/pi/extensions/policy-head-tool.ts +276 -0
  289. package/packages/pi/extensions/portfolio-bridge.ts +90 -0
  290. package/packages/pi/extensions/session.ts +90 -0
  291. package/packages/pi/extensions/shortcuts.ts +259 -0
  292. package/packages/pi/extensions/stratus-bridge.ts +115 -0
  293. package/packages/pi/extensions/synopsis-tool.ts +83 -0
  294. package/packages/pi/extensions/tool-renderers.ts +352 -0
  295. package/packages/pi/extensions/training-buffer-tool.ts +368 -0
  296. package/packages/pi/extensions/types.ts +163 -0
  297. package/packages/pi/package-lock.json +346 -0
  298. package/packages/pi/package.json +44 -0
  299. package/packages/pi/skills/agent-browser/SKILL.md +116 -0
  300. package/packages/pi/skills/brand-architect/SKILL.md +240 -0
  301. package/packages/pi/skills/brand-architect/config.yaml +137 -0
  302. package/packages/pi/skills/campaign-hud/config.yaml +112 -0
  303. package/packages/pi/skills/content-creator/SKILL.md +294 -0
  304. package/packages/pi/skills/context/SKILL.md +65 -0
  305. package/packages/pi/skills/debug/MULTI_AGENT.md +360 -0
  306. package/packages/pi/skills/debug/SKILL.md +554 -0
  307. package/packages/pi/skills/end/SKILL.md +1782 -0
  308. package/packages/pi/skills/eval/SKILL.md +75 -0
  309. package/packages/pi/skills/fly-deploy/SKILL.md +676 -0
  310. package/packages/pi/skills/founder-video/SKILL.md +467 -0
  311. package/packages/pi/skills/hud/SKILL.md +160 -0
  312. package/packages/pi/skills/orchestrate/SKILL.md +74 -0
  313. package/packages/pi/skills/pi-agents/SKILL.md +78 -0
  314. package/packages/pi/skills/react-best-practices/AGENTS.md +2249 -0
  315. package/packages/pi/skills/react-best-practices/README.md +123 -0
  316. package/packages/pi/skills/react-best-practices/SKILL.md +125 -0
  317. package/packages/pi/skills/react-best-practices/metadata.json +15 -0
  318. package/packages/pi/skills/react-best-practices/rules/_sections.md +46 -0
  319. package/packages/pi/skills/react-best-practices/rules/_template.md +28 -0
  320. package/packages/pi/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
  321. package/packages/pi/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
  322. package/packages/pi/skills/react-best-practices/rules/async-api-routes.md +38 -0
  323. package/packages/pi/skills/react-best-practices/rules/async-defer-await.md +80 -0
  324. package/packages/pi/skills/react-best-practices/rules/async-dependencies.md +36 -0
  325. package/packages/pi/skills/react-best-practices/rules/async-parallel.md +28 -0
  326. package/packages/pi/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
  327. package/packages/pi/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
  328. package/packages/pi/skills/react-best-practices/rules/bundle-conditional.md +31 -0
  329. package/packages/pi/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
  330. package/packages/pi/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
  331. package/packages/pi/skills/react-best-practices/rules/bundle-preload.md +50 -0
  332. package/packages/pi/skills/react-best-practices/rules/client-event-listeners.md +74 -0
  333. package/packages/pi/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
  334. package/packages/pi/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
  335. package/packages/pi/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
  336. package/packages/pi/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
  337. package/packages/pi/skills/react-best-practices/rules/js-cache-storage.md +70 -0
  338. package/packages/pi/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
  339. package/packages/pi/skills/react-best-practices/rules/js-early-exit.md +50 -0
  340. package/packages/pi/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
  341. package/packages/pi/skills/react-best-practices/rules/js-index-maps.md +37 -0
  342. package/packages/pi/skills/react-best-practices/rules/js-length-check-first.md +49 -0
  343. package/packages/pi/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
  344. package/packages/pi/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
  345. package/packages/pi/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
  346. package/packages/pi/skills/react-best-practices/rules/rendering-activity.md +26 -0
  347. package/packages/pi/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
  348. package/packages/pi/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
  349. package/packages/pi/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
  350. package/packages/pi/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
  351. package/packages/pi/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
  352. package/packages/pi/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
  353. package/packages/pi/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
  354. package/packages/pi/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
  355. package/packages/pi/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
  356. package/packages/pi/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
  357. package/packages/pi/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
  358. package/packages/pi/skills/react-best-practices/rules/rerender-memo.md +44 -0
  359. package/packages/pi/skills/react-best-practices/rules/rerender-transitions.md +40 -0
  360. package/packages/pi/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
  361. package/packages/pi/skills/react-best-practices/rules/server-cache-lru.md +41 -0
  362. package/packages/pi/skills/react-best-practices/rules/server-cache-react.md +26 -0
  363. package/packages/pi/skills/react-best-practices/rules/server-parallel-fetching.md +79 -0
  364. package/packages/pi/skills/react-best-practices/rules/server-serialization.md +38 -0
  365. package/packages/pi/skills/remotion-best-practices/SKILL.md +43 -0
  366. package/packages/pi/skills/remotion-best-practices/rules/3d.md +86 -0
  367. package/packages/pi/skills/remotion-best-practices/rules/animations.md +29 -0
  368. package/packages/pi/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
  369. package/packages/pi/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
  370. package/packages/pi/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
  371. package/packages/pi/skills/remotion-best-practices/rules/assets.md +78 -0
  372. package/packages/pi/skills/remotion-best-practices/rules/audio.md +172 -0
  373. package/packages/pi/skills/remotion-best-practices/rules/calculate-metadata.md +104 -0
  374. package/packages/pi/skills/remotion-best-practices/rules/can-decode.md +75 -0
  375. package/packages/pi/skills/remotion-best-practices/rules/charts.md +58 -0
  376. package/packages/pi/skills/remotion-best-practices/rules/compositions.md +146 -0
  377. package/packages/pi/skills/remotion-best-practices/rules/display-captions.md +126 -0
  378. package/packages/pi/skills/remotion-best-practices/rules/extract-frames.md +229 -0
  379. package/packages/pi/skills/remotion-best-practices/rules/fonts.md +152 -0
  380. package/packages/pi/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
  381. package/packages/pi/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
  382. package/packages/pi/skills/remotion-best-practices/rules/get-video-duration.md +58 -0
  383. package/packages/pi/skills/remotion-best-practices/rules/gifs.md +138 -0
  384. package/packages/pi/skills/remotion-best-practices/rules/images.md +130 -0
  385. package/packages/pi/skills/remotion-best-practices/rules/import-srt-captions.md +67 -0
  386. package/packages/pi/skills/remotion-best-practices/rules/lottie.md +68 -0
  387. package/packages/pi/skills/remotion-best-practices/rules/measuring-dom-nodes.md +35 -0
  388. package/packages/pi/skills/remotion-best-practices/rules/measuring-text.md +143 -0
  389. package/packages/pi/skills/remotion-best-practices/rules/sequencing.md +106 -0
  390. package/packages/pi/skills/remotion-best-practices/rules/tailwind.md +11 -0
  391. package/packages/pi/skills/remotion-best-practices/rules/text-animations.md +20 -0
  392. package/packages/pi/skills/remotion-best-practices/rules/timing.md +179 -0
  393. package/packages/pi/skills/remotion-best-practices/rules/transcribe-captions.md +19 -0
  394. package/packages/pi/skills/remotion-best-practices/rules/transitions.md +122 -0
  395. package/packages/pi/skills/remotion-best-practices/rules/trimming.md +53 -0
  396. package/packages/pi/skills/remotion-best-practices/rules/videos.md +171 -0
  397. package/packages/pi/skills/search/SKILL.md +220 -0
  398. package/packages/pi/skills/spec/SKILL.md +377 -0
  399. package/packages/pi/skills/startup/SKILL.md +315 -0
  400. package/packages/pi/skills/web-architect/SKILL.md +309 -0
  401. package/packages/pi/skills/x-algorithm/SKILL.md +305 -0
  402. package/packages/pi/teams/dev-team.yaml +63 -0
  403. package/packages/pi/teams/gtm-team.yaml +79 -0
  404. package/packages/pi/themes/jfl.theme.json +76 -0
  405. package/packages/pi/tsconfig.json +21 -0
  406. package/scripts/collect-tuples.sh +124 -0
  407. package/scripts/destroy-fleet.sh +37 -0
  408. package/scripts/jfl-ide.sh +48 -0
  409. package/scripts/session/session-cleanup.sh +4 -11
  410. package/scripts/session/session-init.sh +6 -0
  411. package/scripts/session/session-sync.sh +25 -0
  412. package/scripts/setup-branch-protection.sh +106 -0
  413. package/scripts/spawn-fleet.sh +144 -0
  414. package/scripts/train-policy-head.py +434 -0
  415. package/scripts/vm-swarm/README.md +301 -0
  416. package/scripts/vm-swarm/collect-tuples.sh +331 -0
  417. package/scripts/vm-swarm/create-base-template.sh +339 -0
  418. package/scripts/vm-swarm/kill-fleet.sh +204 -0
  419. package/scripts/vm-swarm/monitor-fleet.sh +346 -0
  420. package/scripts/vm-swarm/spawn-fleet.sh +304 -0
  421. package/template/.github/workflows/jfl-eval.yml +105 -8
  422. package/template/.github/workflows/jfl-review.yml +4 -0
  423. package/template/scripts/session/session-end.sh +69 -6
  424. package/template/scripts/session/session-init.sh +55 -30
  425. package/template/scripts/session/session-lock.sh +464 -0
  426. package/template/templates/service-agent/workflows/jfl-eval.yml +19 -0
  427. package/dist/dashboard-static/assets/index-B6kRK9Rq.js +0 -116
  428. package/dist/dashboard-static/assets/index-BpdKJPLu.css +0 -1
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Journal Extension
3
+ *
4
+ * Detects git commits via tool_execution_end, prompts for journal entries,
5
+ * registers the /journal command with interactive type selection,
6
+ * shows themed journal stream in below-editor widget.
7
+ *
8
+ * @purpose Auto-journal detection + interactive /journal command + themed widget
9
+ */
10
+
11
+ import { existsSync, readFileSync, appendFileSync, mkdirSync } from "fs"
12
+ import { join } from "path"
13
+ import { execSync } from "child_process"
14
+ import type { PiContext, PiTheme, JflConfig, AgentEndEvent, ToolExecutionEvent } from "./types.js"
15
+ import { getCurrentBranch } from "./session.js"
16
+ import { emitCustomEvent } from "./map-bridge.js"
17
+
18
+ let projectRoot = ""
19
+
20
+ interface JournalEntry {
21
+ v: number
22
+ ts: string
23
+ session: string
24
+ type: string
25
+ status: string
26
+ title: string
27
+ summary: string
28
+ detail?: string
29
+ files?: string[]
30
+ incomplete?: string[]
31
+ next?: string
32
+ learned?: string[]
33
+ }
34
+
35
+ function getJournalPath(root: string): string {
36
+ const branch = getCurrentBranch(root)
37
+ return join(root, ".jfl", "journal", `${branch}.jsonl`)
38
+ }
39
+
40
+ function appendJournalEntry(root: string, entry: JournalEntry): void {
41
+ const journalPath = getJournalPath(root)
42
+ mkdirSync(join(root, ".jfl", "journal"), { recursive: true })
43
+ appendFileSync(journalPath, JSON.stringify(entry) + "\n")
44
+ }
45
+
46
+ function readRecentEntries(root: string, count = 5): JournalEntry[] {
47
+ const journalPath = getJournalPath(root)
48
+ if (!existsSync(journalPath)) return []
49
+ try {
50
+ const lines = readFileSync(journalPath, "utf-8")
51
+ .split("\n")
52
+ .filter(Boolean)
53
+ .slice(-count)
54
+ return lines.map(l => JSON.parse(l) as JournalEntry)
55
+ } catch {
56
+ return []
57
+ }
58
+ }
59
+
60
+ function hasJournalEntryForSession(root: string, sessionBranch: string): boolean {
61
+ const journalPath = join(root, ".jfl", "journal", `${sessionBranch}.jsonl`)
62
+ if (!existsSync(journalPath)) return false
63
+ const content = readFileSync(journalPath, "utf-8").trim()
64
+ return content.length > 0
65
+ }
66
+
67
+ const TYPE_ICONS: Record<string, string> = {
68
+ feature: "✦",
69
+ fix: "✧",
70
+ decision: "◆",
71
+ discovery: "◇",
72
+ milestone: "★",
73
+ "session-end": "●",
74
+ spec: "□",
75
+ }
76
+
77
+ const TYPE_COLORS: Record<string, string> = {
78
+ feature: "success",
79
+ fix: "error",
80
+ decision: "warning",
81
+ discovery: "accent",
82
+ milestone: "warning",
83
+ "session-end": "dim",
84
+ spec: "muted",
85
+ }
86
+
87
+ function showRecentJournal(_ctx: PiContext): void {
88
+ // Widget removed — journal entries visible via /journal or Ctrl+Shift+J
89
+ }
90
+
91
+ export async function setupJournal(ctx: PiContext, _config: JflConfig): Promise<void> {
92
+ projectRoot = ctx.session.projectRoot
93
+
94
+ showRecentJournal(ctx)
95
+
96
+ ctx.registerCommand({
97
+ name: "journal",
98
+ description: "Write a journal entry for the current session",
99
+ async handler(_args, ctx) {
100
+ if (ctx.ui.hasUI) {
101
+ const types = [
102
+ { value: "feature" as const, label: "✦ Feature", description: "Something built or completed" },
103
+ { value: "fix" as const, label: "✧ Fix", description: "Bug found and fixed" },
104
+ { value: "decision" as const, label: "◆ Decision", description: "Choice made between options" },
105
+ { value: "discovery" as const, label: "◇ Discovery", description: "Insight or learning" },
106
+ { value: "milestone" as const, label: "★ Milestone", description: "Major goal reached" },
107
+ ]
108
+
109
+ const type = await ctx.ui.select("Journal Entry Type", types)
110
+ if (!type) return
111
+
112
+ const title = await ctx.ui.input("Title", "What happened?")
113
+ if (!title?.trim()) return
114
+
115
+ const summary = await ctx.ui.input("Summary (2-3 sentences)", "Brief description")
116
+
117
+ const entry: JournalEntry = {
118
+ v: 1,
119
+ ts: new Date().toISOString(),
120
+ session: getCurrentBranch(projectRoot),
121
+ type,
122
+ status: "complete",
123
+ title: title.trim(),
124
+ summary: summary?.trim() ?? title.trim(),
125
+ }
126
+
127
+ appendJournalEntry(projectRoot, entry)
128
+ await emitCustomEvent(ctx, "journal:entry", entry)
129
+ ctx.emit("journal:written", entry)
130
+ showRecentJournal(ctx)
131
+ ctx.ui.notify(`${TYPE_ICONS[type] ?? "·"} Journal: ${title.trim()}`, { level: "success" })
132
+ ctx.ui.setStatus("journal", undefined)
133
+ return
134
+ }
135
+
136
+ // Fallback: raw JSON input for non-interactive mode
137
+ const branch = getCurrentBranch(projectRoot)
138
+ const template = JSON.stringify({
139
+ v: 1,
140
+ ts: new Date().toISOString(),
141
+ session: branch,
142
+ type: "feature",
143
+ status: "complete",
144
+ title: "",
145
+ summary: "",
146
+ detail: "",
147
+ files: [],
148
+ }, null, 2)
149
+
150
+ const content = await ctx.ui.input("Journal Entry (paste JSON)", template)
151
+ if (!content?.trim()) return
152
+
153
+ try {
154
+ const entry = JSON.parse(content) as JournalEntry
155
+ appendJournalEntry(projectRoot, entry)
156
+ await emitCustomEvent(ctx, "journal:entry", entry)
157
+ ctx.emit("journal:written", entry)
158
+ showRecentJournal(ctx)
159
+ ctx.ui.notify("Journal entry saved", { level: "success" })
160
+ } catch {
161
+ ctx.ui.notify("Invalid JSON — entry not saved. Use /journal and paste valid JSON.", { level: "warn" })
162
+ }
163
+ },
164
+ })
165
+
166
+ ctx.on("map:journal:entry", () => showRecentJournal(ctx))
167
+ ctx.on("journal:written", () => showRecentJournal(ctx))
168
+ }
169
+
170
+ export async function onToolExecutionEnd(
171
+ ctx: PiContext,
172
+ event: ToolExecutionEvent
173
+ ): Promise<void> {
174
+ const toolName = event.toolName ?? event.tool ?? ""
175
+ if (toolName.toLowerCase() !== "bash") return
176
+
177
+ const result = String(event.result ?? "")
178
+ const isGitCommit = /\[[\w/.-]+\s+[0-9a-f]{7,}\]/.test(result)
179
+ if (!isGitCommit) return
180
+
181
+ let commitMsg = ""
182
+ let commitFiles = ""
183
+ try {
184
+ commitMsg = execSync("git log -1 --pretty=%B", { cwd: projectRoot }).toString().trim()
185
+ commitFiles = execSync("git diff-tree --no-commit-id --name-only -r HEAD", { cwd: projectRoot })
186
+ .toString().trim()
187
+ } catch {}
188
+
189
+ ctx.emit("pi:git-commit-detected", { message: commitMsg, files: commitFiles })
190
+
191
+ ctx.ui.notify([
192
+ "Git commit detected — Ctrl+Shift+J for quick journal or /journal",
193
+ commitMsg ? `Commit: ${commitMsg.split("\n")[0]}` : "",
194
+ commitFiles ? `Files: ${commitFiles.split("\n").slice(0, 3).join(", ")}` : "",
195
+ ].filter(Boolean).join("\n"), { level: "info" })
196
+ }
197
+
198
+ export async function onJournalAgentEnd(
199
+ _ctx: PiContext,
200
+ _event: AgentEndEvent
201
+ ): Promise<void> {
202
+ // Removed: "Journal entry recommended" nudge (noisy)
203
+ }
204
+
205
+ export async function checkJournalBeforeCompact(
206
+ ctx: PiContext
207
+ ): Promise<{ cancel: true } | void> {
208
+ const branch = getCurrentBranch(projectRoot)
209
+ if (!hasJournalEntryForSession(projectRoot, branch)) {
210
+ if (ctx.ui.hasUI) {
211
+ const ok = await ctx.ui.confirm(
212
+ "No Journal Entry",
213
+ "No journal entry for this session. Compacting without one loses context.\n\nContinue anyway?"
214
+ )
215
+ if (!ok) return ctx.cancel()
216
+ } else {
217
+ ctx.ui.notify(
218
+ "No journal entry for this session. Write one with /journal before compacting.",
219
+ { level: "warn" }
220
+ )
221
+ return ctx.cancel()
222
+ }
223
+ }
224
+ }
@@ -0,0 +1,178 @@
1
+ /**
2
+ * MAP Event Bridge Extension
3
+ *
4
+ * Translates Pi lifecycle events → MAP event bus (Context Hub HTTP).
5
+ * Also subscribes to hub SSE stream and re-emits as Pi custom events.
6
+ * Enforces scope filtering from .jfl/config.json context_scope.
7
+ *
8
+ * @purpose Pi lifecycle → MAP bus bridge with scope enforcement
9
+ */
10
+
11
+ import { readFileSync, existsSync } from "fs"
12
+ import { join } from "path"
13
+ import type { PiContext, JflConfig, AgentEndEvent, ToolExecutionEvent } from "./types.js"
14
+
15
+ interface MAPEvent {
16
+ type: string
17
+ source: string
18
+ data?: unknown
19
+ ts?: string
20
+ }
21
+
22
+ interface ContextScope {
23
+ produces?: string[]
24
+ consumes?: string[]
25
+ denied?: string[]
26
+ }
27
+
28
+ let hubUrl = "http://localhost:4242"
29
+ let authToken: string | null = null
30
+ let scope: ContextScope = {}
31
+ let sseAbort: AbortController | null = null
32
+
33
+ const PI_TO_MAP: Record<string, string> = {
34
+ "hook:session-start": "hook:session-start",
35
+ "hook:session-end": "hook:session-end",
36
+ "task:started": "task:started",
37
+ "task:completed": "task:completed",
38
+ "hook:tool-use": "hook:tool-use",
39
+ "hook:tool-result": "hook:tool-result",
40
+ }
41
+
42
+ function readToken(root: string): string | null {
43
+ const tokenPath = join(root, ".jfl", "context-hub.token")
44
+ if (existsSync(tokenPath)) {
45
+ return readFileSync(tokenPath, "utf-8").trim()
46
+ }
47
+ return null
48
+ }
49
+
50
+ function matchPattern(pattern: string, value: string): boolean {
51
+ if (pattern === "*") return true
52
+ if (pattern === value) return true
53
+ if (pattern.includes("*")) {
54
+ const regex = new RegExp("^" + pattern.replace(/\*/g, ".*") + "$")
55
+ return regex.test(value)
56
+ }
57
+ return false
58
+ }
59
+
60
+ function isEventAllowedToEmit(eventType: string, src: ContextScope): boolean {
61
+ if (!src.produces || src.produces.length === 0) return true
62
+ return src.produces.some(p => matchPattern(p, eventType))
63
+ }
64
+
65
+ function isEventDenied(eventType: string, src: ContextScope): boolean {
66
+ if (!src.denied || src.denied.length === 0) return false
67
+ return src.denied.some(d => matchPattern(d, eventType))
68
+ }
69
+
70
+ async function postToHub(event: MAPEvent): Promise<void> {
71
+ if (isEventDenied(event.type, scope)) return
72
+ if (!isEventAllowedToEmit(event.type, scope)) return
73
+
74
+ try {
75
+ const response = await fetch(`${hubUrl}/api/events`, {
76
+ method: "POST",
77
+ headers: {
78
+ "Content-Type": "application/json",
79
+ ...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
80
+ },
81
+ body: JSON.stringify(event),
82
+ })
83
+ if (!response.ok) {
84
+ // Silently ignore hub connectivity issues — it may not be running
85
+ }
86
+ } catch {
87
+ // Hub not available — non-fatal
88
+ }
89
+ }
90
+
91
+ async function subscribeToHubSSE(ctx: PiContext): Promise<void> {
92
+ sseAbort = new AbortController()
93
+
94
+ const consumesPatterns = scope.consumes ?? ["*"]
95
+
96
+ try {
97
+ const patternsParam = consumesPatterns.join(",")
98
+ const url = `${hubUrl}/api/events/stream?pattern=${encodeURIComponent(patternsParam)}`
99
+
100
+ const resp = await fetch(url, {
101
+ headers: authToken ? { Authorization: `Bearer ${authToken}` } : {},
102
+ signal: sseAbort.signal,
103
+ })
104
+
105
+ if (!resp.ok || !resp.body) return
106
+
107
+ const reader = resp.body.getReader()
108
+ const decoder = new TextDecoder()
109
+ let buffer = ""
110
+
111
+ while (true) {
112
+ const { done, value } = await reader.read()
113
+ if (done) break
114
+
115
+ buffer += decoder.decode(value, { stream: true })
116
+ const lines = buffer.split("\n")
117
+ buffer = lines.pop() ?? ""
118
+
119
+ for (const line of lines) {
120
+ if (!line.startsWith("data: ")) continue
121
+ try {
122
+ const event = JSON.parse(line.slice(6)) as MAPEvent
123
+ if (isEventDenied(event.type, scope)) continue
124
+ ctx.emit(`map:${event.type}`, event)
125
+ } catch {}
126
+ }
127
+ }
128
+ } catch (err: unknown) {
129
+ if ((err as { name?: string }).name === "AbortError") return
130
+ // Silently ignore — Context Hub may not be running
131
+ }
132
+ }
133
+
134
+ export async function setupMapBridge(ctx: PiContext, config: JflConfig): Promise<void> {
135
+ const root = ctx.session.projectRoot
136
+
137
+ authToken = readToken(root)
138
+ scope = config.context_scope ?? {}
139
+
140
+ const portFile = join(root, ".jfl", "context-hub.port")
141
+ if (existsSync(portFile)) {
142
+ const port = readFileSync(portFile, "utf-8").trim()
143
+ if (port) hubUrl = `http://localhost:${port}`
144
+ }
145
+
146
+ ctx.on("hook:session-start", (data) => postToHub({ type: "hook:session-start", source: "pi-session", data, ts: new Date().toISOString() }))
147
+ ctx.on("hook:session-end", (data) => postToHub({ type: "hook:session-end", source: "pi-session", data, ts: new Date().toISOString() }))
148
+
149
+ subscribeToHubSSE(ctx).catch(() => {})
150
+ }
151
+
152
+ export async function onMapBridgeShutdown(_ctx: PiContext): Promise<void> {
153
+ sseAbort?.abort()
154
+ sseAbort = null
155
+ }
156
+
157
+ export async function emitAgentStart(ctx: PiContext, event: unknown): Promise<void> {
158
+ await postToHub({ type: "task:started", source: `pi-agent:${ctx.session.id}`, data: event, ts: new Date().toISOString() })
159
+ }
160
+
161
+ export async function emitAgentEnd(ctx: PiContext, event: AgentEndEvent): Promise<void> {
162
+ await postToHub({ type: "task:completed", source: `pi-agent:${ctx.session.id}`, data: event, ts: new Date().toISOString() })
163
+ }
164
+
165
+ export async function onMapToolEnd(ctx: PiContext, event: ToolExecutionEvent): Promise<void> {
166
+ await postToHub({
167
+ type: "hook:tool-result",
168
+ source: `pi-agent:${ctx.session.id}`,
169
+ data: { tool: event.toolName ?? event.tool, duration: event.duration },
170
+ ts: new Date().toISOString(),
171
+ })
172
+ }
173
+
174
+ export async function emitCustomEvent(ctx: PiContext, type: string, data: unknown): Promise<void> {
175
+ await postToHub({ type, source: `pi:${ctx.session.id}`, data, ts: new Date().toISOString() })
176
+ }
177
+
178
+ export { hubUrl, authToken }
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Memory Tool Extension
3
+ *
4
+ * Registers jfl_memory_search tool with custom TUI rendering.
5
+ * Queries Context Hub memory API and renders results with
6
+ * type-colored headers and collapsible sections.
7
+ *
8
+ * @purpose jfl_memory_search tool — themed semantic memory search results
9
+ */
10
+
11
+ import type { PiContext } from "./types.js"
12
+ import { hubUrl, authToken } from "./map-bridge.js"
13
+ import { memoryRenderCall, memoryRenderResult } from "./tool-renderers.js"
14
+
15
+ export function setupMemoryTool(ctx: PiContext): void {
16
+ ctx.registerTool({
17
+ name: "jfl_memory_search",
18
+ description: "Search JFL project memory — find past decisions, learnings, and patterns across all sessions",
19
+ promptSnippet: "Search project memory for decisions, learnings, and session history",
20
+ inputSchema: {
21
+ type: "object",
22
+ properties: {
23
+ query: {
24
+ type: "string",
25
+ description: "Search query to find relevant memories",
26
+ },
27
+ limit: {
28
+ type: "number",
29
+ description: "Maximum results (default: 10)",
30
+ },
31
+ type: {
32
+ type: "string",
33
+ description: "Filter by entry type: feature, fix, decision, discovery, milestone, all",
34
+ enum: ["feature", "fix", "decision", "discovery", "milestone", "all"],
35
+ },
36
+ },
37
+ required: ["query"],
38
+ },
39
+ async handler(input) {
40
+ const { query, limit, type } = input as { query: string; limit?: number; type?: string }
41
+
42
+ try {
43
+ const params = new URLSearchParams({ query, limit: String(limit ?? 10) })
44
+ if (type && type !== "all") params.set("type", type)
45
+
46
+ const resp = await fetch(`${hubUrl}/api/memory/search?${params}`, {
47
+ headers: authToken ? { Authorization: `Bearer ${authToken}` } : {},
48
+ })
49
+
50
+ if (!resp.ok) return "Memory search unavailable."
51
+ const data = await resp.json() as { results?: Array<{ content: string; title?: string; ts?: string; type?: string }> }
52
+
53
+ if (!data.results?.length) return "No memories found."
54
+
55
+ return data.results
56
+ .map(r => {
57
+ const header = [r.type && `[${r.type}]`, r.title].filter(Boolean).join(" ")
58
+ return [header, r.content].filter(Boolean).join("\n")
59
+ })
60
+ .join("\n\n---\n\n")
61
+ } catch {
62
+ return "Memory search unavailable — Context Hub may not be running."
63
+ }
64
+ },
65
+ renderCall: memoryRenderCall,
66
+ renderResult: memoryRenderResult,
67
+ })
68
+ }
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Native Notifications Extension
3
+ *
4
+ * Sends terminal notifications (OSC 777/99) when:
5
+ * - Agent finishes a long task (>10s)
6
+ * - Journal entry is recommended
7
+ * - Eval score changes significantly
8
+ *
9
+ * Supports Ghostty, iTerm2, WezTerm, Kitty, Windows Terminal.
10
+ *
11
+ * @purpose Terminal-native notifications for key JFL events
12
+ */
13
+
14
+ import type { PiContext, JflConfig } from "./types.js"
15
+
16
+ let enabled = true
17
+ let agentStartTime = 0
18
+ const LONG_TASK_THRESHOLD_MS = 10000
19
+
20
+ function notifyOSC777(title: string, body: string): void {
21
+ process.stdout.write(`\x1b]777;notify;${title};${body}\x07`)
22
+ }
23
+
24
+ function notifyOSC99(title: string, body: string): void {
25
+ process.stdout.write(`\x1b]99;i=1:d=0;${title}\x1b\\`)
26
+ process.stdout.write(`\x1b]99;i=1:p=body;${body}\x1b\\`)
27
+ }
28
+
29
+ function notify(title: string, body: string): void {
30
+ if (!enabled) return
31
+
32
+ try {
33
+ if (process.env.KITTY_WINDOW_ID) {
34
+ notifyOSC99(title, body)
35
+ } else {
36
+ notifyOSC777(title, body)
37
+ }
38
+ } catch {}
39
+ }
40
+
41
+ export function setupNotifications(ctx: PiContext, config: JflConfig): void {
42
+ if (config.pi?.disable_notifications) {
43
+ enabled = false
44
+ return
45
+ }
46
+
47
+ ctx.on("agent:start", () => {
48
+ agentStartTime = Date.now()
49
+ })
50
+
51
+ ctx.on("agent:end", (data: any) => {
52
+ const elapsed = Date.now() - agentStartTime
53
+ if (elapsed > LONG_TASK_THRESHOLD_MS) {
54
+ const seconds = Math.floor(elapsed / 1000)
55
+ const reason = data?.exitReason ?? "done"
56
+ notify("JFL", `Agent finished (${seconds}s) — ${reason}`)
57
+ }
58
+ })
59
+
60
+ ctx.on("journal:written", (data: any) => {
61
+ const title = data?.title ?? "entry"
62
+ notify("JFL Journal", title)
63
+ })
64
+
65
+ ctx.registerCommand({
66
+ name: "notify",
67
+ description: "Toggle native terminal notifications",
68
+ async handler(_args, ctx) {
69
+ enabled = !enabled
70
+ ctx.ui.notify(`Notifications ${enabled ? "enabled" : "disabled"}`, { level: "info" })
71
+ },
72
+ })
73
+ }