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
@@ -16,6 +16,8 @@ import { PeterParkerBridge } from "../lib/peter-parker-bridge.js";
16
16
  import { getProjectHubUrl } from "../utils/context-hub-port.js";
17
17
  import { TrajectoryLoader } from "../lib/trajectory-loader.js";
18
18
  import { readEvals } from "../lib/eval-store.js";
19
+ import { TrainingBuffer } from "../lib/training-buffer.js";
20
+ import { PolicyHeadInference } from "../lib/policy-head.js";
19
21
  function hasRalphTui() {
20
22
  try {
21
23
  execSync("which ralph-tui", { stdio: "ignore" });
@@ -374,6 +376,51 @@ async function runWithPR(projectRoot, task) {
374
376
  gitExec(["checkout", baseBranch], projectRoot);
375
377
  console.log(chalk.green(" PP: Done\n"));
376
378
  }
379
+ function buildRLState(evals, trajectoryLength, recentDeltas) {
380
+ const latest = evals.sort((a, b) => b.ts.localeCompare(a.ts))[0];
381
+ return {
382
+ composite_score: latest?.composite ?? 0,
383
+ dimension_scores: latest?.metrics ?? {},
384
+ tests_passing: latest?.metrics?.tests_passed ?? 0,
385
+ tests_total: latest?.metrics?.tests_total ?? 1,
386
+ trajectory_length: trajectoryLength,
387
+ recent_deltas: recentDeltas,
388
+ agent: "peter-parker",
389
+ };
390
+ }
391
+ function proposalToRLAction(p) {
392
+ return {
393
+ type: "experiment",
394
+ description: p.task,
395
+ files_affected: [],
396
+ scope: "medium",
397
+ branch: "",
398
+ };
399
+ }
400
+ async function rerankWithPolicyHead(projectRoot, proposals, evals, recentDeltas) {
401
+ const ph = new PolicyHeadInference(projectRoot);
402
+ if (!ph.isLoaded || proposals.length < 2)
403
+ return proposals;
404
+ const state = buildRLState(evals, 0, recentDeltas);
405
+ const actions = proposals.map(proposalToRLAction);
406
+ try {
407
+ const ranked = await ph.rankActions(state, actions);
408
+ const reordered = ranked.map(r => proposals[actions.indexOf(r.action)]);
409
+ const stats = ph.stats;
410
+ console.log(chalk.magenta(` Policy head re-ranked ${proposals.length} proposals (trained on ${stats.trained_on} tuples, rank_corr=${stats.rank_correlation.toFixed(3)})`));
411
+ for (let i = 0; i < ranked.length; i++) {
412
+ const r = ranked[i];
413
+ const p = reordered[i];
414
+ console.log(chalk.gray(` #${i + 1} [pred=${r.predictedReward.toFixed(4)}] ${p.task.slice(0, 60)}`));
415
+ }
416
+ console.log();
417
+ return reordered;
418
+ }
419
+ catch (err) {
420
+ console.log(chalk.yellow(` Policy head ranking failed: ${err.message}`));
421
+ return proposals;
422
+ }
423
+ }
377
424
  function writeJournalEntry(projectRoot, entry) {
378
425
  const journalDir = path.join(projectRoot, ".jfl", "journal");
379
426
  if (!fs.existsSync(journalDir)) {
@@ -484,9 +531,14 @@ Respond with ONLY a JSON array, no other text.`;
484
531
  const content = response.choices[0]?.message?.content || "";
485
532
  const jsonMatch = content.match(/\[[\s\S]*\]/);
486
533
  if (jsonMatch) {
487
- const proposals = JSON.parse(jsonMatch[0]);
534
+ let proposals = JSON.parse(jsonMatch[0]);
488
535
  if (proposals.length > 0) {
489
536
  proposals.sort((a, b) => b.predicted_delta - a.predicted_delta);
537
+ const recentDeltas = experimentEntries
538
+ .slice(0, 5)
539
+ .map(e => e.score_delta ?? 0)
540
+ .filter((d) => typeof d === "number");
541
+ proposals = await rerankWithPolicyHead(projectRoot, proposals, evals, recentDeltas);
490
542
  console.log(chalk.bold(" Top 3 Proposals:\n"));
491
543
  for (let i = 0; i < Math.min(3, proposals.length); i++) {
492
544
  const p = proposals[i];
@@ -560,8 +612,1119 @@ Respond with ONLY a JSON array, no other text.`;
560
612
  console.log(chalk.cyan(" Dispatching to Peter Parker PR workflow...\n"));
561
613
  await runWithPR(projectRoot, proposal.task);
562
614
  }
615
+ async function runAutoresearch(projectRoot, rounds) {
616
+ console.log(chalk.bold(`\n Peter Parker - Autoresearch Mode (${rounds} rounds)\n`));
617
+ console.log(chalk.gray(" Pattern: branch → change → eval → keep|revert → repeat"));
618
+ console.log(chalk.gray(" Only the winning experiment gets a PR.\n"));
619
+ if (!hasRalphTui()) {
620
+ console.log(chalk.yellow(" ralph-tui is not installed"));
621
+ console.log(chalk.gray(" Install: bun install -g ralph-tui\n"));
622
+ return;
623
+ }
624
+ const configPath = path.join(projectRoot, ".ralph-tui", "config.toml");
625
+ if (!fs.existsSync(configPath)) {
626
+ console.log(chalk.yellow(" No Peter Parker config found"));
627
+ console.log(chalk.gray(" Run: jfl peter setup\n"));
628
+ return;
629
+ }
630
+ const loader = new TrajectoryLoader(projectRoot);
631
+ const evals = readEvals(projectRoot);
632
+ if (evals.length === 0) {
633
+ console.log(chalk.yellow(" No eval history. Run `jfl peter pr` first.\n"));
634
+ return;
635
+ }
636
+ const baseBranch = "main";
637
+ const latestEval = evals.sort((a, b) => b.ts.localeCompare(a.ts))[0];
638
+ const baselinePassRate = latestEval?.composite ?? 0;
639
+ const baselineTotal = latestEval?.metrics?.tests_total ?? 0;
640
+ const baselineScore = baselinePassRate + (baselineTotal > 0 ? baselineTotal * 0.001 : 0);
641
+ console.log(chalk.gray(` Baseline composite: ${baselineScore.toFixed(4)} (${baselineTotal} tests)`));
642
+ const results = [];
643
+ let bestResult = null;
644
+ for (let round = 1; round <= rounds; round++) {
645
+ console.log(chalk.bold(`\n ── Round ${round}/${rounds} ${"─".repeat(40)}\n`));
646
+ const experimentEntries = loader.load({ type: "experiment", maxAge: "30d", limit: 20 });
647
+ const pastTitles = [
648
+ ...experimentEntries.map(e => e.title),
649
+ ...results.map(r => r.task),
650
+ ];
651
+ let proposal = null;
652
+ const stratusKey = process.env.STRATUS_API_KEY;
653
+ if (stratusKey) {
654
+ try {
655
+ const { StratusClient } = await import("../lib/stratus-client.js");
656
+ const stratus = new StratusClient({
657
+ baseUrl: process.env.STRATUS_API_URL || "https://api.stratus.run",
658
+ apiKey: stratusKey,
659
+ model: "stratus-x1ac-base-claude-sonnet-4-6",
660
+ timeout: 60000,
661
+ });
662
+ const evalSummary = buildEvalSummary(evals.concat(results.map(r => ({
663
+ v: 1, ts: new Date().toISOString(), agent: "autoresearch",
664
+ run_id: `autoresearch-r${r.round}`,
665
+ metrics: { composite: r.score }, composite: r.score,
666
+ model_version: `round-${r.round}`,
667
+ }))), 15);
668
+ const policyHead = new PolicyHeadInference(projectRoot);
669
+ const useMultiProposal = policyHead.isLoaded;
670
+ const prompt = useMultiProposal
671
+ ? `Autoresearch round ${round}/${rounds}. Suggest 3 specific improvements ranked by expected impact.
672
+
673
+ Eval history:
674
+ ${evalSummary}
675
+
676
+ Already tried (avoid these):
677
+ ${pastTitles.map(t => `- ${t}`).join("\n") || "Nothing yet"}
678
+
679
+ Previous rounds this session:
680
+ ${results.map(r => `- Round ${r.round}: "${r.task}" → delta=${r.delta > 0 ? "+" : ""}${r.delta.toFixed(4)}`).join("\n") || "None yet"}
681
+
682
+ Respond with ONLY a JSON array of 3 objects: [{"task": "...", "predicted_delta": 0.0-1.0, "reasoning": "...", "risk": "..."}, ...]`
683
+ : `Autoresearch round ${round}/${rounds}. Suggest ONE specific improvement.
684
+
685
+ Eval history:
686
+ ${evalSummary}
687
+
688
+ Already tried (avoid these):
689
+ ${pastTitles.map(t => `- ${t}`).join("\n") || "Nothing yet"}
690
+
691
+ Previous rounds this session:
692
+ ${results.map(r => `- Round ${r.round}: "${r.task}" → delta=${r.delta > 0 ? "+" : ""}${r.delta.toFixed(4)}`).join("\n") || "None yet"}
693
+
694
+ Suggest the SINGLE highest-value change. JSON format:
695
+ {"task": "...", "predicted_delta": 0.0-1.0, "reasoning": "...", "risk": "..."}`;
696
+ const response = await stratus.reason(prompt, {
697
+ temperature: 0.8 + (round * 0.05),
698
+ maxTokens: useMultiProposal ? 1500 : 500,
699
+ featureContext: "autoresearch",
700
+ });
701
+ const content = response.choices[0]?.message?.content || "";
702
+ if (useMultiProposal) {
703
+ const jsonMatch = content.match(/\[[\s\S]*\]/);
704
+ if (jsonMatch) {
705
+ let proposals = JSON.parse(jsonMatch[0]);
706
+ const recentDeltas = results.slice(-5).map(r => r.delta);
707
+ proposals = await rerankWithPolicyHead(projectRoot, proposals, evals, recentDeltas);
708
+ if (proposals.length > 0)
709
+ proposal = proposals[0];
710
+ }
711
+ }
712
+ if (!proposal) {
713
+ const jsonMatch = content.match(/\{[\s\S]*\}/);
714
+ if (jsonMatch) {
715
+ proposal = JSON.parse(jsonMatch[0]);
716
+ }
717
+ }
718
+ }
719
+ catch (err) {
720
+ console.log(chalk.yellow(` Stratus failed: ${err.message}`));
721
+ }
722
+ }
723
+ if (!proposal) {
724
+ const regressed = findRegressedDimensions(evals);
725
+ const lowest = findLowestDimension(evals);
726
+ if (regressed.length > 0) {
727
+ const worst = regressed[0];
728
+ proposal = {
729
+ task: `Improve ${worst.dimension} score (regressed by ${worst.delta.toFixed(4)})`,
730
+ predicted_delta: Math.abs(worst.delta),
731
+ reasoning: `Recovering ${worst.dimension} regression`,
732
+ risk: "May not fully recover",
733
+ };
734
+ }
735
+ else if (lowest) {
736
+ proposal = {
737
+ task: `Improve ${lowest.dimension} score (currently ${lowest.score.toFixed(4)})`,
738
+ predicted_delta: Math.max(0.01, (1 - lowest.score) * 0.1),
739
+ reasoning: `${lowest.dimension} is weakest dimension`,
740
+ risk: "May trade off against others",
741
+ };
742
+ }
743
+ else {
744
+ console.log(chalk.yellow(" No target found, skipping round"));
745
+ continue;
746
+ }
747
+ }
748
+ console.log(chalk.cyan(` Task: ${proposal.task}`));
749
+ console.log(chalk.gray(` Predicted: +${proposal.predicted_delta.toFixed(4)}\n`));
750
+ const branchName = `pp/autoresearch-r${round}-${Date.now()}`;
751
+ gitExec(["stash", "--include-untracked"], projectRoot);
752
+ gitExec(["fetch", "origin", baseBranch], projectRoot);
753
+ let checkout = gitExec(["checkout", "-b", branchName, `origin/${baseBranch}`], projectRoot);
754
+ if (!checkout.ok) {
755
+ checkout = gitExec(["checkout", "-b", branchName, baseBranch], projectRoot);
756
+ }
757
+ if (!checkout.ok) {
758
+ console.log(chalk.red(` Failed to create branch ${branchName}`));
759
+ continue;
760
+ }
761
+ await new Promise((resolve) => {
762
+ const ralphDir = path.join(projectRoot, ".ralph-tui");
763
+ if (!fs.existsSync(ralphDir))
764
+ fs.mkdirSync(ralphDir, { recursive: true });
765
+ const prdPath = path.join(ralphDir, "autoresearch-task.json");
766
+ const prd = {
767
+ name: "Autoresearch Task",
768
+ branchName: `ralph/autoresearch-${Date.now()}`,
769
+ description: proposal.task,
770
+ userStories: [{
771
+ id: "US-001",
772
+ title: proposal.task.slice(0, 80),
773
+ description: proposal.task,
774
+ acceptanceCriteria: ["Task completed"],
775
+ priority: 1, passes: false, notes: "", dependsOn: [],
776
+ }],
777
+ metadata: { updatedAt: new Date().toISOString() },
778
+ };
779
+ fs.writeFileSync(prdPath, JSON.stringify(prd, null, 2));
780
+ const env = { ...process.env };
781
+ delete env.CLAUDECODE;
782
+ delete env.CLAUDE_CODE;
783
+ const child = spawn("claude", [
784
+ "--dangerously-skip-permissions",
785
+ "-p", proposal.task,
786
+ "--output-format", "text",
787
+ ], {
788
+ cwd: projectRoot, stdio: "inherit", env,
789
+ });
790
+ child.on("error", () => { try {
791
+ fs.unlinkSync(prdPath);
792
+ }
793
+ catch { } resolve(); });
794
+ child.on("exit", () => { try {
795
+ fs.unlinkSync(prdPath);
796
+ }
797
+ catch { } resolve(); });
798
+ });
799
+ const diffCheck = gitExec(["diff", "--quiet", "HEAD"], projectRoot);
800
+ const untrackedResult = spawnSync("git", ["ls-files", "--others", "--exclude-standard"], {
801
+ cwd: projectRoot, encoding: "utf-8", stdio: "pipe",
802
+ });
803
+ const hasChanges = !diffCheck.ok || (untrackedResult.stdout || "").trim().length > 0;
804
+ if (!hasChanges) {
805
+ console.log(chalk.yellow(` Round ${round}: No changes, skipping eval`));
806
+ gitExec(["checkout", baseBranch], projectRoot);
807
+ gitExec(["branch", "-D", branchName], projectRoot);
808
+ continue;
809
+ }
810
+ gitExec(["add", "-A"], projectRoot);
811
+ gitExec(["commit", "-m", `autoresearch: round ${round} - ${proposal.task}`], projectRoot);
812
+ console.log(chalk.gray(" Running local eval..."));
813
+ const testResult = spawnSync("npx", ["jest", "--json", "--silent"], {
814
+ cwd: projectRoot, encoding: "utf-8", stdio: "pipe", timeout: 120000,
815
+ });
816
+ let passing = 0, total = 1;
817
+ try {
818
+ const json = JSON.parse(testResult.stdout || "{}");
819
+ passing = json.numPassedTests || 0;
820
+ total = json.numTotalTests || 1;
821
+ }
822
+ catch { }
823
+ const passRate = total > 0 ? passing / total : 0;
824
+ const testsAdded = total - baselineTotal;
825
+ const score = passRate + (testsAdded > 0 ? testsAdded * 0.001 : 0);
826
+ const delta = score - baselineScore;
827
+ const result = {
828
+ round,
829
+ task: proposal.task,
830
+ score,
831
+ delta,
832
+ testsPassing: passing,
833
+ testsTotal: total,
834
+ branch: branchName,
835
+ };
836
+ results.push(result);
837
+ const emoji = delta > 0 ? "+" : delta < 0 ? "" : "=";
838
+ console.log(chalk.bold(` Round ${round} result: ${score.toFixed(4)} (${emoji}${delta.toFixed(4)})`));
839
+ console.log(chalk.gray(` Tests: ${passing}/${total}${testsAdded > 0 ? chalk.green(` (+${testsAdded} new)`) : ""}`));
840
+ if (!bestResult || result.delta > bestResult.delta) {
841
+ bestResult = result;
842
+ console.log(chalk.green(` New best! (round ${round})`));
843
+ }
844
+ writeJournalEntry(projectRoot, {
845
+ v: 1,
846
+ ts: new Date().toISOString(),
847
+ session: "autoresearch",
848
+ type: "experiment",
849
+ status: (delta > 0 || testsAdded > 0) ? "complete" : "incomplete",
850
+ title: `Autoresearch R${round}: ${proposal.task.slice(0, 60)}`,
851
+ summary: `Score: ${score.toFixed(4)}, delta: ${delta > 0 ? "+" : ""}${delta.toFixed(4)}`,
852
+ detail: `Task: ${proposal.task}\nResult: ${passing}/${total} tests passing`,
853
+ hypothesis: `"${proposal.task}" improves composite by ~${proposal.predicted_delta.toFixed(4)}`,
854
+ outcome: delta > 0 ? "confirmed" : delta < 0 ? "rejected" : "inconclusive",
855
+ score_delta: delta,
856
+ agent_id: "peter-parker",
857
+ });
858
+ const tb = new TrainingBuffer(projectRoot);
859
+ tb.append({
860
+ agent: "peter-parker",
861
+ state: {
862
+ composite_score: baselineScore,
863
+ dimension_scores: evals.sort((a, b) => b.ts.localeCompare(a.ts))[0]?.metrics ?? {},
864
+ tests_passing: evals.sort((a, b) => b.ts.localeCompare(a.ts))[0]?.metrics?.tests_passed ?? 0,
865
+ tests_total: evals.sort((a, b) => b.ts.localeCompare(a.ts))[0]?.metrics?.tests_total ?? 1,
866
+ trajectory_length: results.length,
867
+ recent_deltas: results.slice(-5).map(r => r.delta),
868
+ agent: "peter-parker",
869
+ },
870
+ action: {
871
+ type: "experiment",
872
+ description: proposal.task,
873
+ files_affected: [],
874
+ scope: "medium",
875
+ branch: branchName,
876
+ },
877
+ reward: {
878
+ composite_delta: delta,
879
+ dimension_deltas: {},
880
+ tests_added: testsAdded,
881
+ quality_score: passRate,
882
+ improved: delta > 0 || testsAdded > 0,
883
+ prediction_error: Math.abs(proposal.predicted_delta - delta),
884
+ },
885
+ metadata: {
886
+ branch: branchName,
887
+ autoresearch_round: round,
888
+ source: "autoresearch",
889
+ },
890
+ });
891
+ gitExec(["checkout", baseBranch], projectRoot);
892
+ }
893
+ console.log(chalk.bold(`\n ── Autoresearch Complete ${"─".repeat(35)}\n`));
894
+ console.log(chalk.gray(` Rounds: ${rounds}`));
895
+ console.log(chalk.gray(` Results:`));
896
+ for (const r of results) {
897
+ const emoji = r.delta > 0 ? chalk.green("+") : r.delta < 0 ? chalk.red("-") : chalk.gray("=");
898
+ const best = bestResult && r.round === bestResult.round ? chalk.yellow(" ★") : "";
899
+ console.log(chalk.gray(` R${r.round}: ${r.score.toFixed(4)} (${emoji}${Math.abs(r.delta).toFixed(4)}) ${r.task.slice(0, 50)}${best}`));
900
+ }
901
+ if (bestResult && bestResult.delta > 0) {
902
+ console.log(chalk.green(`\n Winner: Round ${bestResult.round} (${bestResult.delta > 0 ? "+" : ""}${bestResult.delta.toFixed(4)})`));
903
+ console.log(chalk.cyan(" Creating PR for winning experiment...\n"));
904
+ gitExec(["checkout", bestResult.branch], projectRoot);
905
+ const pushResult = gitExec(["push", "-u", "origin", bestResult.branch], projectRoot);
906
+ if (pushResult.ok) {
907
+ const prTitle = `PP autoresearch: ${bestResult.task.slice(0, 50)} (+${bestResult.delta.toFixed(4)})`;
908
+ const prBody = [
909
+ "## Autoresearch Winner",
910
+ "",
911
+ `**Task:** ${bestResult.task}`,
912
+ `**Score:** ${bestResult.score.toFixed(4)} (delta: +${bestResult.delta.toFixed(4)})`,
913
+ `**Tests:** ${bestResult.testsPassing}/${bestResult.testsTotal}`,
914
+ `**Round:** ${bestResult.round}/${rounds}`,
915
+ "",
916
+ "### All Rounds",
917
+ ...results.map(r => `- R${r.round}: ${r.score.toFixed(4)} (${r.delta > 0 ? "+" : ""}${r.delta.toFixed(4)}) ${r.task.slice(0, 60)}${r.round === bestResult.round ? " **← winner**" : ""}`),
918
+ "",
919
+ "---",
920
+ "*Generated by JFL autoresearch loop*",
921
+ ].join("\n");
922
+ let prResult = ghExec([
923
+ "pr", "create", "--title", prTitle, "--body", prBody,
924
+ "--base", baseBranch, "--head", bestResult.branch, "--label", "pp-generated",
925
+ ], projectRoot);
926
+ if (!prResult.ok) {
927
+ prResult = ghExec([
928
+ "pr", "create", "--title", prTitle, "--body", prBody,
929
+ "--base", baseBranch, "--head", bestResult.branch,
930
+ ], projectRoot);
931
+ }
932
+ if (prResult.ok) {
933
+ console.log(chalk.green(` PR created: ${prResult.output}`));
934
+ await postHubEvent(projectRoot, "autoresearch:complete", {
935
+ rounds,
936
+ winner_round: bestResult.round,
937
+ winner_task: bestResult.task,
938
+ winner_delta: bestResult.delta,
939
+ pr_url: prResult.output,
940
+ all_results: results.map(r => ({
941
+ round: r.round, task: r.task, score: r.score, delta: r.delta,
942
+ })),
943
+ });
944
+ }
945
+ else {
946
+ console.log(chalk.red(` Failed to create PR: ${prResult.output}`));
947
+ }
948
+ }
949
+ else {
950
+ console.log(chalk.red(` Failed to push: ${pushResult.output}`));
951
+ }
952
+ gitExec(["checkout", baseBranch], projectRoot);
953
+ }
954
+ else {
955
+ console.log(chalk.yellow("\n No improvement found across all rounds."));
956
+ console.log(chalk.gray(" All experiment branches preserved for review.\n"));
957
+ }
958
+ for (const r of results) {
959
+ if (!bestResult || r.round !== bestResult.round) {
960
+ gitExec(["branch", "-D", r.branch], projectRoot);
961
+ }
962
+ }
963
+ gitExec(["stash", "pop"], projectRoot);
964
+ console.log();
965
+ }
966
+ // ============================================================================
967
+ // Scoped Agent Commands
968
+ // ============================================================================
969
+ async function agentCreate(projectRoot) {
970
+ const { writeAgentConfig, generateAgentToml } = await import("../lib/agent-config.js");
971
+ const readline = await import("readline");
972
+ const rl = readline.createInterface({
973
+ input: process.stdin,
974
+ output: process.stdout,
975
+ });
976
+ const ask = (q) => new Promise(resolve => rl.question(q, resolve));
977
+ console.log(chalk.bold("\n Create Scoped Agent\n"));
978
+ const name = await ask(" Agent name (e.g., search-quality): ");
979
+ const scope = await ask(" Scope (e.g., search, tests, quality): ");
980
+ const metric = await ask(" Metric (e.g., ndcg@10, test_pass_rate): ");
981
+ const direction = await ask(" Direction (maximize/minimize) [maximize]: ") || "maximize";
982
+ const timeBudget = await ask(" Time budget seconds [300]: ") || "300";
983
+ const evalScript = await ask(" Eval script path [eval/eval.ts]: ") || "eval/eval.ts";
984
+ const evalData = await ask(" Eval data path [eval/fixtures/data.jsonl]: ") || "eval/fixtures/data.jsonl";
985
+ const filesInScope = await ask(" Files in scope (glob, comma-sep) [src/**]: ") || "src/**";
986
+ rl.close();
987
+ const configPath = writeAgentConfig(projectRoot, {
988
+ name,
989
+ scope,
990
+ metric,
991
+ direction: direction,
992
+ time_budget_seconds: parseInt(timeBudget, 10),
993
+ eval: {
994
+ script: evalScript,
995
+ data: evalData,
996
+ },
997
+ constraints: {
998
+ files_in_scope: filesInScope.split(",").map(s => s.trim()),
999
+ files_readonly: ["eval/**"],
1000
+ max_file_changes: 10,
1001
+ },
1002
+ policy: {
1003
+ embedding_model: "stratus-x1ac-base-claude-sonnet-4-6",
1004
+ exploration_rate: 0.2,
1005
+ decay_per_round: 0.01,
1006
+ min_exploration: 0.05,
1007
+ },
1008
+ });
1009
+ console.log(chalk.green(`\n Agent config created: ${configPath}\n`));
1010
+ }
1011
+ async function agentList(projectRoot) {
1012
+ const { listAgentConfigs, loadAgentConfig } = await import("../lib/agent-config.js");
1013
+ const agents = listAgentConfigs(projectRoot);
1014
+ if (agents.length === 0) {
1015
+ console.log(chalk.yellow("\n No agents configured."));
1016
+ console.log(chalk.gray(" Run: jfl peter agent create\n"));
1017
+ return;
1018
+ }
1019
+ console.log(chalk.bold("\n Configured Agents\n"));
1020
+ for (const name of agents) {
1021
+ try {
1022
+ const config = loadAgentConfig(projectRoot, name);
1023
+ console.log(chalk.cyan(` ${name}`));
1024
+ console.log(chalk.gray(` Scope: ${config.scope}`));
1025
+ console.log(chalk.gray(` Metric: ${config.metric} (${config.direction})`));
1026
+ console.log(chalk.gray(` Time budget: ${config.time_budget_seconds}s`));
1027
+ console.log(chalk.gray(` Files: ${config.constraints.files_in_scope.join(", ")}`));
1028
+ console.log();
1029
+ }
1030
+ catch (err) {
1031
+ console.log(chalk.red(` ${name}: Error loading config - ${err.message}`));
1032
+ }
1033
+ }
1034
+ }
1035
+ async function agentRun(projectRoot, agentName, roundsOverride) {
1036
+ const { loadAgentConfig, validateAgentConfig } = await import("../lib/agent-config.js");
1037
+ const { startSession, runBaseline, runRound, endSession, saveSessionState, writeExperimentHistory } = await import("../lib/agent-session.js");
1038
+ const { ReplayBuffer } = await import("../lib/replay-buffer.js");
1039
+ const { StratusClient } = await import("../lib/stratus-client.js");
1040
+ // Load and validate config
1041
+ let config;
1042
+ try {
1043
+ config = loadAgentConfig(projectRoot, agentName);
1044
+ }
1045
+ catch (err) {
1046
+ console.log(chalk.red(`\n Agent not found: ${agentName}`));
1047
+ console.log(chalk.gray(" Run: jfl peter agent list\n"));
1048
+ return;
1049
+ }
1050
+ const validation = validateAgentConfig(config, projectRoot);
1051
+ if (!validation.valid) {
1052
+ console.log(chalk.red(`\n Invalid agent config:`));
1053
+ for (const err of validation.errors) {
1054
+ console.log(chalk.red(` - ${err}`));
1055
+ }
1056
+ console.log();
1057
+ return;
1058
+ }
1059
+ // Use config.rounds as default (Karpathy: ~50 experiments per session)
1060
+ // Allow override via CLI for debugging/testing
1061
+ const rounds = roundsOverride ?? config.rounds ?? 50;
1062
+ // Get scope_files for display
1063
+ const displayScopeFiles = config.constraints.scope_files || config.constraints.files_in_scope;
1064
+ const scopeFilesDisplay = displayScopeFiles.slice(0, 3).join(", ") + (displayScopeFiles.length > 3 ? "..." : "");
1065
+ console.log(chalk.bold(`\n Running Scoped Agent: ${agentName} (${rounds} rounds)\n`));
1066
+ console.log(chalk.gray(` Metric: ${config.metric} (${config.direction})`));
1067
+ console.log(chalk.gray(` Time budget: ${config.time_budget_seconds}s per round`));
1068
+ console.log(chalk.gray(` Focus files: ${scopeFilesDisplay}`));
1069
+ console.log(chalk.gray(` Pattern: branch → change → eval → keep|revert → repeat\n`));
1070
+ // Start session
1071
+ const session = startSession(config, projectRoot);
1072
+ saveSessionState(session);
1073
+ console.log(chalk.cyan(` Session: ${session.id}`));
1074
+ console.log(chalk.cyan(` Branch: ${session.branch}`));
1075
+ console.log(chalk.gray(` Eval snapshot: ${session.evalSnapshot.hash.slice(0, 8)}\n`));
1076
+ // Run baseline
1077
+ console.log(chalk.gray(" Running baseline eval..."));
1078
+ let baseline;
1079
+ try {
1080
+ baseline = await runBaseline(session);
1081
+ }
1082
+ catch (err) {
1083
+ console.log(chalk.red(` Baseline eval failed: ${err.message}`));
1084
+ return;
1085
+ }
1086
+ console.log(chalk.green(` Baseline: ${baseline.toFixed(4)}\n`));
1087
+ const replayBuffer = new ReplayBuffer(projectRoot);
1088
+ const transitions = [];
1089
+ // Stratus for task generation
1090
+ const stratus = process.env.STRATUS_API_KEY
1091
+ ? new StratusClient({ apiKey: process.env.STRATUS_API_KEY })
1092
+ : null;
1093
+ // Get scope_files from config (Karpathy pattern: focused file list)
1094
+ const scopeFiles = config.constraints.scope_files || config.constraints.files_in_scope || [];
1095
+ const scopeFilesStr = scopeFiles.slice(0, 5).join(", ");
1096
+ for (let round = 1; round <= rounds; round++) {
1097
+ console.log(chalk.bold(`\n ── Round ${round}/${rounds} ${"─".repeat(40)}\n`));
1098
+ // Generate task — informed by past experiment history
1099
+ // Default task uses scope_files for focused changes (Karpathy pattern)
1100
+ let task = `Improve ${config.metric} by modifying: ${scopeFilesStr}. Make ONE small, focused change.`;
1101
+ // Build history context from replay buffer + current session transitions
1102
+ const pastTuples = replayBuffer.getForAgent(config.name).slice(-20); // Last 20 tuples
1103
+ const keptExperiments = [];
1104
+ const rejectedExperiments = [];
1105
+ // From replay buffer (past sessions)
1106
+ for (const t of pastTuples) {
1107
+ const action = t.action?.description || "unknown";
1108
+ const reward = t.reward ?? 0;
1109
+ if (reward > 0) {
1110
+ keptExperiments.push(`"${action.slice(0, 50)}" (+${reward.toFixed(4)})`);
1111
+ }
1112
+ else {
1113
+ rejectedExperiments.push(`"${action.slice(0, 50)}"`);
1114
+ }
1115
+ }
1116
+ // From current session transitions
1117
+ for (const t of transitions) {
1118
+ const action = t.action?.description || "unknown";
1119
+ const reward = t.reward ?? 0;
1120
+ if (reward > 0) {
1121
+ keptExperiments.push(`"${action.slice(0, 50)}" (+${reward.toFixed(4)})`);
1122
+ }
1123
+ else {
1124
+ rejectedExperiments.push(`"${action.slice(0, 50)}"`);
1125
+ }
1126
+ }
1127
+ let historyContext = "";
1128
+ if (keptExperiments.length > 0 || rejectedExperiments.length > 0) {
1129
+ historyContext = `
1130
+
1131
+ Experiment history for ${config.name}:
1132
+ KEPT (these worked — build on them): ${keptExperiments.slice(-5).join(", ") || "none yet"}
1133
+ REJECTED (do NOT repeat these): ${rejectedExperiments.slice(-5).join(", ") || "none yet"}
1134
+
1135
+ CRITICAL: Do NOT repeat rejected experiments. Try something NEW and DIFFERENT.`;
1136
+ }
1137
+ // Read product context (Layer 2) if available
1138
+ let productContext = "";
1139
+ const productContextPath = path.join(projectRoot, ".jfl", "product-context.md");
1140
+ if (fs.existsSync(productContextPath)) {
1141
+ const pc = fs.readFileSync(productContextPath, "utf-8");
1142
+ // Extract agent-specific guidance if present
1143
+ const agentSection = pc.match(new RegExp(`${config.name}[:\\s].*?(?=\\n[A-Z#]|$)`, "s"));
1144
+ productContext = agentSection
1145
+ ? `\n\nProduct context for this agent:\n${agentSection[0].slice(0, 500)}`
1146
+ : `\n\nProduct context (summary):\n${pc.slice(0, 500)}`;
1147
+ }
1148
+ if (stratus) {
1149
+ try {
1150
+ // Enhanced prompt with scope_files and history
1151
+ const prompt = `Suggest ONE specific code change to improve the ${config.metric} metric (${config.direction}).
1152
+
1153
+ FOCUS FILES (modify only these): ${scopeFilesStr}
1154
+ CURRENT METRIC: ${session.baselineMetric.toFixed(4)}
1155
+ ROUND: ${round}/${rounds}
1156
+ ${historyContext}${productContext}
1157
+
1158
+ Be concrete and actionable. Return just the task description (1-2 sentences).`;
1159
+ const response = await stratus.reason(prompt, { maxTokens: 200, temperature: 0.7 + round * 0.03 });
1160
+ task = response.choices[0]?.message?.content || task;
1161
+ }
1162
+ catch { }
1163
+ }
1164
+ const hypothesis = `Implementing this will improve ${config.metric}`;
1165
+ console.log(chalk.cyan(` Task: ${task.slice(0, 80)}...`));
1166
+ // Pass transitions to runRound so it can build experiment history (Karpathy pattern)
1167
+ const { result, transition } = await runRound(session, round, task, hypothesis, transitions);
1168
+ transitions.push(transition);
1169
+ // Write to replay buffer
1170
+ replayBuffer.write({
1171
+ agent: config.name,
1172
+ session_id: session.id,
1173
+ state_hash: transition.state_hash,
1174
+ state: transition.state,
1175
+ action_diff: transition.action_diff,
1176
+ action: transition.action,
1177
+ hypothesis,
1178
+ reward: result.delta,
1179
+ timestamp: new Date().toISOString(),
1180
+ });
1181
+ const emoji = result.kept ? chalk.green("✓") : chalk.red("✗");
1182
+ const deltaStr = result.delta > 0 ? `+${result.delta.toFixed(4)}` : result.delta.toFixed(4);
1183
+ console.log(` ${emoji} Result: ${result.metricAfter.toFixed(4)} (${deltaStr}) ${result.kept ? "KEPT" : "REVERTED"}`);
1184
+ }
1185
+ // End session
1186
+ const summary = await endSession(session, transitions);
1187
+ console.log(chalk.bold(`\n ── Session Complete ${"─".repeat(35)}\n`));
1188
+ console.log(chalk.gray(` Rounds: ${summary.rounds}`));
1189
+ console.log(chalk.gray(` Improved: ${summary.improvedRounds}`));
1190
+ console.log(chalk.gray(` Total delta: ${summary.totalDelta > 0 ? "+" : ""}${summary.totalDelta.toFixed(4)}`));
1191
+ console.log(chalk.gray(` Best delta: +${summary.bestDelta.toFixed(4)}`));
1192
+ if (summary.branchUrl) {
1193
+ console.log(chalk.green(`\n Branch pushed for review: ${summary.branchUrl}`));
1194
+ }
1195
+ if (summary.prUrl) {
1196
+ console.log(chalk.green(` PR: ${summary.prUrl}`));
1197
+ }
1198
+ console.log();
1199
+ }
1200
+ // ============================================================================
1201
+ // Telemetry Agent V2 Commands
1202
+ // ============================================================================
1203
+ async function runTelemetryAgent(projectRoot) {
1204
+ const { TelemetryAgentV2 } = await import("../lib/telemetry-agent-v2.js");
1205
+ console.log(chalk.bold("\n Telemetry Agent V2 - Platform Digest Analysis\n"));
1206
+ const agent = new TelemetryAgentV2({
1207
+ projectRoot,
1208
+ emitEvent: (type, data) => {
1209
+ console.log(chalk.gray(` [EVENT] ${type}`));
1210
+ },
1211
+ });
1212
+ console.log(chalk.gray(" Fetching platform digest..."));
1213
+ const result = await agent.run();
1214
+ if (!result.digest24h) {
1215
+ console.log(chalk.yellow("\n Could not fetch platform digest."));
1216
+ console.log(chalk.gray(" Check JFL_PLATFORM_URL and network connectivity.\n"));
1217
+ return;
1218
+ }
1219
+ console.log(chalk.green(`\n Digest fetched for ${result.digest24h.periodHours}h period\n`));
1220
+ // Show metrics
1221
+ if (result.metrics) {
1222
+ console.log(chalk.bold(" Current Metrics:"));
1223
+ console.log(chalk.gray(` Command Success Rate: ${(result.metrics.command_success_rate * 100).toFixed(1)}%`));
1224
+ console.log(chalk.gray(` Command P90 Latency: ${result.metrics.command_p90_latency_ms}ms`));
1225
+ console.log(chalk.gray(` Session Crash Rate: ${(result.metrics.session_crash_rate * 100).toFixed(2)}%`));
1226
+ console.log(chalk.gray(` Hub Crash Rate: ${(result.metrics.hub_crash_rate * 100).toFixed(2)}%`));
1227
+ console.log(chalk.gray(` Flow Completion Rate: ${(result.metrics.flow_completion_rate * 100).toFixed(1)}%`));
1228
+ console.log(chalk.gray(` Cost Per Session: $${result.metrics.cost_per_session_usd.toFixed(4)}`));
1229
+ console.log(chalk.gray(` Active Installs: ${result.metrics.active_installs}`));
1230
+ console.log(chalk.gray(` Error Clusters: ${result.metrics.error_cluster_count}`));
1231
+ console.log();
1232
+ }
1233
+ // Show alerts
1234
+ if (result.alerts.length > 0) {
1235
+ console.log(chalk.bold(chalk.red(" Alerts:")));
1236
+ for (const alert of result.alerts) {
1237
+ console.log(chalk.red(` ⚠ ${alert.name}: ${alert.percentChange.toFixed(1)}% decline`));
1238
+ console.log(chalk.gray(` ${alert.previous.toFixed(4)} → ${alert.current.toFixed(4)}`));
1239
+ }
1240
+ console.log();
1241
+ }
1242
+ // Show wins
1243
+ if (result.wins.length > 0) {
1244
+ console.log(chalk.bold(chalk.green(" Wins:")));
1245
+ for (const win of result.wins) {
1246
+ console.log(chalk.green(` ✓ ${win.name}: ${win.percentChange.toFixed(1)}% improvement`));
1247
+ console.log(chalk.gray(` ${win.previous.toFixed(4)} → ${win.current.toFixed(4)}`));
1248
+ }
1249
+ console.log();
1250
+ }
1251
+ // Show proposed agents
1252
+ if (result.proposedAgents.length > 0) {
1253
+ console.log(chalk.bold(" Proposed Agents:"));
1254
+ for (const agent of result.proposedAgents) {
1255
+ const priority = agent.priority === "high" ? chalk.red(agent.priority) :
1256
+ agent.priority === "medium" ? chalk.yellow(agent.priority) :
1257
+ chalk.gray(agent.priority);
1258
+ console.log(chalk.cyan(` ${agent.name} [${priority}]`));
1259
+ console.log(chalk.gray(` ${agent.reason}`));
1260
+ }
1261
+ console.log(chalk.gray(`\n Agent configs written to .jfl/agents/proposed/\n`));
1262
+ }
1263
+ // Show status
1264
+ const status = agent.getStatus();
1265
+ console.log(chalk.gray(` Run #${status.runCount}`));
1266
+ console.log(chalk.gray(` Training tuples generated: ${status.totalTrainingTuples}`));
1267
+ console.log();
1268
+ }
1269
+ async function runSentinelReview(projectRoot) {
1270
+ const { SentinelRL } = await import("../lib/sentinel-rl.js");
1271
+ console.log(chalk.bold("\n Sentinel - Nightly System Review\n"));
1272
+ const sentinel = new SentinelRL({
1273
+ projectRoot,
1274
+ emitEvent: (type, data) => {
1275
+ console.log(chalk.gray(` [EVENT] ${type}`));
1276
+ },
1277
+ });
1278
+ console.log(chalk.gray(" Collecting inputs..."));
1279
+ const { scores, recommendations, inputs } = await sentinel.run();
1280
+ // Show scores
1281
+ console.log(chalk.bold("\n System Scores:"));
1282
+ console.log(chalk.gray(` Product Health: ${(scores.productHealth * 100).toFixed(0)}% ${scoreEmoji(scores.productHealth)}`));
1283
+ console.log(chalk.gray(` Development Velocity: ${(scores.developmentVelocity * 100).toFixed(0)}% ${scoreEmoji(scores.developmentVelocity)}`));
1284
+ console.log(chalk.gray(` Agent Effectiveness: ${(scores.agentEffectiveness * 100).toFixed(0)}% ${scoreEmoji(scores.agentEffectiveness)}`));
1285
+ console.log(chalk.gray(` Data Quality: ${(scores.dataQuality * 100).toFixed(0)}% ${scoreEmoji(scores.dataQuality)}`));
1286
+ console.log(chalk.bold(` Composite: ${(scores.composite * 100).toFixed(0)}% ${scoreEmoji(scores.composite)}`));
1287
+ console.log();
1288
+ // Show recommendations
1289
+ if (recommendations.length > 0) {
1290
+ console.log(chalk.bold(" Recommendations:"));
1291
+ for (const rec of recommendations) {
1292
+ const priority = rec.priority === "high" ? chalk.red(`[${rec.priority}]`) :
1293
+ rec.priority === "medium" ? chalk.yellow(`[${rec.priority}]`) :
1294
+ chalk.gray(`[${rec.priority}]`);
1295
+ console.log(` ${priority} ${rec.description}`);
1296
+ console.log(chalk.gray(` ${rec.reason}`));
1297
+ }
1298
+ console.log();
1299
+ }
1300
+ else {
1301
+ console.log(chalk.green(" No recommendations at this time.\n"));
1302
+ }
1303
+ // Show data summary
1304
+ console.log(chalk.gray(` Journal entries analyzed: ${inputs.journalEntries.length}`));
1305
+ console.log(chalk.gray(` Eval entries analyzed: ${inputs.evalHistory.length}`));
1306
+ console.log(chalk.gray(` PR reviews analyzed: ${inputs.prReviews.length}`));
1307
+ console.log(chalk.gray(` Training tuples: ${inputs.trainingStats.total}`));
1308
+ console.log(chalk.gray(` Replay entries: ${inputs.replayStats.totalEntries}`));
1309
+ console.log(chalk.gray(`\n Report written to .jfl/SENTINEL-REPORT.md\n`));
1310
+ }
1311
+ function scoreEmoji(score) {
1312
+ if (score >= 0.8)
1313
+ return chalk.green("●");
1314
+ if (score >= 0.5)
1315
+ return chalk.yellow("●");
1316
+ return chalk.red("●");
1317
+ }
1318
+ async function showMetrics(projectRoot) {
1319
+ const { TelemetryAgentV2 } = await import("../lib/telemetry-agent-v2.js");
1320
+ console.log(chalk.bold("\n Current Platform Metrics\n"));
1321
+ const agent = new TelemetryAgentV2({ projectRoot });
1322
+ const metrics = await agent.getMetrics();
1323
+ if (!metrics) {
1324
+ console.log(chalk.yellow(" Could not fetch metrics from platform."));
1325
+ console.log(chalk.gray(" Check JFL_PLATFORM_URL and network connectivity.\n"));
1326
+ return;
1327
+ }
1328
+ const format = (value, isPercent = false) => {
1329
+ if (isPercent)
1330
+ return `${(value * 100).toFixed(1)}%`;
1331
+ return value.toFixed(4);
1332
+ };
1333
+ console.log(chalk.cyan(" Health Metrics:"));
1334
+ console.log(chalk.gray(` Command Success Rate: ${format(metrics.command_success_rate, true)}`));
1335
+ console.log(chalk.gray(` Session Crash Rate: ${format(metrics.session_crash_rate, true)}`));
1336
+ console.log(chalk.gray(` Hub Crash Rate: ${format(metrics.hub_crash_rate, true)}`));
1337
+ console.log(chalk.gray(` Flow Completion Rate: ${format(metrics.flow_completion_rate, true)}`));
1338
+ console.log();
1339
+ console.log(chalk.cyan(" Performance Metrics:"));
1340
+ console.log(chalk.gray(` Command P90 Latency: ${metrics.command_p90_latency_ms}ms`));
1341
+ console.log(chalk.gray(` MCP Avg Latency: ${metrics.mcp_avg_latency_ms.toFixed(1)}ms`));
1342
+ console.log();
1343
+ console.log(chalk.cyan(" Usage Metrics:"));
1344
+ console.log(chalk.gray(` Active Installs: ${metrics.active_installs}`));
1345
+ console.log(chalk.gray(` Error Clusters: ${metrics.error_cluster_count}`));
1346
+ console.log(chalk.gray(` Hook Hit Rate: ${format(metrics.hook_hit_rate, true)}`));
1347
+ console.log();
1348
+ console.log(chalk.cyan(" Cost Metrics:"));
1349
+ console.log(chalk.gray(` Cost Per Session: $${metrics.cost_per_session_usd.toFixed(4)}`));
1350
+ console.log();
1351
+ }
1352
+ async function runDailyLoop(projectRoot) {
1353
+ const { MetaOrchestrator } = await import("../lib/meta-orchestrator.js");
1354
+ const { loadAllAgentConfigs } = await import("../lib/agent-config.js");
1355
+ console.log(chalk.bold("\n Peter Parker - Daily RL Agent Loop\n"));
1356
+ const orchestrator = new MetaOrchestrator(projectRoot);
1357
+ const agents = orchestrator.getAgents();
1358
+ if (agents.length === 0) {
1359
+ console.log(chalk.yellow(" No RL agents configured."));
1360
+ console.log(chalk.gray(" Run: jfl onboard <service-path> to discover metrics"));
1361
+ console.log(chalk.gray(" Or: jfl peter agent create\n"));
1362
+ return;
1363
+ }
1364
+ console.log(chalk.gray(` Found ${agents.length} RL agent(s):\n`));
1365
+ for (const agent of agents) {
1366
+ console.log(chalk.gray(` • ${agent.name} (${agent.metric}, ${agent.direction})`));
1367
+ }
1368
+ console.log();
1369
+ // Read training buffer to check for stale agents
1370
+ const tb = new TrainingBuffer(projectRoot);
1371
+ const entries = tb.read();
1372
+ const now = Date.now();
1373
+ const staleThreshold = 24 * 60 * 60 * 1000; // 24 hours
1374
+ const staleAgents = [];
1375
+ const activeAgents = [];
1376
+ for (const agent of agents) {
1377
+ const agentEntries = entries.filter(e => e.agent === agent.name);
1378
+ const latestEntry = agentEntries.sort((a, b) => b.ts.localeCompare(a.ts))[0];
1379
+ if (!latestEntry) {
1380
+ staleAgents.push(agent.name);
1381
+ console.log(chalk.yellow(` ⚠ ${agent.name}: No training data (never run)`));
1382
+ }
1383
+ else {
1384
+ const lastRun = new Date(latestEntry.ts).getTime();
1385
+ if (now - lastRun > staleThreshold) {
1386
+ staleAgents.push(agent.name);
1387
+ const hoursAgo = Math.floor((now - lastRun) / (60 * 60 * 1000));
1388
+ console.log(chalk.yellow(` ⚠ ${agent.name}: Stale (${hoursAgo}h since last training)`));
1389
+ }
1390
+ else {
1391
+ activeAgents.push(agent.name);
1392
+ console.log(chalk.green(` ✓ ${agent.name}: Recent training data`));
1393
+ }
1394
+ }
1395
+ }
1396
+ console.log();
1397
+ // ── Pre-flight: Mine tuples + Synthesize product context ──
1398
+ console.log(chalk.cyan(" Pre-flight: Mining tuples from journals...\n"));
1399
+ try {
1400
+ const { mineAll } = await import("../lib/tuple-miner.js");
1401
+ const { tuples: minedTuples, stats: mineStats } = mineAll({ all: true });
1402
+ if (minedTuples.length > 0) {
1403
+ const existing = new Set(entries.map(e => e.id));
1404
+ let newCount = 0;
1405
+ for (const tuple of minedTuples) {
1406
+ const id = `tb_${Buffer.from(JSON.stringify(tuple.state) + JSON.stringify(tuple.action)).toString("base64").slice(0, 12)}`;
1407
+ if (!existing.has(id)) {
1408
+ tb.append({ ...tuple, id, v: "1", ts: new Date().toISOString() });
1409
+ newCount++;
1410
+ }
1411
+ }
1412
+ console.log(chalk.gray(` Mined ${mineStats.totalMined} tuples, ${newCount} new → buffer total: ${entries.length + newCount}`));
1413
+ }
1414
+ }
1415
+ catch (err) {
1416
+ console.log(chalk.yellow(` Tuple mining failed: ${err.message}`));
1417
+ }
1418
+ console.log(chalk.cyan("\n Pre-flight: Synthesizing product context...\n"));
1419
+ try {
1420
+ await runSynthesis(projectRoot);
1421
+ }
1422
+ catch (err) {
1423
+ console.log(chalk.yellow(` Synthesis failed: ${err.message}`));
1424
+ }
1425
+ // ── Layer 3: Strategic reasoning ──
1426
+ console.log(chalk.cyan("\n Layer 3: Strategic reasoning...\n"));
1427
+ try {
1428
+ const { StratusClient } = await import("../lib/stratus-client.js");
1429
+ const stratus = new StratusClient();
1430
+ const productContextPath = path.join(projectRoot, ".jfl", "product-context.md");
1431
+ const productCtx = fs.existsSync(productContextPath) ? fs.readFileSync(productContextPath, "utf-8").slice(0, 2000) : "No product context yet.";
1432
+ // Get recent results per agent
1433
+ const agentSummaries = agents.map(a => {
1434
+ const agentEntries = entries.filter(e => e.agent === a.name).slice(-5);
1435
+ const improved = agentEntries.filter(e => e.reward?.improved).length;
1436
+ return `${a.name} (${a.metric}, ${a.direction}): ${agentEntries.length} recent tuples, ${improved} improved`;
1437
+ }).join("\n");
1438
+ const strategicPrompt = `You are Peter Parker, a strategic agent orchestrator. Based on the product context and agent performance, decide:
1439
+ 1. Which agents should run tonight (and why)
1440
+ 2. Which agents to skip (and why)
1441
+ 3. Any priority shifts or new metric suggestions
1442
+
1443
+ Product context:
1444
+ ${productCtx}
1445
+
1446
+ Agent performance:
1447
+ ${agentSummaries}
1448
+
1449
+ Stale agents (haven't run in 24h): ${staleAgents.join(", ") || "none"}
1450
+
1451
+ Be concise. Output a JSON object: { "run": ["agent1", "agent2"], "skip": ["agent3"], "reasoning": "brief explanation", "suggestions": "any new metrics or priority changes" }`;
1452
+ const response = await stratus.reason(strategicPrompt, { maxTokens: 500, temperature: 0.3 });
1453
+ const strategicText = response?.choices?.[0]?.message?.content || "";
1454
+ console.log(chalk.gray(` Strategic reasoning:\n ${strategicText.slice(0, 500)}`));
1455
+ // Try to parse JSON from response
1456
+ try {
1457
+ const jsonMatch = strategicText.match(/\{[\s\S]*\}/);
1458
+ if (jsonMatch) {
1459
+ const strategic = JSON.parse(jsonMatch[0]);
1460
+ if (strategic.reasoning) {
1461
+ console.log(chalk.cyan(`\n Reasoning: ${strategic.reasoning}`));
1462
+ }
1463
+ if (strategic.suggestions) {
1464
+ console.log(chalk.gray(` Suggestions: ${strategic.suggestions}`));
1465
+ }
1466
+ // Write strategic decision to journal
1467
+ const journalPath = path.join(projectRoot, ".jfl", "journal");
1468
+ if (fs.existsSync(journalPath)) {
1469
+ const entry = {
1470
+ v: 2, ts: new Date().toISOString(), session: "peter-parker",
1471
+ type: "decision", status: "complete",
1472
+ title: "Layer 3: Strategic reasoning",
1473
+ summary: strategic.reasoning || strategicText.slice(0, 200),
1474
+ detail: strategicText,
1475
+ };
1476
+ const files = fs.readdirSync(journalPath).filter(f => f.endsWith(".jsonl")).sort();
1477
+ const latestJournal = files[files.length - 1];
1478
+ if (latestJournal) {
1479
+ fs.appendFileSync(path.join(journalPath, latestJournal), "\n" + JSON.stringify(entry));
1480
+ }
1481
+ }
1482
+ }
1483
+ }
1484
+ catch { /* non-JSON response, that's fine */ }
1485
+ }
1486
+ catch (err) {
1487
+ console.log(chalk.yellow(` Strategic reasoning failed: ${err.message}`));
1488
+ }
1489
+ console.log();
1490
+ // Check for scope:impact events that should trigger downstream agents
1491
+ const impactTriggered = orchestrator.getImpactTriggeredAgents();
1492
+ if (impactTriggered.length > 0) {
1493
+ console.log(chalk.cyan(" Scope impact events detected:"));
1494
+ for (const { agent, impactPattern } of impactTriggered) {
1495
+ console.log(chalk.gray(` • ${agent.name} should react to: ${impactPattern}`));
1496
+ }
1497
+ console.log();
1498
+ }
1499
+ // Schedule next agent using meta-orchestrator
1500
+ const decision = orchestrator.scheduleNext();
1501
+ if (!decision) {
1502
+ console.log(chalk.yellow(" No agent available to schedule."));
1503
+ return;
1504
+ }
1505
+ console.log(chalk.bold(` Scheduled: ${decision.agent.name}`));
1506
+ console.log(chalk.gray(` Reason: ${decision.reason}`));
1507
+ if (decision.impactPattern) {
1508
+ console.log(chalk.gray(` Triggered by: ${decision.impactPattern}`));
1509
+ }
1510
+ if (decision.emaReward !== undefined) {
1511
+ console.log(chalk.gray(` EMA Reward: ${decision.emaReward.toFixed(4)}`));
1512
+ }
1513
+ console.log();
1514
+ // Run autoresearch for stale agents
1515
+ if (staleAgents.length > 0) {
1516
+ console.log(chalk.cyan(` Running autoresearch for ${staleAgents.length} stale agent(s)...\n`));
1517
+ for (const agentName of staleAgents) {
1518
+ console.log(chalk.bold(`\n ── ${agentName} ${"─".repeat(45)}\n`));
1519
+ await agentRun(projectRoot, agentName); // Use config.rounds (default 50)
1520
+ }
1521
+ }
1522
+ // Post summary event
1523
+ await postHubEvent(projectRoot, "peter:daily-complete", {
1524
+ total_agents: agents.length,
1525
+ stale_agents: staleAgents,
1526
+ active_agents: activeAgents,
1527
+ impact_triggered: impactTriggered.map(t => t.agent.name),
1528
+ scheduled: decision.agent.name,
1529
+ reason: decision.reason,
1530
+ });
1531
+ console.log(chalk.green("\n Daily loop complete.\n"));
1532
+ }
1533
+ async function agentSwarm(projectRoot, rounds) {
1534
+ const { MetaOrchestrator } = await import("../lib/meta-orchestrator.js");
1535
+ const orchestrator = new MetaOrchestrator(projectRoot);
1536
+ const agents = orchestrator.getAgents();
1537
+ if (agents.length === 0) {
1538
+ console.log(chalk.yellow("\n No agents configured."));
1539
+ console.log(chalk.gray(" Run: jfl peter agent create\n"));
1540
+ return;
1541
+ }
1542
+ console.log(chalk.bold(`\n Agent Swarm: ${agents.length} agents, ${rounds} rounds total\n`));
1543
+ for (const agent of agents) {
1544
+ console.log(chalk.gray(` - ${agent.name} (${agent.metric})`));
1545
+ }
1546
+ console.log();
1547
+ const result = await orchestrator.runSwarm(rounds, (agent, round, reward, reason) => {
1548
+ const emoji = reward > 0 ? chalk.green("+") : reward < 0 ? chalk.red("-") : chalk.gray("=");
1549
+ console.log(chalk.gray(` [${round}/${rounds}] ${agent} → ${emoji}${Math.abs(reward).toFixed(4)} (${reason})`));
1550
+ });
1551
+ console.log(chalk.bold(`\n ── Swarm Complete ${"─".repeat(38)}\n`));
1552
+ const stats = orchestrator.getStats();
1553
+ console.log(chalk.gray(` Total rounds: ${stats.totalRounds}`));
1554
+ console.log(chalk.gray(` Avg EMA reward: ${stats.avgEmaReward.toFixed(4)}`));
1555
+ console.log(chalk.gray(` Overall win rate: ${(stats.overallWinRate * 100).toFixed(1)}%`));
1556
+ if (stats.bestAgent) {
1557
+ console.log(chalk.green(` Best agent: ${stats.bestAgent.name} (EMA: ${stats.bestAgent.emaReward.toFixed(4)})`));
1558
+ }
1559
+ console.log(chalk.bold("\n Per-Agent Results:\n"));
1560
+ for (const [name, data] of Object.entries(result.perAgent)) {
1561
+ console.log(chalk.cyan(` ${name}: ${data.rounds} rounds, total delta: ${data.totalReward > 0 ? "+" : ""}${data.totalReward.toFixed(4)}`));
1562
+ }
1563
+ console.log();
1564
+ }
1565
+ // ============================================================================
1566
+ // Synthesis — distill journals + events into product context for agents
1567
+ // ============================================================================
1568
+ async function runSynthesis(projectRoot) {
1569
+ console.log(chalk.bold("\n Peter Parker - Product Context Synthesis\n"));
1570
+ const { StratusClient } = await import("../lib/stratus-client.js");
1571
+ const stratus = process.env.STRATUS_API_KEY
1572
+ ? new StratusClient({ apiKey: process.env.STRATUS_API_KEY })
1573
+ : null;
1574
+ if (!stratus) {
1575
+ console.log(chalk.red(" STRATUS_API_KEY required for synthesis"));
1576
+ return;
1577
+ }
1578
+ // 1. Read recent journals (last 7 days)
1579
+ const journalDir = path.join(projectRoot, ".jfl", "journal");
1580
+ let journalEntries = [];
1581
+ if (fs.existsSync(journalDir)) {
1582
+ const files = fs.readdirSync(journalDir).filter(f => f.endsWith(".jsonl"));
1583
+ for (const file of files.slice(-10)) { // Last 10 journal files
1584
+ const content = fs.readFileSync(path.join(journalDir, file), "utf-8").trim();
1585
+ if (content)
1586
+ journalEntries.push(...content.split("\n").slice(-20)); // Last 20 entries per file
1587
+ }
1588
+ }
1589
+ console.log(chalk.gray(` Read ${journalEntries.length} journal entries`));
1590
+ // 2. Read recent events
1591
+ const eventsPath = path.join(projectRoot, ".jfl", "service-events.jsonl");
1592
+ let events = [];
1593
+ if (fs.existsSync(eventsPath)) {
1594
+ events = fs.readFileSync(eventsPath, "utf-8").trim().split("\n").slice(-30); // Last 30 events
1595
+ }
1596
+ console.log(chalk.gray(` Read ${events.length} events`));
1597
+ // 3. Read agent results from training buffer
1598
+ const bufferPath = path.join(projectRoot, ".jfl", "training-buffer.jsonl");
1599
+ let tuples = [];
1600
+ if (fs.existsSync(bufferPath)) {
1601
+ tuples = fs.readFileSync(bufferPath, "utf-8").trim().split("\n").slice(-20);
1602
+ }
1603
+ console.log(chalk.gray(` Read ${tuples.length} training tuples`));
1604
+ // 4. Read current agent configs
1605
+ const { listAgentConfigs, loadAgentConfig } = await import("../lib/agent-config.js");
1606
+ const agentNames = listAgentConfigs(projectRoot);
1607
+ const configs = agentNames.map(name => { try {
1608
+ return loadAgentConfig(projectRoot, name);
1609
+ }
1610
+ catch {
1611
+ return null;
1612
+ } }).filter(Boolean);
1613
+ const agentSummary = configs.map((c) => `${c.name}: ${c.metric} (${c.direction}), target: ${c.target_repo || "self"}`).join("\n");
1614
+ // 5. Send to Stratus for synthesis
1615
+ console.log(chalk.gray("\n Synthesizing with Stratus...\n"));
1616
+ const prompt = `You are Peter Parker, the orchestrator for a software product portfolio.
1617
+
1618
+ Your job: read the recent activity (journals, events, experiment results) and produce a PRODUCT CONTEXT document that tells scoped RL agents what the product is, what's been happening, and what to focus on.
1619
+
1620
+ ## Current Agents
1621
+ ${agentSummary}
1622
+
1623
+ ## Recent Journal Entries (last 7 days)
1624
+ ${journalEntries.slice(-15).map(e => e.slice(0, 200)).join("\n")}
1625
+
1626
+ ## Recent Events
1627
+ ${events.slice(-15).join("\n")}
1628
+
1629
+ ## Recent Experiment Results
1630
+ ${tuples.slice(-10).join("\n")}
1631
+
1632
+ ---
1633
+
1634
+ Produce a concise product-context.md with these sections:
1635
+ 1. **Product State** — what is this product, what's working, what's broken
1636
+ 2. **Recent Activity** — what the team has been building/fixing (distilled from journals)
1637
+ 3. **Agent Guidance** — for each active agent, specific guidance based on recent context (e.g. "cli-speed: startup is already fast, focus on subcommand latency")
1638
+ 4. **Priority Shifts** — any metrics that should be deprioritized or new metrics to consider
1639
+ 5. **Open Questions** — things that need human input
1640
+
1641
+ Be concise. This document is consumed by agents, not humans. Skip fluff.`;
1642
+ try {
1643
+ const response = await stratus.reason(prompt, { maxTokens: 1500, temperature: 0.3 });
1644
+ const synthesis = response.choices[0]?.message?.content || "";
1645
+ if (synthesis) {
1646
+ const outputPath = path.join(projectRoot, ".jfl", "product-context.md");
1647
+ fs.writeFileSync(outputPath, `# Product Context\n\n_Synthesized by Peter Parker at ${new Date().toISOString()}_\n\n${synthesis}\n`);
1648
+ console.log(chalk.green(` ✓ Product context written to .jfl/product-context.md`));
1649
+ console.log(chalk.gray(` (${synthesis.length} chars)\n`));
1650
+ }
1651
+ }
1652
+ catch (err) {
1653
+ console.log(chalk.red(` Synthesis failed: ${err.message}`));
1654
+ }
1655
+ }
1656
+ // ============================================================================
1657
+ // Review — check autoresearch branches, audit with Stratus, prepare PRs
1658
+ // ============================================================================
1659
+ async function runReview(projectRoot) {
1660
+ console.log(chalk.bold("\n Peter Parker - Autoresearch Review\n"));
1661
+ const { spawnSync } = await import("child_process");
1662
+ // Find session branches on remote
1663
+ const result = spawnSync("git", ["branch", "-r", "--list", "origin/session/*"], { cwd: projectRoot, encoding: "utf-8" });
1664
+ const branches = (result.stdout || "").trim().split("\n").map(b => b.trim()).filter(Boolean);
1665
+ if (branches.length === 0) {
1666
+ console.log(chalk.yellow(" No autoresearch branches found.\n"));
1667
+ return;
1668
+ }
1669
+ console.log(chalk.gray(` Found ${branches.length} session branch(es):\n`));
1670
+ for (const branch of branches) {
1671
+ const shortBranch = branch.replace("origin/", "");
1672
+ // Get diff stats
1673
+ const diffStat = spawnSync("git", ["diff", "--stat", `origin/main...${branch}`], { cwd: projectRoot, encoding: "utf-8" });
1674
+ const logResult = spawnSync("git", ["log", "--oneline", `origin/main..${branch}`], { cwd: projectRoot, encoding: "utf-8" });
1675
+ const commits = (logResult.stdout || "").trim().split("\n").filter(Boolean);
1676
+ console.log(chalk.cyan(` ${shortBranch}`));
1677
+ console.log(chalk.gray(` ${commits.length} commits`));
1678
+ if (diffStat.stdout) {
1679
+ const lastLine = diffStat.stdout.trim().split("\n").pop() || "";
1680
+ console.log(chalk.gray(` ${lastLine.trim()}`));
1681
+ }
1682
+ console.log();
1683
+ }
1684
+ console.log(chalk.gray(" To create PRs from these branches:"));
1685
+ console.log(chalk.gray(" gh pr create --base main --head <branch> --title '...'\n"));
1686
+ }
563
1687
  export async function peterCommand(action, options = {}) {
564
1688
  const projectRoot = process.cwd();
1689
+ // Handle "agent" subcommand
1690
+ if (action === "agent") {
1691
+ const subAction = options.name || options.task; // name is the subcommand, task might be agent name
1692
+ if (subAction === "create") {
1693
+ await agentCreate(projectRoot);
1694
+ return;
1695
+ }
1696
+ else if (subAction === "list") {
1697
+ await agentList(projectRoot);
1698
+ return;
1699
+ }
1700
+ else if (subAction === "run") {
1701
+ // jfl peter agent run <name> --rounds N
1702
+ // The agent name would be in a different position
1703
+ console.log(chalk.yellow("\n Usage: jfl peter agent run <name> --rounds N\n"));
1704
+ return;
1705
+ }
1706
+ else if (subAction === "swarm") {
1707
+ const rounds = parseInt(options.rounds || "20", 10);
1708
+ await agentSwarm(projectRoot, rounds);
1709
+ return;
1710
+ }
1711
+ else if (subAction) {
1712
+ // Assume it's an agent name for "run"
1713
+ // Use CLI override if provided, otherwise use config.rounds (default 50)
1714
+ const roundsOverride = options.rounds ? parseInt(options.rounds, 10) : undefined;
1715
+ await agentRun(projectRoot, subAction, roundsOverride);
1716
+ return;
1717
+ }
1718
+ // Show agent help
1719
+ console.log(chalk.bold("\n Peter Parker - Scoped Agent Commands\n"));
1720
+ console.log(chalk.gray(" Commands:"));
1721
+ console.log(" jfl peter agent create Interactive agent creation");
1722
+ console.log(" jfl peter agent list List configured agents");
1723
+ console.log(" jfl peter agent run <name> [--rounds N] Run a specific agent");
1724
+ console.log(" jfl peter agent swarm [--rounds N] Run all agents with meta-orchestrator");
1725
+ console.log();
1726
+ return;
1727
+ }
565
1728
  switch (action) {
566
1729
  case "setup": {
567
1730
  let profile = "balanced";
@@ -592,7 +1755,47 @@ export async function peterCommand(action, options = {}) {
592
1755
  break;
593
1756
  }
594
1757
  case "experiment": {
595
- await runExperiment(projectRoot);
1758
+ if (options.mode === "autoresearch") {
1759
+ const rounds = parseInt(options.rounds || "5", 10);
1760
+ await runAutoresearch(projectRoot, rounds);
1761
+ }
1762
+ else {
1763
+ await runExperiment(projectRoot);
1764
+ }
1765
+ break;
1766
+ }
1767
+ case "autoresearch": {
1768
+ const rounds = parseInt(options.rounds || "5", 10);
1769
+ await runAutoresearch(projectRoot, rounds);
1770
+ break;
1771
+ }
1772
+ case "telemetry": {
1773
+ await runTelemetryAgent(projectRoot);
1774
+ break;
1775
+ }
1776
+ case "sentinel": {
1777
+ await runSentinelReview(projectRoot);
1778
+ break;
1779
+ }
1780
+ case "metrics": {
1781
+ await showMetrics(projectRoot);
1782
+ break;
1783
+ }
1784
+ case "daily": {
1785
+ await runDailyLoop(projectRoot);
1786
+ break;
1787
+ }
1788
+ case "nightly": {
1789
+ // Alias for daily
1790
+ await runDailyLoop(projectRoot);
1791
+ break;
1792
+ }
1793
+ case "synthesize": {
1794
+ await runSynthesis(projectRoot);
1795
+ break;
1796
+ }
1797
+ case "review": {
1798
+ await runReview(projectRoot);
596
1799
  break;
597
1800
  }
598
1801
  default: {
@@ -602,9 +1805,22 @@ export async function peterCommand(action, options = {}) {
602
1805
  console.log(" jfl peter run [--task <task>] Run orchestrator");
603
1806
  console.log(" jfl peter pr --task <task> Run + branch + PR");
604
1807
  console.log(" jfl peter experiment Proactive: pick + execute next experiment");
1808
+ console.log(" jfl peter autoresearch [--rounds N] Shortcut for autoresearch mode");
605
1809
  console.log(" jfl peter status Show status + recent events");
606
1810
  console.log(" jfl peter dashboard Live event stream dashboard");
607
1811
  console.log();
1812
+ console.log(chalk.bold(" Telemetry & RL:\n"));
1813
+ console.log(" jfl peter telemetry Run telemetry agent (platform digest)");
1814
+ console.log(" jfl peter sentinel Run Sentinel nightly review");
1815
+ console.log(" jfl peter metrics Show current platform metrics");
1816
+ console.log(" jfl peter daily Daily RL loop (check stale agents, schedule next)");
1817
+ console.log();
1818
+ console.log(chalk.bold(" Scoped Agents:\n"));
1819
+ console.log(" jfl peter agent create Interactive agent creation");
1820
+ console.log(" jfl peter agent list List configured agents");
1821
+ console.log(" jfl peter agent run <name> [--rounds N] Run a specific scoped agent");
1822
+ console.log(" jfl peter agent swarm [--rounds N] Run all agents with orchestrator");
1823
+ console.log();
608
1824
  }
609
1825
  }
610
1826
  }