jfl 0.4.4 → 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.
- package/dist/commands/context-hub.d.ts.map +1 -1
- package/dist/commands/context-hub.js +818 -39
- package/dist/commands/context-hub.js.map +1 -1
- package/dist/commands/eval.d.ts +1 -1
- package/dist/commands/eval.d.ts.map +1 -1
- package/dist/commands/eval.js +192 -1
- package/dist/commands/eval.js.map +1 -1
- package/dist/commands/findings.d.ts +6 -0
- package/dist/commands/findings.d.ts.map +1 -0
- package/dist/commands/findings.js +203 -0
- package/dist/commands/findings.js.map +1 -0
- package/dist/commands/hud.d.ts.map +1 -1
- package/dist/commands/hud.js +47 -9
- package/dist/commands/hud.js.map +1 -1
- package/dist/commands/ide.d.ts +27 -0
- package/dist/commands/ide.d.ts.map +1 -0
- package/dist/commands/ide.js +546 -0
- package/dist/commands/ide.js.map +1 -0
- package/dist/commands/onboard.d.ts.map +1 -1
- package/dist/commands/onboard.js +212 -2
- package/dist/commands/onboard.js.map +1 -1
- package/dist/commands/openclaw.d.ts +3 -0
- package/dist/commands/openclaw.d.ts.map +1 -1
- package/dist/commands/openclaw.js +76 -2
- package/dist/commands/openclaw.js.map +1 -1
- package/dist/commands/peter.d.ts +1 -0
- package/dist/commands/peter.d.ts.map +1 -1
- package/dist/commands/peter.js +935 -15
- package/dist/commands/peter.js.map +1 -1
- package/dist/commands/pi-fleet.d.ts +18 -0
- package/dist/commands/pi-fleet.d.ts.map +1 -0
- package/dist/commands/pi-fleet.js +382 -0
- package/dist/commands/pi-fleet.js.map +1 -0
- package/dist/commands/pi.d.ts.map +1 -1
- package/dist/commands/pi.js +18 -3
- package/dist/commands/pi.js.map +1 -1
- package/dist/commands/scope.d.ts.map +1 -1
- package/dist/commands/scope.js +90 -1
- package/dist/commands/scope.js.map +1 -1
- package/dist/commands/services.d.ts.map +1 -1
- package/dist/commands/services.js +18 -0
- package/dist/commands/services.js.map +1 -1
- package/dist/commands/status.d.ts.map +1 -1
- package/dist/commands/status.js +22 -4
- package/dist/commands/status.js.map +1 -1
- package/dist/commands/viz.d.ts.map +1 -1
- package/dist/commands/viz.js +417 -0
- package/dist/commands/viz.js.map +1 -1
- package/dist/dashboard-static/assets/index-B6b867Pv.js +121 -0
- package/dist/dashboard-static/assets/index-Y4BrqxV-.css +1 -0
- package/dist/dashboard-static/index.html +2 -2
- package/dist/index.js +225 -61
- package/dist/index.js.map +1 -1
- package/dist/lib/agent-config.d.ts +52 -0
- package/dist/lib/agent-config.d.ts.map +1 -0
- package/dist/lib/agent-config.js +231 -0
- package/dist/lib/agent-config.js.map +1 -0
- package/dist/lib/agent-generator.d.ts +10 -0
- package/dist/lib/agent-generator.d.ts.map +1 -1
- package/dist/lib/agent-generator.js +64 -10
- package/dist/lib/agent-generator.js.map +1 -1
- package/dist/lib/agent-session.d.ts +104 -0
- package/dist/lib/agent-session.d.ts.map +1 -0
- package/dist/lib/agent-session.js +627 -0
- package/dist/lib/agent-session.js.map +1 -0
- package/dist/lib/eval-snapshot.d.ts +47 -0
- package/dist/lib/eval-snapshot.d.ts.map +1 -0
- package/dist/lib/eval-snapshot.js +315 -0
- package/dist/lib/eval-snapshot.js.map +1 -0
- package/dist/lib/eval-store.d.ts +5 -0
- package/dist/lib/eval-store.d.ts.map +1 -1
- package/dist/lib/eval-store.js +33 -3
- package/dist/lib/eval-store.js.map +1 -1
- package/dist/lib/findings-engine.d.ts +51 -0
- package/dist/lib/findings-engine.d.ts.map +1 -0
- package/dist/lib/findings-engine.js +338 -0
- package/dist/lib/findings-engine.js.map +1 -0
- package/dist/lib/flow-engine.d.ts +8 -0
- package/dist/lib/flow-engine.d.ts.map +1 -1
- package/dist/lib/flow-engine.js +84 -2
- package/dist/lib/flow-engine.js.map +1 -1
- package/dist/lib/hub-client.d.ts +1 -0
- package/dist/lib/hub-client.d.ts.map +1 -1
- package/dist/lib/hub-client.js +33 -6
- package/dist/lib/hub-client.js.map +1 -1
- package/dist/lib/ide-panes.d.ts +58 -0
- package/dist/lib/ide-panes.d.ts.map +1 -0
- package/dist/lib/ide-panes.js +508 -0
- package/dist/lib/ide-panes.js.map +1 -0
- package/dist/lib/memory-db.js +4 -4
- package/dist/lib/memory-db.js.map +1 -1
- package/dist/lib/memory-indexer.d.ts.map +1 -1
- package/dist/lib/memory-indexer.js +3 -0
- package/dist/lib/memory-indexer.js.map +1 -1
- package/dist/lib/memory-search.d.ts +148 -4
- package/dist/lib/memory-search.d.ts.map +1 -1
- package/dist/lib/memory-search.js +496 -58
- package/dist/lib/memory-search.js.map +1 -1
- package/dist/lib/meta-orchestrator.d.ts +104 -0
- package/dist/lib/meta-orchestrator.d.ts.map +1 -0
- package/dist/lib/meta-orchestrator.js +373 -0
- package/dist/lib/meta-orchestrator.js.map +1 -0
- package/dist/lib/peer-agent-generator.d.ts.map +1 -1
- package/dist/lib/peer-agent-generator.js +43 -19
- package/dist/lib/peer-agent-generator.js.map +1 -1
- package/dist/lib/policy-head.d.ts +25 -0
- package/dist/lib/policy-head.d.ts.map +1 -0
- package/dist/lib/policy-head.js +136 -0
- package/dist/lib/policy-head.js.map +1 -0
- package/dist/lib/replay-buffer.d.ts +93 -0
- package/dist/lib/replay-buffer.d.ts.map +1 -0
- package/dist/lib/replay-buffer.js +302 -0
- package/dist/lib/replay-buffer.js.map +1 -0
- package/dist/lib/sentinel-rl.d.ts +97 -0
- package/dist/lib/sentinel-rl.d.ts.map +1 -0
- package/dist/lib/sentinel-rl.js +430 -0
- package/dist/lib/sentinel-rl.js.map +1 -0
- package/dist/lib/session-lock.d.ts +61 -0
- package/dist/lib/session-lock.d.ts.map +1 -0
- package/dist/lib/session-lock.js +438 -0
- package/dist/lib/session-lock.js.map +1 -0
- package/dist/lib/stratus-client.d.ts +1 -0
- package/dist/lib/stratus-client.d.ts.map +1 -1
- package/dist/lib/stratus-client.js +24 -2
- package/dist/lib/stratus-client.js.map +1 -1
- package/dist/lib/telemetry-agent-v2.d.ts +128 -0
- package/dist/lib/telemetry-agent-v2.d.ts.map +1 -0
- package/dist/lib/telemetry-agent-v2.js +1042 -0
- package/dist/lib/telemetry-agent-v2.js.map +1 -0
- package/dist/lib/telemetry-agent.d.ts.map +1 -1
- package/dist/lib/telemetry-agent.js +27 -6
- package/dist/lib/telemetry-agent.js.map +1 -1
- package/dist/lib/telemetry-digest.d.ts.map +1 -1
- package/dist/lib/telemetry-digest.js +27 -5
- package/dist/lib/telemetry-digest.js.map +1 -1
- package/dist/lib/telemetry.d.ts.map +1 -1
- package/dist/lib/telemetry.js +29 -4
- package/dist/lib/telemetry.js.map +1 -1
- package/dist/lib/text-preprocessing.d.ts +83 -0
- package/dist/lib/text-preprocessing.d.ts.map +1 -0
- package/dist/lib/text-preprocessing.js +261 -0
- package/dist/lib/text-preprocessing.js.map +1 -0
- package/dist/lib/training-buffer.d.ts +86 -0
- package/dist/lib/training-buffer.d.ts.map +1 -0
- package/dist/lib/training-buffer.js +139 -0
- package/dist/lib/training-buffer.js.map +1 -0
- package/dist/lib/tuple-miner.d.ts +30 -0
- package/dist/lib/tuple-miner.d.ts.map +1 -0
- package/dist/lib/tuple-miner.js +427 -0
- package/dist/lib/tuple-miner.js.map +1 -0
- package/dist/lib/vm-backend.d.ts +72 -0
- package/dist/lib/vm-backend.d.ts.map +1 -0
- package/dist/lib/vm-backend.js +175 -0
- package/dist/lib/vm-backend.js.map +1 -0
- package/dist/lib/workspace/backend.d.ts +53 -0
- package/dist/lib/workspace/backend.d.ts.map +1 -0
- package/dist/lib/workspace/backend.js +37 -0
- package/dist/lib/workspace/backend.js.map +1 -0
- package/dist/lib/workspace/cmux-adapter.d.ts +46 -0
- package/dist/lib/workspace/cmux-adapter.d.ts.map +1 -0
- package/dist/lib/workspace/cmux-adapter.js +261 -0
- package/dist/lib/workspace/cmux-adapter.js.map +1 -0
- package/dist/lib/workspace/data-pipeline.d.ts +35 -0
- package/dist/lib/workspace/data-pipeline.d.ts.map +1 -0
- package/dist/lib/workspace/data-pipeline.js +463 -0
- package/dist/lib/workspace/data-pipeline.js.map +1 -0
- package/dist/lib/workspace/engine.d.ts +64 -0
- package/dist/lib/workspace/engine.d.ts.map +1 -0
- package/dist/lib/workspace/engine.js +397 -0
- package/dist/lib/workspace/engine.js.map +1 -0
- package/dist/lib/workspace/notifications.d.ts +14 -0
- package/dist/lib/workspace/notifications.d.ts.map +1 -0
- package/dist/lib/workspace/notifications.js +41 -0
- package/dist/lib/workspace/notifications.js.map +1 -0
- package/dist/lib/workspace/surface-registry.d.ts +49 -0
- package/dist/lib/workspace/surface-registry.d.ts.map +1 -0
- package/dist/lib/workspace/surface-registry.js +217 -0
- package/dist/lib/workspace/surface-registry.js.map +1 -0
- package/dist/lib/workspace/surface-type.d.ts +153 -0
- package/dist/lib/workspace/surface-type.d.ts.map +1 -0
- package/dist/lib/workspace/surface-type.js +9 -0
- package/dist/lib/workspace/surface-type.js.map +1 -0
- package/dist/lib/workspace/surfaces/agent-overview.d.ts +16 -0
- package/dist/lib/workspace/surfaces/agent-overview.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/agent-overview.js +116 -0
- package/dist/lib/workspace/surfaces/agent-overview.js.map +1 -0
- package/dist/lib/workspace/surfaces/agent.d.ts +16 -0
- package/dist/lib/workspace/surfaces/agent.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/agent.js +112 -0
- package/dist/lib/workspace/surfaces/agent.js.map +1 -0
- package/dist/lib/workspace/surfaces/claude.d.ts +15 -0
- package/dist/lib/workspace/surfaces/claude.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/claude.js +23 -0
- package/dist/lib/workspace/surfaces/claude.js.map +1 -0
- package/dist/lib/workspace/surfaces/dashboard.d.ts +21 -0
- package/dist/lib/workspace/surfaces/dashboard.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/dashboard.js +32 -0
- package/dist/lib/workspace/surfaces/dashboard.js.map +1 -0
- package/dist/lib/workspace/surfaces/eval.d.ts +15 -0
- package/dist/lib/workspace/surfaces/eval.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/eval.js +42 -0
- package/dist/lib/workspace/surfaces/eval.js.map +1 -0
- package/dist/lib/workspace/surfaces/event-stream.d.ts +16 -0
- package/dist/lib/workspace/surfaces/event-stream.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/event-stream.js +40 -0
- package/dist/lib/workspace/surfaces/event-stream.js.map +1 -0
- package/dist/lib/workspace/surfaces/flow.d.ts +16 -0
- package/dist/lib/workspace/surfaces/flow.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/flow.js +49 -0
- package/dist/lib/workspace/surfaces/flow.js.map +1 -0
- package/dist/lib/workspace/surfaces/index.d.ts +16 -0
- package/dist/lib/workspace/surfaces/index.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/index.js +16 -0
- package/dist/lib/workspace/surfaces/index.js.map +1 -0
- package/dist/lib/workspace/surfaces/portfolio.d.ts +16 -0
- package/dist/lib/workspace/surfaces/portfolio.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/portfolio.js +102 -0
- package/dist/lib/workspace/surfaces/portfolio.js.map +1 -0
- package/dist/lib/workspace/surfaces/service.d.ts +16 -0
- package/dist/lib/workspace/surfaces/service.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/service.js +45 -0
- package/dist/lib/workspace/surfaces/service.js.map +1 -0
- package/dist/lib/workspace/surfaces/shell.d.ts +15 -0
- package/dist/lib/workspace/surfaces/shell.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/shell.js +19 -0
- package/dist/lib/workspace/surfaces/shell.js.map +1 -0
- package/dist/lib/workspace/surfaces/telemetry.d.ts +16 -0
- package/dist/lib/workspace/surfaces/telemetry.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/telemetry.js +48 -0
- package/dist/lib/workspace/surfaces/telemetry.js.map +1 -0
- package/dist/lib/workspace/surfaces/topology.d.ts +15 -0
- package/dist/lib/workspace/surfaces/topology.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/topology.js +19 -0
- package/dist/lib/workspace/surfaces/topology.js.map +1 -0
- package/dist/lib/workspace/surfaces/training.d.ts +16 -0
- package/dist/lib/workspace/surfaces/training.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/training.js +22 -0
- package/dist/lib/workspace/surfaces/training.js.map +1 -0
- package/dist/lib/workspace/tmux-adapter.d.ts +27 -0
- package/dist/lib/workspace/tmux-adapter.d.ts.map +1 -0
- package/dist/lib/workspace/tmux-adapter.js +106 -0
- package/dist/lib/workspace/tmux-adapter.js.map +1 -0
- package/dist/mcp/context-hub-mcp.js +7 -24
- package/dist/mcp/context-hub-mcp.js.map +1 -1
- package/dist/types/flows.d.ts +2 -0
- package/dist/types/flows.d.ts.map +1 -1
- package/dist/types/ide.d.ts +49 -0
- package/dist/types/ide.d.ts.map +1 -0
- package/dist/types/ide.js +5 -0
- package/dist/types/ide.js.map +1 -0
- package/dist/types/platform-digest.d.ts +228 -0
- package/dist/types/platform-digest.d.ts.map +1 -0
- package/dist/types/platform-digest.js +5 -0
- package/dist/types/platform-digest.js.map +1 -0
- package/dist/types/telemetry-digest.d.ts +2 -0
- package/dist/types/telemetry-digest.d.ts.map +1 -1
- package/dist/utils/ensure-project.d.ts +1 -0
- package/dist/utils/ensure-project.d.ts.map +1 -1
- package/dist/utils/ensure-project.js +19 -7
- package/dist/utils/ensure-project.js.map +1 -1
- package/dist/utils/jfl-config.d.ts +1 -0
- package/dist/utils/jfl-config.d.ts.map +1 -1
- package/dist/utils/jfl-config.js +19 -1
- package/dist/utils/jfl-config.js.map +1 -1
- package/dist/utils/jfl-paths.d.ts +5 -0
- package/dist/utils/jfl-paths.d.ts.map +1 -1
- package/dist/utils/jfl-paths.js +25 -3
- package/dist/utils/jfl-paths.js.map +1 -1
- package/package.json +3 -2
- package/packages/pi/AGENTS.md +112 -0
- package/packages/pi/extensions/agent-grid.ts +191 -0
- package/packages/pi/extensions/agent-names.ts +178 -0
- package/packages/pi/extensions/autoresearch.ts +427 -0
- package/packages/pi/extensions/bookmarks.ts +85 -0
- package/packages/pi/extensions/context.ts +151 -0
- package/packages/pi/extensions/crm-tool.ts +61 -0
- package/packages/pi/extensions/eval-tool.ts +224 -0
- package/packages/pi/extensions/eval.ts +60 -0
- package/packages/pi/extensions/footer.ts +239 -0
- package/packages/pi/extensions/hud-tool.ts +145 -0
- package/packages/pi/extensions/index.ts +392 -0
- package/packages/pi/extensions/journal.ts +224 -0
- package/packages/pi/extensions/map-bridge.ts +178 -0
- package/packages/pi/extensions/memory-tool.ts +68 -0
- package/packages/pi/extensions/notifications.ts +73 -0
- package/packages/pi/extensions/peter-parker.ts +202 -0
- package/packages/pi/extensions/policy-head-tool.ts +276 -0
- package/packages/pi/extensions/portfolio-bridge.ts +90 -0
- package/packages/pi/extensions/session.ts +90 -0
- package/packages/pi/extensions/shortcuts.ts +259 -0
- package/packages/pi/extensions/stratus-bridge.ts +115 -0
- package/packages/pi/extensions/synopsis-tool.ts +83 -0
- package/packages/pi/extensions/tool-renderers.ts +352 -0
- package/packages/pi/extensions/training-buffer-tool.ts +368 -0
- package/packages/pi/extensions/types.ts +163 -0
- package/packages/pi/package-lock.json +346 -0
- package/packages/pi/package.json +44 -0
- package/packages/pi/skills/agent-browser/SKILL.md +116 -0
- package/packages/pi/skills/brand-architect/SKILL.md +240 -0
- package/packages/pi/skills/brand-architect/config.yaml +137 -0
- package/packages/pi/skills/campaign-hud/config.yaml +112 -0
- package/packages/pi/skills/content-creator/SKILL.md +294 -0
- package/packages/pi/skills/context/SKILL.md +65 -0
- package/packages/pi/skills/debug/MULTI_AGENT.md +360 -0
- package/packages/pi/skills/debug/SKILL.md +554 -0
- package/packages/pi/skills/end/SKILL.md +1782 -0
- package/packages/pi/skills/eval/SKILL.md +75 -0
- package/packages/pi/skills/fly-deploy/SKILL.md +676 -0
- package/packages/pi/skills/founder-video/SKILL.md +467 -0
- package/packages/pi/skills/hud/SKILL.md +160 -0
- package/packages/pi/skills/orchestrate/SKILL.md +74 -0
- package/packages/pi/skills/pi-agents/SKILL.md +78 -0
- package/packages/pi/skills/react-best-practices/AGENTS.md +2249 -0
- package/packages/pi/skills/react-best-practices/README.md +123 -0
- package/packages/pi/skills/react-best-practices/SKILL.md +125 -0
- package/packages/pi/skills/react-best-practices/metadata.json +15 -0
- package/packages/pi/skills/react-best-practices/rules/_sections.md +46 -0
- package/packages/pi/skills/react-best-practices/rules/_template.md +28 -0
- package/packages/pi/skills/react-best-practices/rules/advanced-event-handler-refs.md +55 -0
- package/packages/pi/skills/react-best-practices/rules/advanced-use-latest.md +49 -0
- package/packages/pi/skills/react-best-practices/rules/async-api-routes.md +38 -0
- package/packages/pi/skills/react-best-practices/rules/async-defer-await.md +80 -0
- package/packages/pi/skills/react-best-practices/rules/async-dependencies.md +36 -0
- package/packages/pi/skills/react-best-practices/rules/async-parallel.md +28 -0
- package/packages/pi/skills/react-best-practices/rules/async-suspense-boundaries.md +99 -0
- package/packages/pi/skills/react-best-practices/rules/bundle-barrel-imports.md +59 -0
- package/packages/pi/skills/react-best-practices/rules/bundle-conditional.md +31 -0
- package/packages/pi/skills/react-best-practices/rules/bundle-defer-third-party.md +49 -0
- package/packages/pi/skills/react-best-practices/rules/bundle-dynamic-imports.md +35 -0
- package/packages/pi/skills/react-best-practices/rules/bundle-preload.md +50 -0
- package/packages/pi/skills/react-best-practices/rules/client-event-listeners.md +74 -0
- package/packages/pi/skills/react-best-practices/rules/client-swr-dedup.md +56 -0
- package/packages/pi/skills/react-best-practices/rules/js-batch-dom-css.md +82 -0
- package/packages/pi/skills/react-best-practices/rules/js-cache-function-results.md +80 -0
- package/packages/pi/skills/react-best-practices/rules/js-cache-property-access.md +28 -0
- package/packages/pi/skills/react-best-practices/rules/js-cache-storage.md +70 -0
- package/packages/pi/skills/react-best-practices/rules/js-combine-iterations.md +32 -0
- package/packages/pi/skills/react-best-practices/rules/js-early-exit.md +50 -0
- package/packages/pi/skills/react-best-practices/rules/js-hoist-regexp.md +45 -0
- package/packages/pi/skills/react-best-practices/rules/js-index-maps.md +37 -0
- package/packages/pi/skills/react-best-practices/rules/js-length-check-first.md +49 -0
- package/packages/pi/skills/react-best-practices/rules/js-min-max-loop.md +82 -0
- package/packages/pi/skills/react-best-practices/rules/js-set-map-lookups.md +24 -0
- package/packages/pi/skills/react-best-practices/rules/js-tosorted-immutable.md +57 -0
- package/packages/pi/skills/react-best-practices/rules/rendering-activity.md +26 -0
- package/packages/pi/skills/react-best-practices/rules/rendering-animate-svg-wrapper.md +47 -0
- package/packages/pi/skills/react-best-practices/rules/rendering-conditional-render.md +40 -0
- package/packages/pi/skills/react-best-practices/rules/rendering-content-visibility.md +38 -0
- package/packages/pi/skills/react-best-practices/rules/rendering-hoist-jsx.md +46 -0
- package/packages/pi/skills/react-best-practices/rules/rendering-hydration-no-flicker.md +82 -0
- package/packages/pi/skills/react-best-practices/rules/rendering-svg-precision.md +28 -0
- package/packages/pi/skills/react-best-practices/rules/rerender-defer-reads.md +39 -0
- package/packages/pi/skills/react-best-practices/rules/rerender-dependencies.md +45 -0
- package/packages/pi/skills/react-best-practices/rules/rerender-derived-state.md +29 -0
- package/packages/pi/skills/react-best-practices/rules/rerender-functional-setstate.md +74 -0
- package/packages/pi/skills/react-best-practices/rules/rerender-lazy-state-init.md +58 -0
- package/packages/pi/skills/react-best-practices/rules/rerender-memo.md +44 -0
- package/packages/pi/skills/react-best-practices/rules/rerender-transitions.md +40 -0
- package/packages/pi/skills/react-best-practices/rules/server-after-nonblocking.md +73 -0
- package/packages/pi/skills/react-best-practices/rules/server-cache-lru.md +41 -0
- package/packages/pi/skills/react-best-practices/rules/server-cache-react.md +26 -0
- package/packages/pi/skills/react-best-practices/rules/server-parallel-fetching.md +79 -0
- package/packages/pi/skills/react-best-practices/rules/server-serialization.md +38 -0
- package/packages/pi/skills/remotion-best-practices/SKILL.md +43 -0
- package/packages/pi/skills/remotion-best-practices/rules/3d.md +86 -0
- package/packages/pi/skills/remotion-best-practices/rules/animations.md +29 -0
- package/packages/pi/skills/remotion-best-practices/rules/assets/charts-bar-chart.tsx +173 -0
- package/packages/pi/skills/remotion-best-practices/rules/assets/text-animations-typewriter.tsx +100 -0
- package/packages/pi/skills/remotion-best-practices/rules/assets/text-animations-word-highlight.tsx +108 -0
- package/packages/pi/skills/remotion-best-practices/rules/assets.md +78 -0
- package/packages/pi/skills/remotion-best-practices/rules/audio.md +172 -0
- package/packages/pi/skills/remotion-best-practices/rules/calculate-metadata.md +104 -0
- package/packages/pi/skills/remotion-best-practices/rules/can-decode.md +75 -0
- package/packages/pi/skills/remotion-best-practices/rules/charts.md +58 -0
- package/packages/pi/skills/remotion-best-practices/rules/compositions.md +146 -0
- package/packages/pi/skills/remotion-best-practices/rules/display-captions.md +126 -0
- package/packages/pi/skills/remotion-best-practices/rules/extract-frames.md +229 -0
- package/packages/pi/skills/remotion-best-practices/rules/fonts.md +152 -0
- package/packages/pi/skills/remotion-best-practices/rules/get-audio-duration.md +58 -0
- package/packages/pi/skills/remotion-best-practices/rules/get-video-dimensions.md +68 -0
- package/packages/pi/skills/remotion-best-practices/rules/get-video-duration.md +58 -0
- package/packages/pi/skills/remotion-best-practices/rules/gifs.md +138 -0
- package/packages/pi/skills/remotion-best-practices/rules/images.md +130 -0
- package/packages/pi/skills/remotion-best-practices/rules/import-srt-captions.md +67 -0
- package/packages/pi/skills/remotion-best-practices/rules/lottie.md +68 -0
- package/packages/pi/skills/remotion-best-practices/rules/measuring-dom-nodes.md +35 -0
- package/packages/pi/skills/remotion-best-practices/rules/measuring-text.md +143 -0
- package/packages/pi/skills/remotion-best-practices/rules/sequencing.md +106 -0
- package/packages/pi/skills/remotion-best-practices/rules/tailwind.md +11 -0
- package/packages/pi/skills/remotion-best-practices/rules/text-animations.md +20 -0
- package/packages/pi/skills/remotion-best-practices/rules/timing.md +179 -0
- package/packages/pi/skills/remotion-best-practices/rules/transcribe-captions.md +19 -0
- package/packages/pi/skills/remotion-best-practices/rules/transitions.md +122 -0
- package/packages/pi/skills/remotion-best-practices/rules/trimming.md +53 -0
- package/packages/pi/skills/remotion-best-practices/rules/videos.md +171 -0
- package/packages/pi/skills/search/SKILL.md +220 -0
- package/packages/pi/skills/spec/SKILL.md +377 -0
- package/packages/pi/skills/startup/SKILL.md +315 -0
- package/packages/pi/skills/web-architect/SKILL.md +309 -0
- package/packages/pi/skills/x-algorithm/SKILL.md +305 -0
- package/packages/pi/teams/dev-team.yaml +63 -0
- package/packages/pi/teams/gtm-team.yaml +79 -0
- package/packages/pi/themes/jfl.theme.json +76 -0
- package/packages/pi/tsconfig.json +21 -0
- package/scripts/collect-tuples.sh +124 -0
- package/scripts/destroy-fleet.sh +37 -0
- package/scripts/jfl-ide.sh +48 -0
- package/scripts/session/session-cleanup.sh +4 -11
- package/scripts/session/session-init.sh +6 -0
- package/scripts/session/session-sync.sh +25 -0
- package/scripts/setup-branch-protection.sh +106 -0
- package/scripts/spawn-fleet.sh +144 -0
- package/scripts/train-policy-head.py +434 -0
- package/scripts/vm-swarm/README.md +301 -0
- package/scripts/vm-swarm/collect-tuples.sh +331 -0
- package/scripts/vm-swarm/create-base-template.sh +339 -0
- package/scripts/vm-swarm/kill-fleet.sh +204 -0
- package/scripts/vm-swarm/monitor-fleet.sh +346 -0
- package/scripts/vm-swarm/spawn-fleet.sh +304 -0
- package/template/.github/workflows/jfl-eval.yml +6 -1
- package/template/.github/workflows/jfl-review.yml +4 -0
- package/template/scripts/session/session-end.sh +69 -6
- package/template/scripts/session/session-init.sh +55 -30
- package/template/scripts/session/session-lock.sh +464 -0
- package/template/templates/service-agent/workflows/jfl-eval.yml +19 -0
- package/dist/dashboard-static/assets/index-B6kRK9Rq.js +0 -116
- package/dist/dashboard-static/assets/index-BpdKJPLu.css +0 -1
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CRM Tool Extension
|
|
3
|
+
*
|
|
4
|
+
* Registers jfl_crm tool with custom TUI rendering.
|
|
5
|
+
* Delegates to ./crm CLI (Google Sheets backed).
|
|
6
|
+
* Renders pipeline data with color-coded deal stages.
|
|
7
|
+
*
|
|
8
|
+
* @purpose jfl_crm tool — themed CRM display with deal status colors
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { execSync } from "child_process"
|
|
12
|
+
import type { PiContext } from "./types.js"
|
|
13
|
+
import { crmRenderCall, crmRenderResult } from "./tool-renderers.js"
|
|
14
|
+
|
|
15
|
+
let projectRoot = ""
|
|
16
|
+
|
|
17
|
+
export function setupCrmTool(ctx: PiContext): void {
|
|
18
|
+
projectRoot = ctx.session.projectRoot
|
|
19
|
+
|
|
20
|
+
ctx.registerTool({
|
|
21
|
+
name: "jfl_crm",
|
|
22
|
+
description: "Query or update the JFL CRM (contacts, deals, pipeline). Delegates to ./crm CLI backed by Google Sheets.",
|
|
23
|
+
promptSnippet: "Query CRM pipeline, contacts, and deals via Google Sheets",
|
|
24
|
+
inputSchema: {
|
|
25
|
+
type: "object",
|
|
26
|
+
properties: {
|
|
27
|
+
command: {
|
|
28
|
+
type: "string",
|
|
29
|
+
description: "CRM subcommand: list, prep, stale, priority, touch, update, add",
|
|
30
|
+
},
|
|
31
|
+
args: {
|
|
32
|
+
type: "string",
|
|
33
|
+
description: "Arguments for the subcommand (e.g., contact name, field, value)",
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
required: ["command"],
|
|
37
|
+
},
|
|
38
|
+
async handler(input) {
|
|
39
|
+
const { command, args } = input as { command: string; args?: string }
|
|
40
|
+
const fullCmd = args ? `./crm ${command} ${args}` : `./crm ${command}`
|
|
41
|
+
|
|
42
|
+
try {
|
|
43
|
+
const output = execSync(fullCmd, {
|
|
44
|
+
cwd: projectRoot,
|
|
45
|
+
timeout: 15000,
|
|
46
|
+
encoding: "utf-8",
|
|
47
|
+
})
|
|
48
|
+
return output.trim()
|
|
49
|
+
} catch (err: unknown) {
|
|
50
|
+
const error = err as { message?: string; stderr?: Buffer }
|
|
51
|
+
if (error.stderr) {
|
|
52
|
+
const stderr = error.stderr.toString().trim()
|
|
53
|
+
if (stderr) return `Error: ${stderr}`
|
|
54
|
+
}
|
|
55
|
+
return `Error running crm: ${error.message ?? String(err)}`
|
|
56
|
+
}
|
|
57
|
+
},
|
|
58
|
+
renderCall: crmRenderCall,
|
|
59
|
+
renderResult: crmRenderResult,
|
|
60
|
+
})
|
|
61
|
+
}
|
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Eval Tool
|
|
3
|
+
*
|
|
4
|
+
* Exposes eval scoring and history to the agent. The agent can check
|
|
5
|
+
* project quality, view eval trends, and trigger eval runs.
|
|
6
|
+
*
|
|
7
|
+
* @purpose Pi tool for eval system — check scores, view trends, trigger evals
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { existsSync, readFileSync, readdirSync } from "fs"
|
|
11
|
+
import { join } from "path"
|
|
12
|
+
import type { PiContext, JflConfig } from "./types.js"
|
|
13
|
+
import { emitCustomEvent } from "./map-bridge.js"
|
|
14
|
+
|
|
15
|
+
let projectRoot = ""
|
|
16
|
+
|
|
17
|
+
interface EvalEntry {
|
|
18
|
+
v: number
|
|
19
|
+
ts: string
|
|
20
|
+
agent: string
|
|
21
|
+
run_id: string
|
|
22
|
+
composite: number
|
|
23
|
+
metrics: Record<string, any>
|
|
24
|
+
model_version?: string
|
|
25
|
+
branch?: string
|
|
26
|
+
pr_number?: number
|
|
27
|
+
delta?: number
|
|
28
|
+
improved?: boolean
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readEvals(): EvalEntry[] {
|
|
32
|
+
const evalPath = join(projectRoot, ".jfl", "eval", "eval.jsonl")
|
|
33
|
+
if (!existsSync(evalPath)) return []
|
|
34
|
+
const entries: EvalEntry[] = []
|
|
35
|
+
for (const line of readFileSync(evalPath, "utf-8").split("\n")) {
|
|
36
|
+
if (!line.trim()) continue
|
|
37
|
+
try { entries.push(JSON.parse(line)) } catch {}
|
|
38
|
+
}
|
|
39
|
+
return entries.sort((a, b) => (a.ts || "").localeCompare(b.ts || ""))
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function readServiceEvents(): Array<Record<string, any>> {
|
|
43
|
+
const eventsPath = join(projectRoot, ".jfl", "service-events.jsonl")
|
|
44
|
+
if (!existsSync(eventsPath)) return []
|
|
45
|
+
const entries: any[] = []
|
|
46
|
+
for (const line of readFileSync(eventsPath, "utf-8").split("\n")) {
|
|
47
|
+
if (!line.trim()) continue
|
|
48
|
+
try { entries.push(JSON.parse(line)) } catch {}
|
|
49
|
+
}
|
|
50
|
+
return entries
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export async function setupEvalTool(ctx: PiContext, _config: JflConfig): Promise<void> {
|
|
54
|
+
projectRoot = ctx.session.projectRoot
|
|
55
|
+
|
|
56
|
+
ctx.registerTool({
|
|
57
|
+
name: "jfl_eval_status",
|
|
58
|
+
description: "Get current eval status — latest scores, trends, and quality metrics. Use to understand project health before making changes.",
|
|
59
|
+
inputSchema: {
|
|
60
|
+
type: "object",
|
|
61
|
+
properties: {
|
|
62
|
+
limit: {
|
|
63
|
+
type: "string",
|
|
64
|
+
description: "Number of recent eval entries to show (default: 5)",
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
async handler(input) {
|
|
69
|
+
const { limit: limitStr } = input as { limit?: string }
|
|
70
|
+
const limit = parseInt(limitStr || "5", 10)
|
|
71
|
+
|
|
72
|
+
const evals = readEvals()
|
|
73
|
+
if (evals.length === 0) {
|
|
74
|
+
return "No eval history found. Run `jfl eval` or push a PR to trigger CI eval."
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const recent = evals.slice(-limit)
|
|
78
|
+
const latest = recent[recent.length - 1]
|
|
79
|
+
|
|
80
|
+
const lines = [
|
|
81
|
+
`Eval History: ${evals.length} total entries`,
|
|
82
|
+
"",
|
|
83
|
+
`Latest composite: ${latest.composite.toFixed(4)}`,
|
|
84
|
+
`Latest agent: ${latest.agent}`,
|
|
85
|
+
`Latest time: ${latest.ts}`,
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
if (latest.metrics) {
|
|
89
|
+
lines.push("", "Dimensions:")
|
|
90
|
+
for (const [key, val] of Object.entries(latest.metrics)) {
|
|
91
|
+
if (typeof val === "number") {
|
|
92
|
+
lines.push(` ${key}: ${val.toFixed(4)}`)
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (recent.length > 1) {
|
|
98
|
+
lines.push("", "Trend (recent):")
|
|
99
|
+
for (const e of recent) {
|
|
100
|
+
const delta = e.delta ?? 0
|
|
101
|
+
const sign = delta >= 0 ? "+" : ""
|
|
102
|
+
const improved = e.improved ? "✓" : "✗"
|
|
103
|
+
lines.push(` ${e.ts.slice(0, 16)} ${e.composite.toFixed(4)} (${sign}${delta.toFixed(4)}) ${improved} [${e.agent}]`)
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const deltas = evals.slice(-10).map(e => e.delta ?? 0)
|
|
108
|
+
const avgDelta = deltas.length > 0 ? deltas.reduce((a, b) => a + b, 0) / deltas.length : 0
|
|
109
|
+
const improving = deltas.filter(d => d > 0).length
|
|
110
|
+
lines.push(
|
|
111
|
+
"",
|
|
112
|
+
`10-run trend: avg delta ${avgDelta >= 0 ? "+" : ""}${avgDelta.toFixed(4)}, ${improving}/${deltas.length} improved`,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
return lines.join("\n")
|
|
116
|
+
},
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
ctx.registerTool({
|
|
120
|
+
name: "jfl_eval_compare",
|
|
121
|
+
description: "Compare two eval snapshots by index (0 = oldest, -1 = latest). Useful for understanding what changed between versions.",
|
|
122
|
+
inputSchema: {
|
|
123
|
+
type: "object",
|
|
124
|
+
properties: {
|
|
125
|
+
a: {
|
|
126
|
+
type: "string",
|
|
127
|
+
description: "Index of first snapshot (default: -2, second-to-last)",
|
|
128
|
+
},
|
|
129
|
+
b: {
|
|
130
|
+
type: "string",
|
|
131
|
+
description: "Index of second snapshot (default: -1, latest)",
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
},
|
|
135
|
+
async handler(input) {
|
|
136
|
+
const { a: aStr, b: bStr } = input as { a?: string; b?: string }
|
|
137
|
+
|
|
138
|
+
const evals = readEvals()
|
|
139
|
+
if (evals.length < 2) {
|
|
140
|
+
return "Need at least 2 eval entries to compare."
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
const idxA = parseInt(aStr || "-2", 10)
|
|
144
|
+
const idxB = parseInt(bStr || "-1", 10)
|
|
145
|
+
|
|
146
|
+
const resolveIdx = (idx: number) => idx < 0 ? evals.length + idx : idx
|
|
147
|
+
const evalA = evals[resolveIdx(idxA)]
|
|
148
|
+
const evalB = evals[resolveIdx(idxB)]
|
|
149
|
+
|
|
150
|
+
if (!evalA || !evalB) {
|
|
151
|
+
return `Invalid indices. Have ${evals.length} entries (0 to ${evals.length - 1}).`
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
const lines = [
|
|
155
|
+
"Eval Comparison",
|
|
156
|
+
"",
|
|
157
|
+
`A: ${evalA.ts.slice(0, 16)} (composite: ${evalA.composite.toFixed(4)}) [${evalA.agent}]`,
|
|
158
|
+
`B: ${evalB.ts.slice(0, 16)} (composite: ${evalB.composite.toFixed(4)}) [${evalB.agent}]`,
|
|
159
|
+
"",
|
|
160
|
+
`Composite delta: ${(evalB.composite - evalA.composite) >= 0 ? "+" : ""}${(evalB.composite - evalA.composite).toFixed(4)}`,
|
|
161
|
+
"",
|
|
162
|
+
"Dimension changes:",
|
|
163
|
+
]
|
|
164
|
+
|
|
165
|
+
const allKeys = new Set([
|
|
166
|
+
...Object.keys(evalA.metrics || {}),
|
|
167
|
+
...Object.keys(evalB.metrics || {}),
|
|
168
|
+
])
|
|
169
|
+
|
|
170
|
+
for (const key of allKeys) {
|
|
171
|
+
const valA = (evalA.metrics?.[key] as number) ?? 0
|
|
172
|
+
const valB = (evalB.metrics?.[key] as number) ?? 0
|
|
173
|
+
if (typeof valA !== "number" || typeof valB !== "number") continue
|
|
174
|
+
const diff = valB - valA
|
|
175
|
+
const sign = diff >= 0 ? "+" : ""
|
|
176
|
+
const arrow = diff > 0.001 ? "↑" : diff < -0.001 ? "↓" : "="
|
|
177
|
+
lines.push(` ${arrow} ${key}: ${valA.toFixed(4)} → ${valB.toFixed(4)} (${sign}${diff.toFixed(4)})`)
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return lines.join("\n")
|
|
181
|
+
},
|
|
182
|
+
})
|
|
183
|
+
|
|
184
|
+
ctx.registerCommand({
|
|
185
|
+
name: "eval",
|
|
186
|
+
description: "Show eval status and recent scores",
|
|
187
|
+
async handler(_args, ctx) {
|
|
188
|
+
const evals = readEvals()
|
|
189
|
+
if (evals.length === 0) {
|
|
190
|
+
ctx.ui.notify("No eval history. Push a PR or run jfl eval.", { level: "info" })
|
|
191
|
+
return
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
const latest = evals[evals.length - 1]
|
|
195
|
+
const recent = evals.slice(-5)
|
|
196
|
+
|
|
197
|
+
const lines = [
|
|
198
|
+
`Eval: ${evals.length} entries | Latest: ${latest.composite.toFixed(4)}`,
|
|
199
|
+
"",
|
|
200
|
+
]
|
|
201
|
+
|
|
202
|
+
for (const e of recent) {
|
|
203
|
+
const delta = e.delta ?? 0
|
|
204
|
+
const sign = delta >= 0 ? "+" : ""
|
|
205
|
+
lines.push(` ${e.ts.slice(0, 10)} ${e.composite.toFixed(4)} (${sign}${delta.toFixed(4)}) [${e.agent}]`)
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const serviceEvents = readServiceEvents()
|
|
209
|
+
const recentPRs = serviceEvents
|
|
210
|
+
.filter(e => e.type === "eval:scored")
|
|
211
|
+
.slice(-3)
|
|
212
|
+
|
|
213
|
+
if (recentPRs.length > 0) {
|
|
214
|
+
lines.push("", "Recent PR evals:")
|
|
215
|
+
for (const e of recentPRs) {
|
|
216
|
+
const d = e.data || {}
|
|
217
|
+
lines.push(` PR #${d.pr_number || "?"}: ${d.composite?.toFixed(4) || "?"} (delta: ${d.delta?.toFixed(4) || "?"})`)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
ctx.ui.notify(lines.join("\n"), { level: "info" })
|
|
222
|
+
},
|
|
223
|
+
})
|
|
224
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Eval Extension
|
|
3
|
+
*
|
|
4
|
+
* Captures agent turn metrics and writes eval entries via eval-store.ts.
|
|
5
|
+
* Emits eval:submitted to MAP bus after each captured turn.
|
|
6
|
+
*
|
|
7
|
+
* @purpose Capture per-turn eval data and emit to MAP bus
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { randomUUID } from "crypto"
|
|
11
|
+
import type { PiContext, JflConfig, AgentEndEvent } from "./types.js"
|
|
12
|
+
import { emitCustomEvent } from "./map-bridge.js"
|
|
13
|
+
|
|
14
|
+
interface EvalEntry {
|
|
15
|
+
id: string
|
|
16
|
+
session_id: string
|
|
17
|
+
ts: string
|
|
18
|
+
model?: string
|
|
19
|
+
turn_count: number
|
|
20
|
+
tools_used?: string[]
|
|
21
|
+
files_changed?: string[]
|
|
22
|
+
duration_ms?: number
|
|
23
|
+
exit_reason?: string
|
|
24
|
+
project_root: string
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
let projectRoot = ""
|
|
28
|
+
|
|
29
|
+
export async function setupEval(ctx: PiContext, _config: JflConfig): Promise<void> {
|
|
30
|
+
projectRoot = ctx.session.projectRoot
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export async function onAgentEnd(ctx: PiContext, event: AgentEndEvent): Promise<void> {
|
|
34
|
+
// Pi's AgentEndEvent has messages array; use length as turn count
|
|
35
|
+
const turnCount = event.messages?.length ?? event.turnCount ?? 0
|
|
36
|
+
if (turnCount < 1) return
|
|
37
|
+
|
|
38
|
+
const entry: EvalEntry = {
|
|
39
|
+
id: randomUUID(),
|
|
40
|
+
session_id: ctx.session.id,
|
|
41
|
+
ts: new Date().toISOString(),
|
|
42
|
+
model: event.model,
|
|
43
|
+
turn_count: turnCount,
|
|
44
|
+
tools_used: event.toolsUsed,
|
|
45
|
+
files_changed: event.filesChanged,
|
|
46
|
+
duration_ms: event.duration,
|
|
47
|
+
exit_reason: event.exitReason,
|
|
48
|
+
project_root: projectRoot,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
try {
|
|
52
|
+
// @ts-ignore — resolved from jfl package at runtime
|
|
53
|
+
const { appendEval } = await import("../../src/lib/eval-store.js")
|
|
54
|
+
appendEval(entry as Parameters<typeof appendEval>[0], projectRoot)
|
|
55
|
+
} catch {
|
|
56
|
+
// eval-store may not be available in all contexts — non-fatal
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
await emitCustomEvent(ctx, "eval:submitted", entry)
|
|
60
|
+
}
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Custom Footer Extension
|
|
3
|
+
*
|
|
4
|
+
* Rich status footer showing project name, branch, turn count, session
|
|
5
|
+
* duration, model, pipeline deals, active agents, and eval score.
|
|
6
|
+
* Reactive to branch changes and turn updates.
|
|
7
|
+
*
|
|
8
|
+
* Layout: ◆ project [branch] │ T3 2m │ model │ 3 deals │ ✓ 0.89
|
|
9
|
+
*
|
|
10
|
+
* @purpose Custom footer with live project telemetry
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { existsSync, readFileSync, readdirSync } from "fs"
|
|
14
|
+
import { join } from "path"
|
|
15
|
+
import { execSync } from "child_process"
|
|
16
|
+
import type { PiContext, PiTheme, JflConfig } from "./types.js"
|
|
17
|
+
|
|
18
|
+
interface FooterState {
|
|
19
|
+
turnCount: () => number
|
|
20
|
+
sessionStart: () => number
|
|
21
|
+
model: () => string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
let projectRoot = ""
|
|
25
|
+
let projectName = ""
|
|
26
|
+
let projectType = ""
|
|
27
|
+
let state: FooterState
|
|
28
|
+
let cachedPipelineCount: number | null = null
|
|
29
|
+
let pipelineCacheTime = 0
|
|
30
|
+
let cachedEvalScore: string | null = null
|
|
31
|
+
let evalCacheTime = 0
|
|
32
|
+
|
|
33
|
+
function formatDuration(ms: number): string {
|
|
34
|
+
const s = Math.floor(ms / 1000)
|
|
35
|
+
if (s < 60) return `${s}s`
|
|
36
|
+
const m = Math.floor(s / 60)
|
|
37
|
+
if (m < 60) return `${m}m`
|
|
38
|
+
const h = Math.floor(m / 60)
|
|
39
|
+
const rm = m % 60
|
|
40
|
+
return `${h}h${rm > 0 ? `${rm}m` : ""}`
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function getPipelineCount(): number | null {
|
|
44
|
+
const now = Date.now()
|
|
45
|
+
if (cachedPipelineCount !== null && now - pipelineCacheTime < 60000) return cachedPipelineCount
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const output = execSync("./crm list --compact 2>/dev/null | wc -l", {
|
|
49
|
+
cwd: projectRoot,
|
|
50
|
+
timeout: 3000,
|
|
51
|
+
encoding: "utf-8",
|
|
52
|
+
}).trim()
|
|
53
|
+
cachedPipelineCount = parseInt(output, 10) || 0
|
|
54
|
+
pipelineCacheTime = now
|
|
55
|
+
return cachedPipelineCount
|
|
56
|
+
} catch {
|
|
57
|
+
return cachedPipelineCount
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function getActiveAgentCount(): number {
|
|
62
|
+
try {
|
|
63
|
+
const journalDir = join(projectRoot, ".jfl", "journal")
|
|
64
|
+
if (!existsSync(journalDir)) return 0
|
|
65
|
+
const files = readdirSync(journalDir).filter(f => f.startsWith("session-") && f.endsWith(".jsonl"))
|
|
66
|
+
const now = Date.now()
|
|
67
|
+
let active = 0
|
|
68
|
+
for (const f of files) {
|
|
69
|
+
try {
|
|
70
|
+
const stat = require("fs").statSync(join(journalDir, f))
|
|
71
|
+
if (now - stat.mtimeMs < 300000) active++
|
|
72
|
+
} catch {}
|
|
73
|
+
}
|
|
74
|
+
return active
|
|
75
|
+
} catch {
|
|
76
|
+
return 0
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function getEvalScore(): string | null {
|
|
81
|
+
const now = Date.now()
|
|
82
|
+
if (cachedEvalScore !== null && now - evalCacheTime < 30000) return cachedEvalScore
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
const evalPath = join(projectRoot, ".jfl", "eval-store.jsonl")
|
|
86
|
+
if (!existsSync(evalPath)) return null
|
|
87
|
+
const lines = readFileSync(evalPath, "utf-8").trim().split("\n").filter(Boolean)
|
|
88
|
+
if (lines.length === 0) return null
|
|
89
|
+
const last = JSON.parse(lines[lines.length - 1])
|
|
90
|
+
if (last.composite !== undefined) {
|
|
91
|
+
cachedEvalScore = last.composite.toFixed(2)
|
|
92
|
+
evalCacheTime = now
|
|
93
|
+
return cachedEvalScore
|
|
94
|
+
}
|
|
95
|
+
return null
|
|
96
|
+
} catch {
|
|
97
|
+
return cachedEvalScore
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function getJournalCount(): number {
|
|
102
|
+
try {
|
|
103
|
+
const journalDir = join(projectRoot, ".jfl", "journal")
|
|
104
|
+
if (!existsSync(journalDir)) return 0
|
|
105
|
+
let count = 0
|
|
106
|
+
for (const f of readdirSync(journalDir).filter(f => f.endsWith(".jsonl"))) {
|
|
107
|
+
const content = readFileSync(join(journalDir, f), "utf-8").trim()
|
|
108
|
+
count += content.split("\n").filter(Boolean).length
|
|
109
|
+
}
|
|
110
|
+
return count
|
|
111
|
+
} catch {
|
|
112
|
+
return 0
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export function setupFooter(
|
|
117
|
+
ctx: PiContext,
|
|
118
|
+
config: JflConfig,
|
|
119
|
+
footerState: FooterState
|
|
120
|
+
): void {
|
|
121
|
+
projectRoot = ctx.session.projectRoot
|
|
122
|
+
state = footerState
|
|
123
|
+
|
|
124
|
+
const configPath = join(projectRoot, ".jfl", "config.json")
|
|
125
|
+
if (existsSync(configPath)) {
|
|
126
|
+
try {
|
|
127
|
+
const cfg = JSON.parse(readFileSync(configPath, "utf-8"))
|
|
128
|
+
projectName = cfg.name ?? projectRoot.split("/").pop() ?? "JFL"
|
|
129
|
+
projectType = cfg.type ?? "gtm"
|
|
130
|
+
} catch {
|
|
131
|
+
projectName = projectRoot.split("/").pop() ?? "JFL"
|
|
132
|
+
projectType = "gtm"
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
projectName = projectRoot.split("/").pop() ?? "JFL"
|
|
136
|
+
projectType = "gtm"
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
if (config.pi?.disable_footer) return
|
|
140
|
+
|
|
141
|
+
ctx.ui.setFooter((tui: any, theme: PiTheme, footerData: any) => {
|
|
142
|
+
let disposed = false
|
|
143
|
+
const unsub = footerData?.onBranchChange?.(() => {
|
|
144
|
+
if (!disposed) tui.requestRender()
|
|
145
|
+
})
|
|
146
|
+
|
|
147
|
+
return {
|
|
148
|
+
dispose: () => {
|
|
149
|
+
disposed = true
|
|
150
|
+
if (typeof unsub === "function") unsub()
|
|
151
|
+
},
|
|
152
|
+
|
|
153
|
+
invalidate() {},
|
|
154
|
+
|
|
155
|
+
render(width: number): string[] {
|
|
156
|
+
const turns = state.turnCount()
|
|
157
|
+
const elapsed = formatDuration(Date.now() - state.sessionStart())
|
|
158
|
+
const model = state.model()
|
|
159
|
+
const branch = footerData?.getGitBranch?.() ?? ctx.session.branch
|
|
160
|
+
|
|
161
|
+
// ─── Left side: project identity + session info ───────────────
|
|
162
|
+
const bullet = theme.fg("accent", "◆")
|
|
163
|
+
const name = theme.fg("text", projectName)
|
|
164
|
+
const branchStr = theme.fg("muted", `[${branch}]`)
|
|
165
|
+
const turnStr = theme.fg("dim", `T${turns}`)
|
|
166
|
+
const timeStr = theme.fg("dim", elapsed)
|
|
167
|
+
const modelStr = model ? theme.fg("muted", model.split("/").pop() ?? model) : ""
|
|
168
|
+
|
|
169
|
+
const leftParts = [
|
|
170
|
+
`${bullet} ${name} ${branchStr}`,
|
|
171
|
+
`${turnStr} ${timeStr}`,
|
|
172
|
+
modelStr,
|
|
173
|
+
].filter(Boolean)
|
|
174
|
+
|
|
175
|
+
const left = leftParts.join(theme.fg("dim", " │ "))
|
|
176
|
+
|
|
177
|
+
// ─── Right side: pipeline + agents + eval ─────────────────────
|
|
178
|
+
const rightParts: string[] = []
|
|
179
|
+
|
|
180
|
+
const pipeline = getPipelineCount()
|
|
181
|
+
if (pipeline !== null && pipeline > 0) {
|
|
182
|
+
rightParts.push(theme.fg("accent", `${pipeline} deal${pipeline !== 1 ? "s" : ""}`))
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const agents = getActiveAgentCount()
|
|
186
|
+
if (agents > 0) {
|
|
187
|
+
rightParts.push(theme.fg("warning", `${agents} agent${agents !== 1 ? "s" : ""}`))
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
const journals = getJournalCount()
|
|
191
|
+
if (journals > 0) {
|
|
192
|
+
rightParts.push(theme.fg("dim", `${journals}j`))
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const evalScore = getEvalScore()
|
|
196
|
+
if (evalScore) {
|
|
197
|
+
const score = parseFloat(evalScore)
|
|
198
|
+
const color = score >= 0.8 ? "success" : score >= 0.5 ? "warning" : "error"
|
|
199
|
+
rightParts.push(theme.fg(color, `✓${evalScore}`))
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const right = rightParts.join(theme.fg("dim", " │ "))
|
|
203
|
+
|
|
204
|
+
// ─── Compose line ─────────────────────────────────────────────
|
|
205
|
+
const leftLen = stripAnsi(left).length
|
|
206
|
+
const rightLen = stripAnsi(right).length
|
|
207
|
+
const gap = Math.max(1, width - leftLen - rightLen)
|
|
208
|
+
|
|
209
|
+
const line = left + " ".repeat(gap) + right
|
|
210
|
+
return [truncateVisible(line, width)]
|
|
211
|
+
},
|
|
212
|
+
}
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
ctx.on("turn:start", () => {
|
|
216
|
+
// Footer auto-rerenders via requestRender when data changes
|
|
217
|
+
})
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function stripAnsi(str: string): string {
|
|
221
|
+
return str.replace(/\x1b\[[0-9;]*m/g, "")
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function truncateVisible(str: string, maxWidth: number): string {
|
|
225
|
+
const visible = stripAnsi(str)
|
|
226
|
+
if (visible.length <= maxWidth) return str
|
|
227
|
+
// Rough truncation — find where visible chars exceed width
|
|
228
|
+
let visCount = 0
|
|
229
|
+
let i = 0
|
|
230
|
+
while (i < str.length && visCount < maxWidth - 1) {
|
|
231
|
+
if (str[i] === "\x1b") {
|
|
232
|
+
const end = str.indexOf("m", i)
|
|
233
|
+
if (end !== -1) { i = end + 1; continue }
|
|
234
|
+
}
|
|
235
|
+
visCount++
|
|
236
|
+
i++
|
|
237
|
+
}
|
|
238
|
+
return str.slice(0, i) + "…"
|
|
239
|
+
}
|