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,259 @@
1
+ /**
2
+ * Keyboard Shortcuts Extension
3
+ *
4
+ * Registers Ctrl+H (HUD overlay), Ctrl+J (quick journal), Ctrl+G (agent grid).
5
+ * Each opens an interactive overlay — no slash commands needed.
6
+ *
7
+ * @purpose Keyboard shortcuts for instant TUI overlays
8
+ */
9
+
10
+ import { existsSync, readFileSync, readdirSync, appendFileSync, mkdirSync } from "fs"
11
+ import { join } from "path"
12
+ import { execSync } from "child_process"
13
+ import type { PiContext, PiTheme, JflConfig } from "./types.js"
14
+ import { getCurrentBranch } from "./session.js"
15
+ import { resolveDisplayName } from "./agent-names.js"
16
+
17
+ let projectRoot = ""
18
+
19
+ // ─── HUD Overlay ────────────────────────────────────────────────────────────
20
+
21
+ function buildHudOverlayData(root: string) {
22
+ const config = existsSync(join(root, ".jfl", "config.json"))
23
+ ? JSON.parse(readFileSync(join(root, ".jfl", "config.json"), "utf-8"))
24
+ : {}
25
+
26
+ const name = config.name ?? root.split("/").pop() ?? "JFL"
27
+ const type = config.type ?? "gtm"
28
+
29
+ let phase = "unknown"
30
+ let launchDate = ""
31
+ const roadmapPath = join(root, "knowledge", "ROADMAP.md")
32
+ if (existsSync(roadmapPath)) {
33
+ const content = readFileSync(roadmapPath, "utf-8")
34
+ const phaseMatch = content.match(/## (?:Phase|Current Phase)[:\s]*([^\n]+)/i)
35
+ if (phaseMatch) phase = phaseMatch[1].trim()
36
+ const dateMatch = content.match(/(\d{4}-\d{2}-\d{2})/m)
37
+ if (dateMatch) {
38
+ launchDate = dateMatch[1]
39
+ const diff = Math.ceil((new Date(dateMatch[1]).getTime() - Date.now()) / 86400000)
40
+ launchDate = diff > 0 ? `${dateMatch[1]} (${diff}d away)` : `${dateMatch[1]} (${Math.abs(diff)}d ago)`
41
+ }
42
+ }
43
+
44
+ const journalDir = join(root, ".jfl", "journal")
45
+ const recentJournal: string[] = []
46
+ if (existsSync(journalDir)) {
47
+ const files = readdirSync(journalDir).filter(f => f.endsWith(".jsonl")).sort()
48
+ for (const f of files.slice(-3)) {
49
+ try {
50
+ const lines = readFileSync(join(journalDir, f), "utf-8").trim().split("\n").filter(Boolean)
51
+ for (const l of lines.slice(-3)) {
52
+ const e = JSON.parse(l)
53
+ recentJournal.push(`[${e.type}] ${e.title}`)
54
+ }
55
+ } catch {}
56
+ }
57
+ }
58
+
59
+ let branch = "main"
60
+ try { branch = execSync("git branch --show-current", { cwd: root, stdio: ["pipe", "pipe", "ignore"] }).toString().trim() } catch {}
61
+
62
+ return { name, type, phase, launchDate, branch, recentJournal: recentJournal.slice(-5) }
63
+ }
64
+
65
+ async function showHudOverlay(ctx: PiContext): Promise<void> {
66
+ if (!ctx.ui.hasUI) return
67
+
68
+ const data = buildHudOverlayData(projectRoot)
69
+
70
+ await ctx.ui.custom<void>((tui: any, theme: PiTheme, _kb: any, done: (r: void) => void) => {
71
+ return {
72
+ handleInput(key: string) {
73
+ // Any of: escape, q, h
74
+ if (key === "\x1b" || key === "q" || key === "h") done(undefined)
75
+ },
76
+
77
+ render(width: number): string[] {
78
+ const w = Math.min(width, 72)
79
+ const inner = w - 4
80
+ const lines: string[] = []
81
+
82
+ const hr = theme.fg("border", "─".repeat(w))
83
+ const pad = (s: string) => ` ${s}`
84
+
85
+ lines.push("")
86
+ lines.push(pad(`${theme.fg("accent", "◆")} ${theme.bold(data.name)} ${theme.fg("muted", `[${data.type}]`)} ${theme.fg("dim", `on ${data.branch}`)}`))
87
+ lines.push(hr)
88
+ lines.push("")
89
+
90
+ lines.push(pad(`${theme.fg("accent", "Phase")} ${theme.fg("text", data.phase)}`))
91
+ if (data.launchDate) {
92
+ lines.push(pad(`${theme.fg("accent", "Launch")} ${theme.fg("text", data.launchDate)}`))
93
+ }
94
+
95
+ lines.push("")
96
+ lines.push(pad(theme.fg("accent", "Recent Journal")))
97
+ if (data.recentJournal.length === 0) {
98
+ lines.push(pad(theme.fg("dim", " No entries yet")))
99
+ } else {
100
+ for (const entry of data.recentJournal) {
101
+ lines.push(pad(theme.fg("muted", ` ${entry}`)))
102
+ }
103
+ }
104
+
105
+ lines.push("")
106
+ lines.push(pad(theme.fg("dim", "Esc/q close │ /hud full dashboard")))
107
+ lines.push("")
108
+
109
+ return lines
110
+ },
111
+
112
+ invalidate() {},
113
+ }
114
+ }, { overlay: true })
115
+ }
116
+
117
+ // ─── Quick Journal Overlay ──────────────────────────────────────────────────
118
+
119
+ async function showJournalOverlay(ctx: PiContext): Promise<void> {
120
+ if (!ctx.ui.hasUI) return
121
+
122
+ const types = ["feature", "fix", "decision", "discovery", "milestone"]
123
+
124
+ const type = await ctx.ui.select("Journal Entry Type", types.map(t => ({
125
+ value: t,
126
+ label: t,
127
+ description: t === "feature" ? "Something built" : t === "fix" ? "Bug fixed" : t === "decision" ? "Choice made" : t === "discovery" ? "Insight learned" : "Goal reached",
128
+ })))
129
+
130
+ if (!type) return
131
+
132
+ const title = await ctx.ui.input("Title", "What happened?")
133
+ if (!title?.trim()) return
134
+
135
+ const summary = await ctx.ui.input("Summary", "2-3 sentences")
136
+ if (!summary?.trim()) return
137
+
138
+ const entry = {
139
+ v: 1,
140
+ ts: new Date().toISOString(),
141
+ session: getCurrentBranch(projectRoot),
142
+ type,
143
+ status: "complete",
144
+ title: title.trim(),
145
+ summary: summary.trim(),
146
+ }
147
+
148
+ const journalPath = join(projectRoot, ".jfl", "journal", `${getCurrentBranch(projectRoot)}.jsonl`)
149
+ mkdirSync(join(projectRoot, ".jfl", "journal"), { recursive: true })
150
+ appendFileSync(journalPath, JSON.stringify(entry) + "\n")
151
+
152
+ ctx.ui.notify(`Journal: ${title.trim()}`, { level: "success" })
153
+ ctx.emit("journal:written", entry)
154
+ }
155
+
156
+ // ─── Agent Grid Overlay ─────────────────────────────────────────────────────
157
+
158
+ async function showGridOverlay(ctx: PiContext): Promise<void> {
159
+ if (!ctx.ui.hasUI) return
160
+
161
+ await ctx.ui.custom<void>((_tui: any, theme: PiTheme, _kb: any, done: (r: void) => void) => {
162
+ let interval: ReturnType<typeof setInterval> | null = null
163
+ const agents: Array<{ name: string; status: string; task: string; age: string }> = []
164
+
165
+ function refreshAgents() {
166
+ agents.length = 0
167
+ const journalDir = join(projectRoot, ".jfl", "journal")
168
+ if (!existsSync(journalDir)) return
169
+ const now = Date.now()
170
+ for (const f of readdirSync(journalDir)) {
171
+ if (!f.startsWith("session-") || !f.endsWith(".jsonl")) continue
172
+ try {
173
+ const stat = require("fs").statSync(join(journalDir, f))
174
+ const age = now - stat.mtimeMs
175
+ if (age < 600000) {
176
+ const name = resolveDisplayName(f.replace(".jsonl", "").replace("session-", ""))
177
+ agents.push({
178
+ name,
179
+ status: age < 120000 ? "active" : "idle",
180
+ task: "session",
181
+ age: age < 60000 ? `${Math.floor(age / 1000)}s` : `${Math.floor(age / 60000)}m`,
182
+ })
183
+ }
184
+ } catch {}
185
+ }
186
+ }
187
+
188
+ refreshAgents()
189
+ interval = setInterval(() => { refreshAgents(); _tui.requestRender() }, 5000)
190
+
191
+ return {
192
+ handleInput(key: string) {
193
+ if (key === "\x1b" || key === "q" || key === "g") {
194
+ if (interval) clearInterval(interval)
195
+ done(undefined)
196
+ }
197
+ },
198
+
199
+ render(width: number): string[] {
200
+ const lines: string[] = []
201
+ lines.push("")
202
+ lines.push(` ${theme.fg("accent", "◆")} ${theme.bold("Agent Grid")} ${theme.fg("dim", `(${agents.length} sessions)`)}`)
203
+ lines.push(` ${theme.fg("border", "─".repeat(Math.min(width - 4, 60)))}`)
204
+ lines.push("")
205
+
206
+ if (agents.length === 0) {
207
+ lines.push(` ${theme.fg("dim", "No active sessions detected")}`)
208
+ lines.push(` ${theme.fg("dim", "Sessions appear here when .jfl/journal/ files are recent")}`)
209
+ } else {
210
+ for (const a of agents) {
211
+ const icon = a.status === "active" ? theme.fg("success", "●") : theme.fg("dim", "○")
212
+ lines.push(` ${icon} ${theme.fg("text", a.name)} ${theme.fg("dim", a.age)}`)
213
+ }
214
+ }
215
+
216
+ lines.push("")
217
+ lines.push(` ${theme.fg("dim", "Esc/q close │ refreshes every 5s")}`)
218
+ lines.push("")
219
+ return lines
220
+ },
221
+
222
+ invalidate() {},
223
+ }
224
+ }, { overlay: true })
225
+ }
226
+
227
+ // ─── Setup ──────────────────────────────────────────────────────────────────
228
+
229
+ export function setupShortcuts(ctx: PiContext, _config: JflConfig): void {
230
+ projectRoot = ctx.session.projectRoot
231
+
232
+ ctx.registerShortcut("ctrl+shift+h", {
233
+ description: "JFL: HUD overlay",
234
+ handler: () => showHudOverlay(ctx),
235
+ })
236
+
237
+ ctx.registerShortcut("ctrl+shift+j", {
238
+ description: "JFL: Quick journal entry",
239
+ handler: () => showJournalOverlay(ctx),
240
+ })
241
+
242
+ ctx.registerShortcut("ctrl+shift+g", {
243
+ description: "JFL: Agent grid",
244
+ handler: () => showGridOverlay(ctx),
245
+ })
246
+
247
+ ctx.registerCommand({
248
+ name: "shortcuts",
249
+ description: "Show JFL keyboard shortcuts",
250
+ async handler(_args, ctx) {
251
+ ctx.ui.notify([
252
+ "JFL Shortcuts:",
253
+ " Ctrl+Shift+H HUD overlay",
254
+ " Ctrl+Shift+J Quick journal",
255
+ " Ctrl+Shift+G Agent grid",
256
+ ].join("\n"), { level: "info" })
257
+ },
258
+ })
259
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Stratus Bridge Extension
3
+ *
4
+ * Wires the RL prediction loop: predict before agent starts, resolve after,
5
+ * emit training tuples, and buffer for nightly policy head training.
6
+ *
7
+ * @purpose RL flywheel — predict/resolve per agent turn, emit training tuples to Stratus
8
+ */
9
+
10
+ import { existsSync, appendFileSync, mkdirSync } from "fs"
11
+ import { join } from "path"
12
+ import { homedir } from "os"
13
+ import type { PiContext, AgentStartEvent, AgentEndEvent } from "./types.js"
14
+ import { emitCustomEvent } from "./map-bridge.js"
15
+
16
+ const TRAINING_BUFFER = join(homedir(), ".jfl", "training-buffer.jsonl")
17
+
18
+ let projectRoot = ""
19
+ const sessionPredictions = new Map<string, string>()
20
+ // Store current prediction per session (replaces ctx.session.custom)
21
+ let currentPredictionId: string | undefined
22
+ let currentPredictionKey: string | undefined
23
+
24
+ function getTrainingBufferPath(): string {
25
+ const dir = join(homedir(), ".jfl")
26
+ mkdirSync(dir, { recursive: true })
27
+ return TRAINING_BUFFER
28
+ }
29
+
30
+ async function getPredictor(root: string) {
31
+ try {
32
+ // @ts-ignore — resolved from jfl package at runtime
33
+ const { Predictor } = await import("../../src/lib/predictor.js")
34
+ return new Predictor(root)
35
+ } catch {
36
+ return null
37
+ }
38
+ }
39
+
40
+ function buildProposal(event: AgentStartEvent) {
41
+ return {
42
+ description: event.prompt?.slice(0, 200) ?? "agent task",
43
+ change_type: "feature" as const,
44
+ scope: "medium" as const,
45
+ }
46
+ }
47
+
48
+ export async function onAgentStart(ctx: PiContext, event: AgentStartEvent): Promise<void> {
49
+ if (!projectRoot) return
50
+
51
+ const predictor = await getPredictor(projectRoot)
52
+ if (!predictor) return
53
+
54
+ try {
55
+ const prediction = await predictor.predict({
56
+ proposal: buildProposal(event),
57
+ current_score: 0,
58
+ goal: "improve project quality",
59
+ recent_trajectory: [],
60
+ })
61
+
62
+ const key = `${ctx.session.id}:${Date.now()}`
63
+ sessionPredictions.set(key, prediction.prediction_id)
64
+ currentPredictionId = prediction.prediction_id
65
+ currentPredictionKey = key
66
+
67
+ ctx.log(`Stratus prediction: ${prediction.recommendation} (Δ${prediction.predicted_delta.toFixed(3)})`, "debug")
68
+ } catch (err) {
69
+ ctx.log(`Stratus predict failed: ${err}`, "debug")
70
+ }
71
+ }
72
+
73
+ export async function onAgentEnd(ctx: PiContext, event: AgentEndEvent): Promise<void> {
74
+ if (!projectRoot) return
75
+
76
+ const predictionId = currentPredictionId
77
+ if (!predictionId) return
78
+
79
+ const predictor = await getPredictor(projectRoot)
80
+ if (!predictor) return
81
+
82
+ try {
83
+ const actualDelta = event.exitReason === "success" ? 0.05 : 0.0
84
+ await predictor.resolve(predictionId, actualDelta, {
85
+ eval_run_id: ctx.session.id,
86
+ resolved_at: new Date().toISOString(),
87
+ })
88
+
89
+ const tuple = {
90
+ ts: new Date().toISOString(),
91
+ session_id: ctx.session.id,
92
+ prediction_id: predictionId,
93
+ actual_delta: actualDelta,
94
+ turn_count: event.turnCount ?? 0,
95
+ model: event.model,
96
+ exit_reason: event.exitReason,
97
+ }
98
+
99
+ appendFileSync(getTrainingBufferPath(), JSON.stringify(tuple) + "\n")
100
+ await emitCustomEvent(ctx, "training:tuple:added", tuple)
101
+
102
+ currentPredictionId = undefined
103
+ currentPredictionKey = undefined
104
+ } catch (err) {
105
+ ctx.log(`Stratus resolve failed: ${err}`, "debug")
106
+ }
107
+ }
108
+
109
+ export function setProjectRoot(root: string): void {
110
+ projectRoot = root
111
+ }
112
+
113
+ export function initStratusBridge(root: string): void {
114
+ projectRoot = root
115
+ }
@@ -0,0 +1,83 @@
1
+ /**
2
+ * Synopsis Tool Extension
3
+ *
4
+ * Registers jfl_synopsis tool with custom TUI rendering.
5
+ * Renders work summaries with color-coded sections for features,
6
+ * fixes, decisions, and time breakdowns.
7
+ *
8
+ * @purpose jfl_synopsis tool — themed work summary with category colors
9
+ */
10
+
11
+ import { execSync } from "child_process"
12
+ import { existsSync } from "fs"
13
+ import { join } from "path"
14
+ import type { PiContext } from "./types.js"
15
+ import { synopsisRenderCall, synopsisRenderResult } from "./tool-renderers.js"
16
+
17
+ let projectRoot = ""
18
+
19
+ function findSynopsisScript(root: string): string | null {
20
+ const candidates = [
21
+ join(root, "product", "packages", "memory", "dist", "journal", "cli.js"),
22
+ join(root, "..", "jfl-platform", "packages", "memory", "dist", "journal", "cli.js"),
23
+ ]
24
+ for (const c of candidates) {
25
+ if (existsSync(c)) return c
26
+ }
27
+ return null
28
+ }
29
+
30
+ export function setupSynopsisTool(ctx: PiContext): void {
31
+ projectRoot = ctx.session.projectRoot
32
+
33
+ ctx.registerTool({
34
+ name: "jfl_synopsis",
35
+ description: "Get a work summary across all sessions — what happened, who did what, time breakdown",
36
+ promptSnippet: "Summarize recent work: features, fixes, decisions, time audit",
37
+ inputSchema: {
38
+ type: "object",
39
+ properties: {
40
+ hours: {
41
+ type: "number",
42
+ description: "How many hours back to summarize (default: 24)",
43
+ },
44
+ author: {
45
+ type: "string",
46
+ description: "Filter by git author name or username",
47
+ },
48
+ },
49
+ },
50
+ async handler(input) {
51
+ const { hours, author } = input as { hours?: number; author?: string }
52
+
53
+ const synopsisScript = findSynopsisScript(projectRoot)
54
+ if (synopsisScript) {
55
+ try {
56
+ const args = [String(hours ?? 24), author].filter(Boolean).join(" ")
57
+ const output = execSync(`node "${synopsisScript}" synopsis ${args}`, {
58
+ cwd: projectRoot,
59
+ timeout: 30000,
60
+ encoding: "utf-8",
61
+ })
62
+ return output.trim()
63
+ } catch {}
64
+ }
65
+
66
+ try {
67
+ const args = ["synopsis", String(hours ?? 24), author && `--author "${author}"`]
68
+ .filter(Boolean)
69
+ .join(" ")
70
+ const output = execSync(`jfl synopsis ${args}`, {
71
+ cwd: projectRoot,
72
+ timeout: 30000,
73
+ encoding: "utf-8",
74
+ })
75
+ return output.trim()
76
+ } catch (err) {
77
+ return `Synopsis unavailable: ${err}`
78
+ }
79
+ },
80
+ renderCall: synopsisRenderCall,
81
+ renderResult: synopsisRenderResult,
82
+ })
83
+ }