jfl 0.3.0 → 0.4.2
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/README.md +294 -30
- package/dist/commands/context-hub.d.ts.map +1 -1
- package/dist/commands/context-hub.js +154 -0
- package/dist/commands/context-hub.js.map +1 -1
- package/dist/commands/flows.d.ts +4 -1
- package/dist/commands/flows.d.ts.map +1 -1
- package/dist/commands/flows.js +160 -1
- package/dist/commands/flows.js.map +1 -1
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +42 -0
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/peter.d.ts.map +1 -1
- package/dist/commands/peter.js +220 -1
- package/dist/commands/peter.js.map +1 -1
- package/dist/commands/pi.d.ts +21 -0
- package/dist/commands/pi.d.ts.map +1 -0
- package/dist/commands/pi.js +154 -0
- package/dist/commands/pi.js.map +1 -0
- package/dist/commands/portfolio.d.ts.map +1 -1
- package/dist/commands/portfolio.js +22 -69
- package/dist/commands/portfolio.js.map +1 -1
- package/dist/commands/predict.d.ts +6 -0
- package/dist/commands/predict.d.ts.map +1 -0
- package/dist/commands/predict.js +234 -0
- package/dist/commands/predict.js.map +1 -0
- package/dist/commands/synopsis.d.ts +44 -0
- package/dist/commands/synopsis.d.ts.map +1 -1
- package/dist/commands/synopsis.js +1 -1
- package/dist/commands/synopsis.js.map +1 -1
- package/dist/commands/update.d.ts.map +1 -1
- package/dist/commands/update.js +49 -1
- package/dist/commands/update.js.map +1 -1
- package/dist/commands/viz.d.ts +7 -0
- package/dist/commands/viz.d.ts.map +1 -0
- package/dist/commands/viz.js +460 -0
- package/dist/commands/viz.js.map +1 -0
- package/dist/dashboard/index.d.ts +4 -5
- package/dist/dashboard/index.d.ts.map +1 -1
- package/dist/dashboard/index.js +57 -146
- package/dist/dashboard/index.js.map +1 -1
- package/dist/dashboard-static/assets/index-B6kRK9Rq.js +116 -0
- package/dist/dashboard-static/assets/index-BpdKJPLu.css +1 -0
- package/dist/dashboard-static/index.html +16 -0
- package/dist/index.js +112 -19
- package/dist/index.js.map +1 -1
- package/dist/lib/flow-engine.d.ts +1 -0
- package/dist/lib/flow-engine.d.ts.map +1 -1
- package/dist/lib/flow-engine.js +30 -1
- package/dist/lib/flow-engine.js.map +1 -1
- package/dist/lib/hub-client.d.ts +80 -0
- package/dist/lib/hub-client.d.ts.map +1 -0
- package/dist/lib/hub-client.js +46 -0
- package/dist/lib/hub-client.js.map +1 -0
- package/dist/lib/predictor.d.ts +99 -0
- package/dist/lib/predictor.d.ts.map +1 -0
- package/dist/lib/predictor.js +394 -0
- package/dist/lib/predictor.js.map +1 -0
- package/dist/lib/service-gtm.d.ts +86 -51
- package/dist/lib/service-gtm.d.ts.map +1 -1
- package/dist/lib/service-gtm.js +417 -242
- package/dist/lib/service-gtm.js.map +1 -1
- package/dist/lib/telemetry-agent.d.ts +57 -0
- package/dist/lib/telemetry-agent.d.ts.map +1 -0
- package/dist/lib/telemetry-agent.js +268 -0
- package/dist/lib/telemetry-agent.js.map +1 -0
- package/dist/lib/telemetry-digest.d.ts.map +1 -1
- package/dist/lib/telemetry-digest.js +17 -17
- package/dist/lib/telemetry-digest.js.map +1 -1
- package/dist/lib/telemetry.d.ts +1 -0
- package/dist/lib/telemetry.d.ts.map +1 -1
- package/dist/lib/telemetry.js +14 -6
- package/dist/lib/telemetry.js.map +1 -1
- package/dist/mcp/context-hub-mcp.js +0 -0
- package/dist/mcp/service-registry-mcp.js +0 -0
- package/dist/types/map.d.ts +1 -1
- package/dist/types/map.d.ts.map +1 -1
- package/dist/types/map.js.map +1 -1
- package/dist/utils/jfl-paths.d.ts +1 -0
- package/dist/utils/jfl-paths.d.ts.map +1 -1
- package/dist/utils/jfl-paths.js +1 -0
- package/dist/utils/jfl-paths.js.map +1 -1
- package/package.json +7 -2
- package/scripts/generate-changesets.sh +113 -0
- package/scripts/pp-branch-pr.sh +115 -0
- package/template/.jfl/flows-self-driving.yaml +170 -0
- package/dist/dashboard/components.d.ts +0 -7
- package/dist/dashboard/components.d.ts.map +0 -1
- package/dist/dashboard/components.js +0 -575
- package/dist/dashboard/components.js.map +0 -1
- package/dist/dashboard/pages.d.ts +0 -7
- package/dist/dashboard/pages.d.ts.map +0 -1
- package/dist/dashboard/pages.js +0 -1580
- package/dist/dashboard/pages.js.map +0 -1
- package/dist/dashboard/styles.d.ts +0 -7
- package/dist/dashboard/styles.d.ts.map +0 -1
- package/dist/dashboard/styles.js +0 -1110
- package/dist/dashboard/styles.js.map +0 -1
package/README.md
CHANGED
|
@@ -55,28 +55,37 @@ That's it. SessionStart hooks handle repo sync, session branching, Context Hub s
|
|
|
55
55
|
|
|
56
56
|
## Architecture
|
|
57
57
|
|
|
58
|
-
JFL
|
|
58
|
+
JFL supports a three-level hierarchy: **Portfolio > GTM > Services**. Portfolios coordinate multiple products. GTMs are context layers for individual products. Services are the repos that do the actual work.
|
|
59
59
|
|
|
60
60
|
```
|
|
61
|
-
|
|
61
|
+
visa-portfolio/ <- Portfolio (strategy, cross-product RL, data flow)
|
|
62
62
|
├── .jfl/
|
|
63
|
-
│ ├── config.json <-
|
|
64
|
-
│ ├──
|
|
65
|
-
│ ├──
|
|
66
|
-
│
|
|
67
|
-
│
|
|
68
|
-
|
|
69
|
-
│
|
|
70
|
-
├──
|
|
71
|
-
├──
|
|
72
|
-
├──
|
|
73
|
-
├──
|
|
74
|
-
│ ├──
|
|
75
|
-
│
|
|
76
|
-
│
|
|
77
|
-
├──
|
|
78
|
-
├──
|
|
79
|
-
|
|
63
|
+
│ ├── config.json <- type: "portfolio", registered child GTMs
|
|
64
|
+
│ ├── eval.jsonl <- Aggregated eval data from all children
|
|
65
|
+
│ ├── flows.yaml <- Cross-product event routing
|
|
66
|
+
│ └── journal/ <- Portfolio-level + synced child journals
|
|
67
|
+
│
|
|
68
|
+
├── productrank-gtm/ <- GTM workspace (registered as child)
|
|
69
|
+
│ ├── .jfl/
|
|
70
|
+
│ │ ├── config.json <- type: "gtm", portfolio_parent, registered services
|
|
71
|
+
│ │ ├── eval.jsonl <- Eval entries from arena competitions
|
|
72
|
+
│ │ ├── journal/ <- Session journals + synced service journals
|
|
73
|
+
│ │ ├── agents/ <- Agent manifests + policies
|
|
74
|
+
│ │ ├── flows/ <- Per-agent flow definitions
|
|
75
|
+
│ │ └── service-events.jsonl
|
|
76
|
+
│ ├── knowledge/ <- Strategy docs (VISION, ROADMAP, THESIS, etc.)
|
|
77
|
+
│ ├── content/ <- Generated content
|
|
78
|
+
│ ├── suggestions/ <- Per-contributor workspaces
|
|
79
|
+
│ ├── .claude/
|
|
80
|
+
│ │ ├── settings.json <- Claude Code hooks
|
|
81
|
+
│ │ ├── agents/ <- Service agent definitions
|
|
82
|
+
│ │ └── skills/ <- Slash commands (/hud, /content, etc.)
|
|
83
|
+
│ ├── scripts/session/ <- Session management
|
|
84
|
+
│ ├── CLAUDE.md <- AI instructions
|
|
85
|
+
│ └── .mcp.json <- MCP server config
|
|
86
|
+
│
|
|
87
|
+
└── seo-agent/ <- Another GTM (registered as child)
|
|
88
|
+
└── ...
|
|
80
89
|
|
|
81
90
|
my-api/ <- Service repo (registered in GTM)
|
|
82
91
|
├── src/
|
|
@@ -89,7 +98,8 @@ my-api/ <- Service repo (registered in GTM)
|
|
|
89
98
|
- Services work independently
|
|
90
99
|
- Multiple services register to one GTM
|
|
91
100
|
- `jfl update` updates tooling without touching service code
|
|
92
|
-
-
|
|
101
|
+
- Eval data dual-writes up the chain (service > GTM > portfolio)
|
|
102
|
+
- Cross-product event routing at portfolio level
|
|
93
103
|
|
|
94
104
|
---
|
|
95
105
|
|
|
@@ -106,14 +116,14 @@ jfl context-hub stop # Stop daemon
|
|
|
106
116
|
jfl context-hub restart # Restart daemon
|
|
107
117
|
jfl context-hub doctor # Diagnose all projects (OK/ZOMBIE/DOWN/STALE)
|
|
108
118
|
jfl context-hub ensure-all # Start for all GTM projects
|
|
109
|
-
jfl context-hub dashboard #
|
|
119
|
+
jfl context-hub dashboard # Open web dashboard (opens browser)
|
|
110
120
|
jfl context-hub install-daemon # Auto-start on boot (launchd/systemd)
|
|
111
121
|
jfl context-hub uninstall-daemon # Remove auto-start
|
|
112
122
|
jfl context-hub query # Query context from CLI
|
|
113
123
|
jfl context-hub serve # Run in foreground (daemon mode)
|
|
114
124
|
```
|
|
115
125
|
|
|
116
|
-
**Per-project ports** assigned automatically (or set in `.jfl/config.json`
|
|
126
|
+
**Per-project ports** assigned automatically (or set in `.jfl/config.json` > `contextHub.port`).
|
|
117
127
|
|
|
118
128
|
**MCP Tools** (available to Claude Code and any MCP client):
|
|
119
129
|
|
|
@@ -126,9 +136,32 @@ jfl context-hub serve # Run in foreground (daemon mode)
|
|
|
126
136
|
| `memory_search` | Search indexed journal memories |
|
|
127
137
|
| `memory_status` | Memory system statistics |
|
|
128
138
|
| `memory_add` | Add manual memory entry |
|
|
139
|
+
| `query_experiment_history` | Query RL trajectories for agent experiments |
|
|
129
140
|
|
|
130
141
|
**Resilience:** 5-layer system — MCP auto-recovery on ECONNREFUSED, health-check-before-ensure hooks, `ensure-all` for batch startup, `doctor` diagnostics, launchd/systemd daemon with keepalive.
|
|
131
142
|
|
|
143
|
+
### Dashboard V2
|
|
144
|
+
|
|
145
|
+
A pre-built Vite + Preact + Tailwind SPA served by Context Hub at `/dashboard/`. Auto-detects workspace type and adapts layout.
|
|
146
|
+
|
|
147
|
+
**Pages:**
|
|
148
|
+
|
|
149
|
+
| Page | What It Shows |
|
|
150
|
+
|------|--------------|
|
|
151
|
+
| **Overview** | Activity charts, product cards, metric cards |
|
|
152
|
+
| **Journal** | Searchable journal entries with type filters |
|
|
153
|
+
| **Events** | Live event feed with pattern filter presets (eval, session, flow, etc.) |
|
|
154
|
+
| **Services** | Registered services with type badges, context scope visualization, data flows |
|
|
155
|
+
| **Flows** | Flow definitions and execution history |
|
|
156
|
+
| **Health** | System metrics, context sources, memory index, tracked projects |
|
|
157
|
+
| **Agents** | Eval leaderboards grouped by product domain |
|
|
158
|
+
|
|
159
|
+
**Features:** Sidebar with structured sections (Workspace / Infra / Eval), inline SVG icons, agent leaderboard in sidebar, sparkline charts, real-time polling.
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
jfl context-hub dashboard # Opens /dashboard/ in browser
|
|
163
|
+
```
|
|
164
|
+
|
|
132
165
|
### MAP Event Bus
|
|
133
166
|
|
|
134
167
|
Metrics, Agents, Pipeline — an in-process event bus inside Context Hub.
|
|
@@ -138,10 +171,126 @@ Metrics, Agents, Pipeline — an in-process event bus inside Context Hub.
|
|
|
138
171
|
- **Journal bridge** — watches `.jfl/journal/`, emits events on new entries
|
|
139
172
|
- **Pattern-matching subscriptions** (glob support)
|
|
140
173
|
- **Transports:** SSE, WebSocket, HTTP polling
|
|
141
|
-
- **
|
|
174
|
+
- **Cross-product routing** — portfolio flows route events between child GTMs
|
|
175
|
+
- **Event types:** `session:started`, `session:ended`, `eval:scored`, `journal:entry`, `flow:triggered`, `agent:iteration-complete`, `portfolio:phone-home`, and more
|
|
142
176
|
|
|
143
177
|
Services emit events by appending to `.jfl/service-events.jsonl` — no auth needed, Context Hub watches the file automatically.
|
|
144
178
|
|
|
179
|
+
### Eval Framework
|
|
180
|
+
|
|
181
|
+
Track agent performance over time. Eval entries dual-write up the parent chain (service > GTM > portfolio) so every level has visibility.
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
jfl eval list # List recent eval entries
|
|
185
|
+
jfl eval list -a shadow # Filter by agent
|
|
186
|
+
jfl eval trajectory -a shadow # Composite score over time (with sparkline)
|
|
187
|
+
jfl eval log -a shadow -m '{"composite":0.69}' # Log an eval entry
|
|
188
|
+
jfl eval compare # Side-by-side agent comparison
|
|
189
|
+
jfl eval tuples # Extract (state, action, reward) training tuples
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**Eval entries** are JSONL with agent name, metrics, composite score, model version, and deltas:
|
|
193
|
+
|
|
194
|
+
```json
|
|
195
|
+
{
|
|
196
|
+
"v": 1, "ts": "2026-03-05T15:22:47Z",
|
|
197
|
+
"agent": "productrank-shadow",
|
|
198
|
+
"dataset": "vibe-50-v1",
|
|
199
|
+
"model_version": "shadow-0.3.1",
|
|
200
|
+
"metrics": {"ndcg@10": 0.59, "mrr": 0.77, "precision@5": 0.43},
|
|
201
|
+
"composite": 0.6935,
|
|
202
|
+
"delta": {"composite": -0.029}
|
|
203
|
+
}
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
**Leaderboard:** Agents grouped by metric domain. ProductRank agents scored on ndcg@10, mrr, precision@5. SEO agents scored on avg_rank, keywords_ranked. Dashboard Agents page shows leaderboards per domain.
|
|
207
|
+
|
|
208
|
+
**Training tuples** extracted from journals for fine-tuning: `(state, action, reward)` — maps codebase state + experiment action to eval score delta.
|
|
209
|
+
|
|
210
|
+
**API endpoints** on Context Hub:
|
|
211
|
+
- `GET /api/eval/leaderboard` — all agents ranked by composite
|
|
212
|
+
- `GET /api/eval/trajectory?agent=X&metric=composite` — score trajectory with timestamps
|
|
213
|
+
|
|
214
|
+
### RL Infrastructure
|
|
215
|
+
|
|
216
|
+
JFL generalizes the Karpathy nanochat pattern: structured journals are the replay buffer, eval scores are rewards, agents learn in-context from past trajectories.
|
|
217
|
+
|
|
218
|
+
```
|
|
219
|
+
Agent LLM (Policy) > reads trajectories, proposes experiments
|
|
220
|
+
Stratus (World Model) > predicts outcomes, filters bad proposals
|
|
221
|
+
Journals (Replay Buffer) > structured experiment history
|
|
222
|
+
Eval Framework (Reward) > composite scores, score deltas
|
|
223
|
+
Event Bus (Nervous System) > connects everything
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
**JournalEntry type** — canonical schema with 6 RL fields: `hypothesis`, `outcome`, `score_delta`, `eval_snapshot`, `diff_hash`, `context_entries`.
|
|
227
|
+
|
|
228
|
+
**TrajectoryLoader** — query, filter, and render experiment trajectories for agent context windows. Supports filtering by session, agent, outcome, score range.
|
|
229
|
+
|
|
230
|
+
**Peter Parker** — model-routed orchestrator with cost/balanced/quality profiles. Routes tasks to haiku/sonnet/opus based on complexity. Subscribes to event bus for reactive dispatch.
|
|
231
|
+
|
|
232
|
+
**Flow Engine** — declarative trigger-action automation in `.jfl/flows.yaml`:
|
|
233
|
+
|
|
234
|
+
```yaml
|
|
235
|
+
- name: eval-scored-trigger-analysis
|
|
236
|
+
trigger:
|
|
237
|
+
pattern: "eval:scored"
|
|
238
|
+
gate:
|
|
239
|
+
requires_approval: true
|
|
240
|
+
actions:
|
|
241
|
+
- type: spawn
|
|
242
|
+
command: "claude -p 'Analyze the latest eval results'"
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
Flow actions: `log`, `emit`, `journal`, `webhook`, `command`, `spawn`. Gates: `after` (time-gated), `before` (deadline), `requires_approval`.
|
|
246
|
+
|
|
247
|
+
**MCP tool:** `query_experiment_history` — agents query past experiment trajectories to inform next proposals.
|
|
248
|
+
|
|
249
|
+
### Portfolio Management
|
|
250
|
+
|
|
251
|
+
Coordinate multiple GTM workspaces under one portfolio.
|
|
252
|
+
|
|
253
|
+
```bash
|
|
254
|
+
jfl portfolio register /path/to/gtm # Register a GTM in this portfolio
|
|
255
|
+
jfl portfolio list # List child GTMs with health
|
|
256
|
+
jfl portfolio unregister <name> # Remove a GTM
|
|
257
|
+
jfl portfolio status # Portfolio health + eval summary
|
|
258
|
+
jfl portfolio phone-home # Report GTM health to portfolio parent
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
**Portfolio Context Hub** operates in fan-out mode:
|
|
262
|
+
- Connects to child GTM hubs via SSE
|
|
263
|
+
- Bridges child events into portfolio event bus
|
|
264
|
+
- Fans out search queries across all child hubs
|
|
265
|
+
- Aggregates eval leaderboard across products
|
|
266
|
+
- Enforces context scope (produces/consumes/denied) between GTMs
|
|
267
|
+
|
|
268
|
+
**Cross-product flows** defined in `.jfl/flows.yaml`:
|
|
269
|
+
|
|
270
|
+
```yaml
|
|
271
|
+
- name: tool-trends-to-seo
|
|
272
|
+
trigger:
|
|
273
|
+
pattern: "discovery:tool-trend"
|
|
274
|
+
source: "productrank-gtm"
|
|
275
|
+
actions:
|
|
276
|
+
- type: webhook
|
|
277
|
+
url: "http://localhost:{{child.seo-agent.port}}/api/events"
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
Template variables: `{{child.NAME.port}}`, `{{child.NAME.token}}`
|
|
281
|
+
|
|
282
|
+
**Context scope** — each child GTM declares what events it produces and consumes. Portfolio enforces boundaries:
|
|
283
|
+
|
|
284
|
+
```json
|
|
285
|
+
{
|
|
286
|
+
"context_scope": {
|
|
287
|
+
"produces": ["discovery:tool-trend", "eval:*"],
|
|
288
|
+
"consumes": ["strategy:*", "seo:serp-data"],
|
|
289
|
+
"denied": []
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
145
294
|
### Memory System
|
|
146
295
|
|
|
147
296
|
Hybrid search over all journal entries with TF-IDF (40%) + semantic embeddings (60%).
|
|
@@ -164,6 +313,7 @@ Automatic session isolation for parallel work:
|
|
|
164
313
|
- **Multiple concurrent sessions:** Isolated git worktrees prevent conflicts
|
|
165
314
|
- **Auto-commit:** Saves work every 2 minutes (knowledge, journal, suggestions)
|
|
166
315
|
- **Crash recovery:** Detects uncommitted work in stale sessions, auto-commits on next start
|
|
316
|
+
- **Cleanup guard:** Prevents `rm -rf` on main branch when no worktrees exist
|
|
167
317
|
|
|
168
318
|
```bash
|
|
169
319
|
# Hooks handle everything automatically. Manual control:
|
|
@@ -212,6 +362,18 @@ jfl services # Interactive TUI (no args)
|
|
|
212
362
|
- Service entry in `.jfl/services.json`
|
|
213
363
|
- Config in service repo (`.jfl/config.json` with `gtm_parent`)
|
|
214
364
|
|
|
365
|
+
**Context scoping:** Each service declares what events it produces and consumes. The GTM enforces scope — teams can't read each other's journals unless explicitly granted.
|
|
366
|
+
|
|
367
|
+
```json
|
|
368
|
+
{
|
|
369
|
+
"context_scope": {
|
|
370
|
+
"produces": ["eval:submission", "journal:my-team*"],
|
|
371
|
+
"consumes": ["eval:scored", "leaderboard:updated"],
|
|
372
|
+
"denied": ["journal:other-team*"]
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
215
377
|
**Phone-home on session end:** When a service session ends, it syncs to the parent GTM:
|
|
216
378
|
- Journal entries copied to `GTM/.jfl/journal/service-{name}-*.jsonl`
|
|
217
379
|
- Comprehensive sync payload (git stats, health, environment)
|
|
@@ -254,12 +416,32 @@ jfl services # Interactive TUI (no args)
|
|
|
254
416
|
| `jfl context-hub status` | Health check |
|
|
255
417
|
| `jfl context-hub doctor [--clean]` | Diagnose all projects |
|
|
256
418
|
| `jfl context-hub ensure-all` | Start for all GTM projects |
|
|
257
|
-
| `jfl context-hub dashboard` |
|
|
419
|
+
| `jfl context-hub dashboard` | Open web dashboard in browser |
|
|
258
420
|
| `jfl context-hub query` | Query context from CLI |
|
|
259
421
|
| `jfl context-hub serve` | Run in foreground (daemon mode) |
|
|
260
422
|
| `jfl context-hub install-daemon` | Auto-start on boot |
|
|
261
423
|
| `jfl context-hub uninstall-daemon` | Remove auto-start |
|
|
262
424
|
|
|
425
|
+
### Eval Framework
|
|
426
|
+
|
|
427
|
+
| Command | Description |
|
|
428
|
+
|---------|-------------|
|
|
429
|
+
| `jfl eval list [-a agent] [-l limit]` | List recent eval entries |
|
|
430
|
+
| `jfl eval trajectory -a <agent>` | Composite score trajectory with sparkline |
|
|
431
|
+
| `jfl eval log -a <agent> -m <metrics>` | Log an eval entry |
|
|
432
|
+
| `jfl eval compare` | Side-by-side agent comparison |
|
|
433
|
+
| `jfl eval tuples [--limit N] [--format json]` | Extract training tuples from journals |
|
|
434
|
+
|
|
435
|
+
### Portfolio
|
|
436
|
+
|
|
437
|
+
| Command | Description |
|
|
438
|
+
|---------|-------------|
|
|
439
|
+
| `jfl portfolio register <path>` | Register GTM workspace in portfolio |
|
|
440
|
+
| `jfl portfolio list` | List child GTMs with health status |
|
|
441
|
+
| `jfl portfolio unregister <name>` | Remove GTM from portfolio |
|
|
442
|
+
| `jfl portfolio status` | Portfolio health and eval summary |
|
|
443
|
+
| `jfl portfolio phone-home` | Report GTM health to portfolio parent |
|
|
444
|
+
|
|
263
445
|
### Memory
|
|
264
446
|
|
|
265
447
|
| Command | Description |
|
|
@@ -302,6 +484,22 @@ jfl services # Interactive TUI (no args)
|
|
|
302
484
|
| `jfl dashboard` | Interactive service monitoring TUI |
|
|
303
485
|
| `jfl events [-p pattern]` | Live MAP event bus dashboard |
|
|
304
486
|
|
|
487
|
+
### Hooks & Flows
|
|
488
|
+
|
|
489
|
+
| Command | Description |
|
|
490
|
+
|---------|-------------|
|
|
491
|
+
| `jfl hooks init` | Generate HTTP hooks + default flows |
|
|
492
|
+
| `jfl hooks status` | Check hooks and hub connectivity |
|
|
493
|
+
| `jfl hooks remove` | Remove HTTP hooks |
|
|
494
|
+
| `jfl hooks deploy` | Deploy hooks to all registered services |
|
|
495
|
+
| `jfl flows list` | List configured event-action flows |
|
|
496
|
+
| `jfl flows add` | Interactive flow builder |
|
|
497
|
+
| `jfl flows test <name>` | Test a flow with synthetic event |
|
|
498
|
+
| `jfl flows enable/disable <name>` | Toggle flows |
|
|
499
|
+
| `jfl scope list` | View service context scopes |
|
|
500
|
+
| `jfl scope set` | Set scope declarations |
|
|
501
|
+
| `jfl scope test` | Test scope enforcement |
|
|
502
|
+
|
|
305
503
|
### Platform
|
|
306
504
|
|
|
307
505
|
| Command | Description |
|
|
@@ -319,7 +517,7 @@ jfl services # Interactive TUI (no args)
|
|
|
319
517
|
|---------|-------------|
|
|
320
518
|
| `jfl telemetry status` | Show telemetry status |
|
|
321
519
|
| `jfl telemetry show` | Show queued events |
|
|
322
|
-
| `jfl telemetry digest [--hours N] [--format json] [--
|
|
520
|
+
| `jfl telemetry digest [--hours N] [--format json] [--plots]` | Cost breakdown, health analysis, terminal charts |
|
|
323
521
|
| `jfl telemetry reset` | Reset install ID |
|
|
324
522
|
| `jfl telemetry track --category <c> --event <e>` | Emit event from shell scripts |
|
|
325
523
|
| `jfl improve [--dry-run] [--auto] [--hours N]` | Self-improvement loop: analyze, suggest, create issues |
|
|
@@ -327,7 +525,7 @@ jfl services # Interactive TUI (no args)
|
|
|
327
525
|
|
|
328
526
|
**Model cost tracking:** Every Stratus API call emits token counts and estimated cost. Covers claude-opus-4-6, claude-sonnet-4-6, claude-sonnet-4-5, claude-haiku-3-5, gpt-4o.
|
|
329
527
|
|
|
330
|
-
**`jfl telemetry digest`** analyzes local events: per-model cost tables, command stats, error rates, hub/memory/session health.
|
|
528
|
+
**`jfl telemetry digest`** analyzes local events: per-model cost tables, command stats, error rates, hub/memory/session health. `--plots` renders bar charts via kuva (falls back to ASCII).
|
|
331
529
|
|
|
332
530
|
**`jfl improve`** generates actionable suggestions from the digest. `--dry-run` previews, `--auto` creates GitHub issues tagged `[jfl-improve]`.
|
|
333
531
|
|
|
@@ -461,14 +659,16 @@ Every session MUST write journal entries. Hooks enforce this.
|
|
|
461
659
|
"summary": "Built jfl onboard command that registers service repos in GTM",
|
|
462
660
|
"detail": "Creates agent definition, skill wrapper, services.json entry...",
|
|
463
661
|
"files": ["src/commands/onboard.ts"],
|
|
464
|
-
"
|
|
465
|
-
"
|
|
662
|
+
"hypothesis": "Structured onboarding reduces setup errors",
|
|
663
|
+
"outcome": "confirmed",
|
|
664
|
+
"score_delta": 0.12,
|
|
665
|
+
"eval_snapshot": {"composite": 0.85}
|
|
466
666
|
}
|
|
467
667
|
```
|
|
468
668
|
|
|
469
669
|
**Write entries when:** Feature completed, decision made, bug fixed, milestone reached, session ending.
|
|
470
670
|
|
|
471
|
-
Entries become searchable via `jfl memory search` and MCP `memory_search` tool.
|
|
671
|
+
Entries become searchable via `jfl memory search` and MCP `memory_search` tool. RL fields (`hypothesis`, `outcome`, `score_delta`, `eval_snapshot`, `diff_hash`, `context_entries`) enable trajectory-based learning.
|
|
472
672
|
|
|
473
673
|
---
|
|
474
674
|
|
|
@@ -488,13 +688,63 @@ SessionStart hook fires You work normally Stop hook fire
|
|
|
488
688
|
├─ Serves MCP tools to Claude Code
|
|
489
689
|
├─ Aggregates journal + knowledge + code
|
|
490
690
|
├─ Bridges service events from file-drop
|
|
491
|
-
|
|
691
|
+
├─ Watches journal/ for live entries
|
|
692
|
+
├─ Portfolio mode: fans out to child hubs
|
|
693
|
+
├─ Flow engine: reactive trigger→action
|
|
694
|
+
└─ Web dashboard at /dashboard/
|
|
492
695
|
```
|
|
493
696
|
|
|
494
697
|
**Everything is files.** No proprietary database. No lock-in. Context is git-native — version controlled, portable, model-agnostic.
|
|
495
698
|
|
|
496
699
|
---
|
|
497
700
|
|
|
701
|
+
## CI/CD
|
|
702
|
+
|
|
703
|
+
Two GitHub Actions workflows handle quality and releases.
|
|
704
|
+
|
|
705
|
+
### CI — `.github/workflows/ci.yml`
|
|
706
|
+
|
|
707
|
+
Runs on every push and PR to `main`:
|
|
708
|
+
|
|
709
|
+
- TypeScript strict mode type checking
|
|
710
|
+
- Full test suite (~365 tests across 17 test files)
|
|
711
|
+
- Coverage report uploaded as artifact
|
|
712
|
+
|
|
713
|
+
### CD — `.github/workflows/release.yml`
|
|
714
|
+
|
|
715
|
+
Fires after CI passes on `main`. Uses [Changesets](https://github.com/changesets/changesets) for version management and npm Trusted Publisher (OIDC) for secretless publishing.
|
|
716
|
+
|
|
717
|
+
**Auto-changeset generation:** `scripts/generate-changesets.sh` converts conventional commit messages into changesets automatically:
|
|
718
|
+
- `feat:` = minor bump
|
|
719
|
+
- `fix:` = patch bump
|
|
720
|
+
- `feat!:` = major bump
|
|
721
|
+
|
|
722
|
+
No manual `npx changeset` needed for most changes.
|
|
723
|
+
|
|
724
|
+
**Release flow:**
|
|
725
|
+
|
|
726
|
+
```bash
|
|
727
|
+
# Option A: Manual changeset
|
|
728
|
+
npx changeset # pick bump level, write summary
|
|
729
|
+
|
|
730
|
+
# Option B: Just use conventional commits — auto-generated on CI
|
|
731
|
+
|
|
732
|
+
# Push to main — CI runs, then release.yml fires
|
|
733
|
+
# → changesets/action creates a "Version Packages" PR
|
|
734
|
+
|
|
735
|
+
# Merge the Version PR
|
|
736
|
+
# → release.yml fires again → npm publish --provenance --access public
|
|
737
|
+
```
|
|
738
|
+
|
|
739
|
+
No `NPM_TOKEN` needed. Publishing uses OIDC provenance via npm Trusted Publisher.
|
|
740
|
+
|
|
741
|
+
**One-time setup (per package):**
|
|
742
|
+
> npmjs.com > `jfl` package > Settings > Publish Access > Add Provenance
|
|
743
|
+
> - Repository: `402goose/jfl-cli`
|
|
744
|
+
> - Workflow: `.github/workflows/release.yml`
|
|
745
|
+
|
|
746
|
+
---
|
|
747
|
+
|
|
498
748
|
## Auto-Update
|
|
499
749
|
|
|
500
750
|
JFL checks for npm updates on session start (24-hour cache):
|
|
@@ -530,6 +780,20 @@ jfl wallet # Wallet and day pass status
|
|
|
530
780
|
|
|
531
781
|
## What's New
|
|
532
782
|
|
|
783
|
+
**0.3.0**
|
|
784
|
+
- Feat: **Portfolio workspace type** — `jfl portfolio register/list/unregister/status/phone-home`. Portfolios contain multiple GTM workspaces with cross-product event routing via SSE, context scope enforcement (produces/consumes/denied), fan-out queries to child hubs, and portfolio-level leaderboard aggregation
|
|
785
|
+
- Feat: **Dashboard V2** — pre-built Vite + Preact + Tailwind SPA served at `/dashboard/`. Pages: Overview (activity charts, metric cards), Journal (search + type filters), Events (pattern filter presets), Services (type badges, context scope, data flows), Flows (definitions + execution history), Health (system metrics, memory index), Agents (eval leaderboards grouped by domain)
|
|
786
|
+
- Feat: **Eval framework** — `jfl eval list/trajectory/log/compare/tuples`. Track agent metrics over time with composite scores, dual-write up parent chain, extract (state, action, reward) training tuples. Agents grouped by metric domain (ProductRank: ndcg@10/mrr/precision@5, SEO: avg_rank/keywords_ranked)
|
|
787
|
+
- Feat: **RL infrastructure (Phase 1)** — `JournalEntry` type with 6 RL fields, `TrajectoryLoader` for querying experiment history, `query_experiment_history` MCP tool
|
|
788
|
+
- Feat: **Flow engine** — declarative trigger-action automation in `.jfl/flows.yaml`. Actions: log, emit, journal, webhook, command, spawn. Gates: time-gated, deadline, requires_approval. Template interpolation with `{{child.NAME.port}}`
|
|
789
|
+
- Feat: **HTTP hooks** — Claude Code lifecycle hooks (PostToolUse, Stop, PreCompact, SubagentStart/Stop) POST to Context Hub. `jfl hooks init/status/remove/deploy`
|
|
790
|
+
- Feat: **Context scope enforcement** — produces/consumes/denied patterns. Event bus filters by scope declarations. `jfl scope list/set/test`
|
|
791
|
+
- Feat: CI/CD pipeline — GitHub Actions CI (strict TypeScript + Jest gate) + CD via Changesets with auto-generation from conventional commits. npm Trusted Publisher with OIDC provenance
|
|
792
|
+
- Feat: Service agent templates (CLAUDE.md, settings.json, knowledge docs)
|
|
793
|
+
- Feat: Session cleanup guard — prevents `rm -rf` on main when no worktrees exist
|
|
794
|
+
- Fix: TypeScript strict mode build errors resolved
|
|
795
|
+
- Test: ~365 tests across 17 test files (up from 237)
|
|
796
|
+
|
|
533
797
|
**0.2.5**
|
|
534
798
|
- Feat: Docker-style grouped `jfl --help` — 5 groups (Getting Started, Daily Use, Management, Platform, Advanced), ~30 lines down from 52
|
|
535
799
|
- Feat: `jfl doctor [--fix]` — unified project health checker (9 checks: .jfl dir, config, Context Hub, hooks, memory, journal, agents, flows, git). Auto-repairs hooks, config, and journal with `--fix`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"context-hub.d.ts","sourceRoot":"","sources":["../../src/commands/context-hub.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;
|
|
1
|
+
{"version":3,"file":"context-hub.d.ts","sourceRoot":"","sources":["../../src/commands/context-hub.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAg8CH,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAiBjF;AA2ND,wBAAsB,qBAAqB,CAAC,IAAI,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAA;CAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAiHxF;AAMD,wBAAsB,iBAAiB,CACrC,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAAC,KAAK,CAAC,EAAE,OAAO,CAAA;CAAO,iBA+fnE"}
|
|
@@ -632,10 +632,16 @@ function createServer(projectRoot, port, eventBus, flowEngine) {
|
|
|
632
632
|
scope: cfg.context_scope || null,
|
|
633
633
|
registered_services: (cfg.registered_services || []).map((s) => ({
|
|
634
634
|
name: s.name,
|
|
635
|
+
path: s.path,
|
|
635
636
|
type: s.type,
|
|
636
637
|
status: s.status,
|
|
637
638
|
context_scope: s.context_scope || null,
|
|
638
639
|
})),
|
|
640
|
+
openclaw_agents: (cfg.openclaw_agents || []).map((a) => ({
|
|
641
|
+
id: a.id,
|
|
642
|
+
runtime: a.runtime,
|
|
643
|
+
registered_at: a.registered_at,
|
|
644
|
+
})),
|
|
639
645
|
gtm_parent: cfg.gtm_parent || null,
|
|
640
646
|
portfolio_parent: cfg.portfolio_parent || null,
|
|
641
647
|
};
|
|
@@ -876,6 +882,38 @@ function createServer(projectRoot, port, eventBus, flowEngine) {
|
|
|
876
882
|
}
|
|
877
883
|
return;
|
|
878
884
|
}
|
|
885
|
+
// Synopsis (work summary)
|
|
886
|
+
if (url.pathname === "/api/synopsis" && req.method === "GET") {
|
|
887
|
+
try {
|
|
888
|
+
const hours = parseInt(url.searchParams.get("hours") || "24", 10);
|
|
889
|
+
const author = url.searchParams.get("author") || undefined;
|
|
890
|
+
const { generateSynopsis } = await import("./synopsis.js");
|
|
891
|
+
const synopsis = generateSynopsis(projectRoot, hours, author);
|
|
892
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
893
|
+
res.end(JSON.stringify(synopsis));
|
|
894
|
+
}
|
|
895
|
+
catch (err) {
|
|
896
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
897
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
898
|
+
}
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
// Prediction accuracy (Stratus)
|
|
902
|
+
if (url.pathname === "/api/eval/predictions" && req.method === "GET") {
|
|
903
|
+
try {
|
|
904
|
+
const { Predictor } = await import("../lib/predictor.js");
|
|
905
|
+
const predictor = new Predictor({ projectRoot });
|
|
906
|
+
const accuracy = predictor.getAccuracy();
|
|
907
|
+
const recent = predictor.getHistory(20).reverse();
|
|
908
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
909
|
+
res.end(JSON.stringify({ accuracy, recent }));
|
|
910
|
+
}
|
|
911
|
+
catch (err) {
|
|
912
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
913
|
+
res.end(JSON.stringify({ accuracy: { total: 0, resolved: 0, direction_accuracy: 0, mean_delta_error: 0, calibration: 0 }, recent: [] }));
|
|
914
|
+
}
|
|
915
|
+
return;
|
|
916
|
+
}
|
|
879
917
|
// Cross-project health
|
|
880
918
|
if (url.pathname === "/api/projects" && req.method === "GET") {
|
|
881
919
|
const tracked = getTrackedProjects();
|
|
@@ -1005,6 +1043,39 @@ function createServer(projectRoot, port, eventBus, flowEngine) {
|
|
|
1005
1043
|
}
|
|
1006
1044
|
return;
|
|
1007
1045
|
}
|
|
1046
|
+
// Telemetry agent status
|
|
1047
|
+
if (url.pathname === "/api/telemetry/agent" && req.method === "GET") {
|
|
1048
|
+
const agent = server.__telemetryAgent;
|
|
1049
|
+
if (agent) {
|
|
1050
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1051
|
+
res.end(JSON.stringify(agent.getStatus()));
|
|
1052
|
+
}
|
|
1053
|
+
else {
|
|
1054
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1055
|
+
res.end(JSON.stringify({ running: false, lastRun: '', runCount: 0, lastInsights: [] }));
|
|
1056
|
+
}
|
|
1057
|
+
return;
|
|
1058
|
+
}
|
|
1059
|
+
// Telemetry agent: trigger manual run
|
|
1060
|
+
if (url.pathname === "/api/telemetry/agent/run" && req.method === "POST") {
|
|
1061
|
+
const agent = server.__telemetryAgent;
|
|
1062
|
+
if (agent) {
|
|
1063
|
+
try {
|
|
1064
|
+
const insights = await agent.run();
|
|
1065
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1066
|
+
res.end(JSON.stringify({ ok: true, insights }));
|
|
1067
|
+
}
|
|
1068
|
+
catch (err) {
|
|
1069
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1070
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
1071
|
+
}
|
|
1072
|
+
}
|
|
1073
|
+
else {
|
|
1074
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
1075
|
+
res.end(JSON.stringify({ error: "Telemetry agent not running" }));
|
|
1076
|
+
}
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1008
1079
|
// Flow definitions
|
|
1009
1080
|
if (url.pathname === "/api/flows" && req.method === "GET") {
|
|
1010
1081
|
if (!flowEngine) {
|
|
@@ -1061,6 +1132,72 @@ function createServer(projectRoot, port, eventBus, flowEngine) {
|
|
|
1061
1132
|
});
|
|
1062
1133
|
return;
|
|
1063
1134
|
}
|
|
1135
|
+
if (url.pathname.match(/^\/api\/flows\/[^/]+\/toggle$/) && req.method === "POST") {
|
|
1136
|
+
if (!flowEngine) {
|
|
1137
|
+
res.writeHead(503, { "Content-Type": "application/json" });
|
|
1138
|
+
res.end(JSON.stringify({ error: "Flow engine not initialized" }));
|
|
1139
|
+
return;
|
|
1140
|
+
}
|
|
1141
|
+
const flowName = decodeURIComponent(url.pathname.split("/")[3]);
|
|
1142
|
+
let body = "";
|
|
1143
|
+
req.on("data", chunk => body += chunk);
|
|
1144
|
+
req.on("end", () => {
|
|
1145
|
+
try {
|
|
1146
|
+
const { enabled } = JSON.parse(body || "{}");
|
|
1147
|
+
const result = flowEngine.toggleFlow(flowName, enabled);
|
|
1148
|
+
if (!result) {
|
|
1149
|
+
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1150
|
+
res.end(JSON.stringify({ error: "Flow not found" }));
|
|
1151
|
+
return;
|
|
1152
|
+
}
|
|
1153
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1154
|
+
res.end(JSON.stringify({ ok: true, flow: flowName, enabled: result.enabled }));
|
|
1155
|
+
}
|
|
1156
|
+
catch (err) {
|
|
1157
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1158
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
1159
|
+
}
|
|
1160
|
+
});
|
|
1161
|
+
return;
|
|
1162
|
+
}
|
|
1163
|
+
if (url.pathname === "/api/actions/spawn" && req.method === "POST") {
|
|
1164
|
+
let body = "";
|
|
1165
|
+
req.on("data", chunk => body += chunk);
|
|
1166
|
+
req.on("end", () => {
|
|
1167
|
+
try {
|
|
1168
|
+
const { command, args, cwd, event_type, event_data } = JSON.parse(body || "{}");
|
|
1169
|
+
if (!command) {
|
|
1170
|
+
res.writeHead(400, { "Content-Type": "application/json" });
|
|
1171
|
+
res.end(JSON.stringify({ error: "command required" }));
|
|
1172
|
+
return;
|
|
1173
|
+
}
|
|
1174
|
+
const env = { ...process.env };
|
|
1175
|
+
delete env.ANTHROPIC_API_KEY;
|
|
1176
|
+
delete env.CLAUDE_CODE_ENTRYPOINT;
|
|
1177
|
+
const child = spawn(command, args || [], {
|
|
1178
|
+
cwd: cwd || projectRoot,
|
|
1179
|
+
detached: true,
|
|
1180
|
+
stdio: "ignore",
|
|
1181
|
+
env,
|
|
1182
|
+
});
|
|
1183
|
+
child.unref();
|
|
1184
|
+
if (event_type && eventBus) {
|
|
1185
|
+
eventBus.emit({
|
|
1186
|
+
type: event_type,
|
|
1187
|
+
source: "dashboard:action",
|
|
1188
|
+
data: event_data || { command, args, pid: child.pid },
|
|
1189
|
+
});
|
|
1190
|
+
}
|
|
1191
|
+
res.writeHead(200, { "Content-Type": "application/json" });
|
|
1192
|
+
res.end(JSON.stringify({ ok: true, pid: child.pid }));
|
|
1193
|
+
}
|
|
1194
|
+
catch (err) {
|
|
1195
|
+
res.writeHead(500, { "Content-Type": "application/json" });
|
|
1196
|
+
res.end(JSON.stringify({ error: err.message }));
|
|
1197
|
+
}
|
|
1198
|
+
});
|
|
1199
|
+
return;
|
|
1200
|
+
}
|
|
1064
1201
|
// 404
|
|
1065
1202
|
res.writeHead(404, { "Content-Type": "application/json" });
|
|
1066
1203
|
res.end(JSON.stringify({ error: "Not found" }));
|
|
@@ -1728,6 +1865,23 @@ export async function contextHubCommand(action, options = {}) {
|
|
|
1728
1865
|
catch (err) {
|
|
1729
1866
|
console.error(`[${timestamp}] Failed to start flow engine:`, err.message);
|
|
1730
1867
|
}
|
|
1868
|
+
// Start telemetry agent (periodic pattern detection)
|
|
1869
|
+
try {
|
|
1870
|
+
const { TelemetryAgent } = await import("../lib/telemetry-agent.js");
|
|
1871
|
+
const telemetryAgent = new TelemetryAgent({
|
|
1872
|
+
projectRoot,
|
|
1873
|
+
intervalMs: 30 * 60 * 1000,
|
|
1874
|
+
emitEvent: (type, data, source) => {
|
|
1875
|
+
eventBus.emit({ type: type, data, source: source || 'telemetry-agent' });
|
|
1876
|
+
},
|
|
1877
|
+
});
|
|
1878
|
+
telemetryAgent.start();
|
|
1879
|
+
server.__telemetryAgent = telemetryAgent;
|
|
1880
|
+
console.log(`[${timestamp}] Telemetry agent started (interval: 30m)`);
|
|
1881
|
+
}
|
|
1882
|
+
catch (err) {
|
|
1883
|
+
console.error(`[${timestamp}] Failed to start telemetry agent:`, err.message);
|
|
1884
|
+
}
|
|
1731
1885
|
console.log(`[${timestamp}] MAP event bus initialized (buffer: 1000, subscribers: ${eventBus.getSubscriberCount()})`);
|
|
1732
1886
|
console.log(`[${timestamp}] Ready to serve requests`);
|
|
1733
1887
|
});
|