jfl 0.9.0 → 0.9.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.
Files changed (136) hide show
  1. package/README.md +35 -4
  2. package/dist/commands/context-hub.d.ts.map +1 -1
  3. package/dist/commands/context-hub.js +118 -2
  4. package/dist/commands/context-hub.js.map +1 -1
  5. package/dist/commands/digest.d.ts +6 -0
  6. package/dist/commands/digest.d.ts.map +1 -1
  7. package/dist/commands/digest.js +70 -69
  8. package/dist/commands/digest.js.map +1 -1
  9. package/dist/commands/eval.d.ts +40 -0
  10. package/dist/commands/eval.d.ts.map +1 -1
  11. package/dist/commands/eval.js +8 -8
  12. package/dist/commands/eval.js.map +1 -1
  13. package/dist/commands/findings.d.ts +7 -0
  14. package/dist/commands/findings.d.ts.map +1 -1
  15. package/dist/commands/findings.js +4 -4
  16. package/dist/commands/findings.js.map +1 -1
  17. package/dist/commands/ide.d.ts.map +1 -1
  18. package/dist/commands/ide.js +25 -2
  19. package/dist/commands/ide.js.map +1 -1
  20. package/dist/commands/kanban.js +6 -6
  21. package/dist/commands/kanban.js.map +1 -1
  22. package/dist/commands/linear.d.ts.map +1 -1
  23. package/dist/commands/linear.js +24 -0
  24. package/dist/commands/linear.js.map +1 -1
  25. package/dist/commands/pi.d.ts +3 -0
  26. package/dist/commands/pi.d.ts.map +1 -1
  27. package/dist/commands/pi.js +19 -0
  28. package/dist/commands/pi.js.map +1 -1
  29. package/dist/commands/portfolio.d.ts +5 -0
  30. package/dist/commands/portfolio.d.ts.map +1 -1
  31. package/dist/commands/portfolio.js +193 -203
  32. package/dist/commands/portfolio.js.map +1 -1
  33. package/dist/commands/predict.d.ts +19 -0
  34. package/dist/commands/predict.d.ts.map +1 -1
  35. package/dist/commands/predict.js +4 -4
  36. package/dist/commands/predict.js.map +1 -1
  37. package/dist/commands/setup.d.ts.map +1 -1
  38. package/dist/commands/setup.js +106 -7
  39. package/dist/commands/setup.js.map +1 -1
  40. package/dist/commands/start.d.ts +25 -0
  41. package/dist/commands/start.d.ts.map +1 -0
  42. package/dist/commands/start.js +191 -0
  43. package/dist/commands/start.js.map +1 -0
  44. package/dist/commands/tenet-agents.js +2 -2
  45. package/dist/commands/tenet-agents.js.map +1 -1
  46. package/dist/commands/tenet-setup.d.ts +2 -1
  47. package/dist/commands/tenet-setup.d.ts.map +1 -1
  48. package/dist/commands/tenet-setup.js +22 -18
  49. package/dist/commands/tenet-setup.js.map +1 -1
  50. package/dist/commands/viz.d.ts +33 -0
  51. package/dist/commands/viz.d.ts.map +1 -1
  52. package/dist/commands/viz.js +9 -9
  53. package/dist/commands/viz.js.map +1 -1
  54. package/dist/index.js +97 -43
  55. package/dist/index.js.map +1 -1
  56. package/dist/lib/advanced-setup.d.ts +1 -1
  57. package/dist/lib/advanced-setup.d.ts.map +1 -1
  58. package/dist/lib/advanced-setup.js +22 -22
  59. package/dist/lib/advanced-setup.js.map +1 -1
  60. package/dist/lib/discovery-agent.js +4 -4
  61. package/dist/lib/discovery-agent.js.map +1 -1
  62. package/dist/lib/linear-id-map.d.ts.map +1 -1
  63. package/dist/lib/linear-id-map.js +2 -0
  64. package/dist/lib/linear-id-map.js.map +1 -1
  65. package/dist/lib/linear-webhook.d.ts +50 -0
  66. package/dist/lib/linear-webhook.d.ts.map +1 -0
  67. package/dist/lib/linear-webhook.js +92 -0
  68. package/dist/lib/linear-webhook.js.map +1 -0
  69. package/dist/lib/onboarding.d.ts +1 -1
  70. package/dist/lib/onboarding.js +14 -14
  71. package/dist/lib/onboarding.js.map +1 -1
  72. package/dist/lib/rl-manager.d.ts +1 -1
  73. package/dist/lib/rl-manager.d.ts.map +1 -1
  74. package/dist/lib/rl-manager.js +5 -4
  75. package/dist/lib/rl-manager.js.map +1 -1
  76. package/dist/lib/setup/starter-intelligence.d.ts +25 -0
  77. package/dist/lib/setup/starter-intelligence.d.ts.map +1 -0
  78. package/dist/lib/setup/starter-intelligence.js +309 -0
  79. package/dist/lib/setup/starter-intelligence.js.map +1 -0
  80. package/dist/lib/tool-schemas.d.ts +35 -0
  81. package/dist/lib/tool-schemas.d.ts.map +1 -0
  82. package/dist/lib/tool-schemas.js +246 -0
  83. package/dist/lib/tool-schemas.js.map +1 -0
  84. package/dist/lib/workspace/data-pipeline.d.ts.map +1 -1
  85. package/dist/lib/workspace/data-pipeline.js +29 -20
  86. package/dist/lib/workspace/data-pipeline.js.map +1 -1
  87. package/dist/lib/workspace/engine.d.ts +1 -0
  88. package/dist/lib/workspace/engine.d.ts.map +1 -1
  89. package/dist/lib/workspace/engine.js +10 -0
  90. package/dist/lib/workspace/engine.js.map +1 -1
  91. package/dist/lib/workspace/surface-registry.d.ts.map +1 -1
  92. package/dist/lib/workspace/surface-registry.js +5 -0
  93. package/dist/lib/workspace/surface-registry.js.map +1 -1
  94. package/dist/lib/workspace/surfaces/sidebar.d.ts.map +1 -1
  95. package/dist/lib/workspace/surfaces/sidebar.js +5 -1
  96. package/dist/lib/workspace/surfaces/sidebar.js.map +1 -1
  97. package/dist/lib/workspace/tmux-adapter.d.ts +8 -5
  98. package/dist/lib/workspace/tmux-adapter.d.ts.map +1 -1
  99. package/dist/lib/workspace/tmux-adapter.js +38 -7
  100. package/dist/lib/workspace/tmux-adapter.js.map +1 -1
  101. package/dist/lib/workspace/tmux-sidebar.d.ts +14 -0
  102. package/dist/lib/workspace/tmux-sidebar.d.ts.map +1 -0
  103. package/dist/lib/workspace/tmux-sidebar.js +230 -0
  104. package/dist/lib/workspace/tmux-sidebar.js.map +1 -0
  105. package/dist/mcp/context-hub-mcp.js +7 -1
  106. package/dist/mcp/context-hub-mcp.js.map +1 -1
  107. package/dist/types/telemetry.d.ts +1 -0
  108. package/dist/types/telemetry.d.ts.map +1 -1
  109. package/dist/utils/jfl-config.d.ts +7 -2
  110. package/dist/utils/jfl-config.d.ts.map +1 -1
  111. package/dist/utils/jfl-config.js +14 -4
  112. package/dist/utils/jfl-config.js.map +1 -1
  113. package/package.json +1 -1
  114. package/packages/pi/assets/boot.mp3 +0 -0
  115. package/packages/pi/extensions/autoresearch.ts +3 -2
  116. package/packages/pi/extensions/context.ts +29 -66
  117. package/packages/pi/extensions/eval.ts +2 -1
  118. package/packages/pi/extensions/hub-tools.ts +267 -0
  119. package/packages/pi/extensions/hud-tool.ts +230 -69
  120. package/packages/pi/extensions/index.ts +43 -63
  121. package/packages/pi/extensions/jfl-resolve.ts +98 -0
  122. package/packages/pi/extensions/journal.ts +91 -6
  123. package/packages/pi/extensions/map-bridge.ts +31 -0
  124. package/packages/pi/extensions/memory-tool.ts +84 -4
  125. package/packages/pi/extensions/onboarding-v2.ts +367 -399
  126. package/packages/pi/extensions/peter-parker.ts +2 -1
  127. package/packages/pi/extensions/policy-head-tool.ts +3 -2
  128. package/packages/pi/extensions/portfolio-bridge.ts +3 -4
  129. package/packages/pi/extensions/service-skills.ts +214 -0
  130. package/packages/pi/extensions/session.ts +91 -15
  131. package/packages/pi/extensions/stratus-bridge.ts +2 -1
  132. package/packages/pi/extensions/synopsis-tool.ts +6 -1
  133. package/packages/pi/extensions/training-buffer-tool.ts +3 -2
  134. package/packages/pi/extensions/types.ts +2 -0
  135. package/packages/pi/package.json +3 -1
  136. package/packages/pi/skills/viz/SKILL.md +204 -0
package/README.md CHANGED
@@ -8,7 +8,7 @@
8
8
  <a href="https://www.npmjs.com/package/jfl"><img src="https://img.shields.io/npm/v/jfl.svg" alt="npm version" /></a>
9
9
  <a href="https://github.com/402goose/jfl-cli/actions"><img src="https://img.shields.io/github/actions/workflow/status/402goose/jfl-cli/ci.yml?branch=main" alt="CI" /></a>
10
10
  <a href="https://github.com/402goose/jfl-cli"><img src="https://img.shields.io/github/stars/402goose/jfl-cli" alt="GitHub stars" /></a>
11
- <img src="https://img.shields.io/badge/tests-952%20passing-brightgreen" alt="tests" />
11
+ <img src="https://img.shields.io/badge/tests-1270%20passing-brightgreen" alt="tests" />
12
12
  <a href="https://github.com/402goose/jfl-cli/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-MIT-blue.svg" alt="MIT license" /></a>
13
13
  </p>
14
14
 
@@ -59,7 +59,7 @@ You (or agent) works in a session
59
59
  | **MAP Event Bus** | Nervous system | Events flow between agents, services, and flows. SSE + WebSocket. |
60
60
  | **Flow Engine** | Declarative automation | YAML trigger-action flows: `eval:scored` → auto-merge, `telemetry:insight` → spawn fix. |
61
61
  | **Pi Runtime** | Multi-model TUI agent | JFL extension for Pi — full context injection, tools, custom rendering. |
62
- | **Kanban** | Task board | GitHub Issues-backed cards: `jfl kanban add` `pick` `done`. Dashboard + Peter integration. |
62
+ | **Kanban** | Task board | GitHub Issues-backed cards with Linear bidirectional sync. PP picks up and ships autonomously. |
63
63
 
64
64
  ---
65
65
 
@@ -89,9 +89,12 @@ GTMs are context layers — they never house product code. Services register to
89
89
 
90
90
  ```bash
91
91
  jfl status # Project status
92
+ jfl whoami # Current user, project, and root
92
93
  jfl hud # Campaign dashboard
93
94
  jfl synopsis [hours] # Work summary
94
95
  jfl ask "what did we decide?" # Search memory
96
+ jfl doctor # Health check
97
+ jfl doctor --json # Machine-readable health output (for CI/evals)
95
98
  jfl ide # Terminal workspace (tmux/cmux)
96
99
  ```
97
100
 
@@ -111,9 +114,22 @@ jfl eval list # Recent eval entries
111
114
  jfl eval trajectory -a shadow # Score over time
112
115
  jfl peter experiment # Run autonomous improvement cycle
113
116
  jfl predict run --proposal "Fix auth" --goal "improve tests"
117
+ jfl train policy-head # Train RL policy head on journal data
118
+ jfl train status # Training pipeline status
119
+ ```
120
+
121
+ ### Kanban & Linear
122
+
123
+ ```bash
114
124
  jfl kanban ls # Task board
115
125
  jfl kanban add "Fix the thing" # Add card
116
126
  jfl kanban pick --agent cash # Agent picks top card
127
+ jfl kanban bootstrap # Create kanban labels in GitHub repo
128
+ jfl linear sync # Bidirectional GitHub ↔ Linear sync
129
+ jfl linear sync --direction github # Push GitHub state to Linear
130
+ jfl linear sync --direction linear # Pull Linear into GitHub
131
+ jfl linear link # Link repo to Linear project
132
+ jfl linear status # Sync status and config
117
133
  ```
118
134
 
119
135
  ### Services
@@ -122,6 +138,7 @@ jfl kanban pick --agent cash # Agent picks top card
122
138
  jfl onboard /path/to/repo # Register a service
123
139
  jfl services # Interactive TUI
124
140
  jfl services validate --fix # Auto-repair config issues
141
+ jfl ci setup # Deploy eval + review CI workflows
125
142
  ```
126
143
 
127
144
  ### Pi Runtime
@@ -162,7 +179,21 @@ Agents detect issues, create fixes, and the system auto-merges if eval scores im
162
179
  | Cost spike | `telemetry:insight` (high) | Create fix PR |
163
180
  | Predict first | `peter:pr-proposed` | Stratus prediction gate |
164
181
 
165
- **CI closes the loop:** `jfl-eval.yml` runs eval on PP pull requests. `jfl-review.yml` does AI code review. Both post events to Context Hub.
182
+ **CI closes the loop:** `jfl-eval.yml` runs eval on PP pull requests. `jfl-review.yml` does AI code review. Both post events to Context Hub. `jfl ci setup` deploys both workflows to any repo.
183
+
184
+ ---
185
+
186
+ ## Linear Integration
187
+
188
+ JFL syncs GitHub Issues ↔ Linear bidirectionally. When a PR is merged, the linked Linear issue moves to Done automatically.
189
+
190
+ ```bash
191
+ jfl linear link # Connect to your Linear workspace
192
+ jfl linear sync # Full bidirectional sync
193
+ jfl linear sync --direction github --json # CI-friendly output
194
+ ```
195
+
196
+ The `linear-sync` GitHub Actions workflow runs on every issue event and on a 30-minute cron for drift correction. PP picks up Linear issues, creates PRs with cross-repo references (`Closes org/repo#N`), and the kanban moves them through `backlog → in-progress → eval → done`.
166
197
 
167
198
  ---
168
199
 
@@ -207,7 +238,7 @@ git clone https://github.com/402goose/jfl-cli
207
238
  cd jfl-cli
208
239
  npm install
209
240
  npm run build
210
- npm test # 952 tests
241
+ npm test # 1270 tests
211
242
  npm link # Use local build globally
212
243
  ```
213
244
 
@@ -1 +1 @@
1
- {"version":3,"file":"context-hub.d.ts","sourceRoot":"","sources":["../../src/commands/context-hub.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAolFH,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAiBjF;AAkMD,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,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,iBA8lBzF"}
1
+ {"version":3,"file":"context-hub.d.ts","sourceRoot":"","sources":["../../src/commands/context-hub.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAktFH,wBAAgB,SAAS,CAAC,WAAW,EAAE,MAAM,GAAG;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CAiBjF;AAkMD,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,CAAC;IAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAAO,iBA8lBzF"}
@@ -318,6 +318,74 @@ function readCodeContext(projectRoot, limit = 30) {
318
318
  }
319
319
  return items;
320
320
  }
321
+ /**
322
+ * Read GTM content artifacts — drafts, specs, content, scripts.
323
+ * Used instead of code indexing for GTM-type projects.
324
+ */
325
+ function readGtmContent(projectRoot, limit = 40) {
326
+ const items = [];
327
+ const gtmDirs = [
328
+ { dir: "content", type: "content" },
329
+ { dir: "drafts", type: "draft" },
330
+ { dir: "specs", type: "spec" },
331
+ { dir: "scripts", type: "script" },
332
+ ];
333
+ const mdExtensions = [".md", ".txt"];
334
+ const scriptExtensions = [".sh", ".py", ".mjs", ".js"];
335
+ for (const { dir, type } of gtmDirs) {
336
+ const fullDir = path.join(projectRoot, dir);
337
+ if (!fs.existsSync(fullDir))
338
+ continue;
339
+ function scanDir(d, depth = 0) {
340
+ if (depth > 3 || items.length >= limit)
341
+ return;
342
+ try {
343
+ const entries = fs.readdirSync(d, { withFileTypes: true });
344
+ for (const entry of entries) {
345
+ if (items.length >= limit)
346
+ break;
347
+ if (entry.name.startsWith(".") || entry.name === "node_modules")
348
+ continue;
349
+ const fullPath = path.join(d, entry.name);
350
+ if (entry.isDirectory()) {
351
+ scanDir(fullPath, depth + 1);
352
+ }
353
+ else {
354
+ const isMarkdown = mdExtensions.some(ext => entry.name.endsWith(ext));
355
+ const isScript = type === "script" && scriptExtensions.some(ext => entry.name.endsWith(ext));
356
+ if (isMarkdown || isScript) {
357
+ try {
358
+ const raw = fs.readFileSync(fullPath, "utf-8");
359
+ // Extract first heading + first paragraph as summary
360
+ const lines = raw.split("\n").filter(l => l.trim());
361
+ const heading = lines.find(l => l.startsWith("#"))?.replace(/^#+\s*/, "") || entry.name;
362
+ const bodyLines = lines.filter(l => !l.startsWith("#") && !l.startsWith("---"));
363
+ const summary = bodyLines.slice(0, 5).join(" ").slice(0, 500);
364
+ if (summary.length > 20) {
365
+ items.push({
366
+ source: "content",
367
+ type,
368
+ title: heading,
369
+ content: summary,
370
+ path: fullPath
371
+ });
372
+ }
373
+ }
374
+ catch {
375
+ // Skip unreadable files
376
+ }
377
+ }
378
+ }
379
+ }
380
+ }
381
+ catch {
382
+ // Skip unreadable directories
383
+ }
384
+ }
385
+ scanDir(fullDir);
386
+ }
387
+ return items;
388
+ }
321
389
  // ============================================================================
322
390
  // Search & Scoring (TF-IDF style)
323
391
  // ============================================================================
@@ -401,11 +469,24 @@ function semanticSearch(items, query) {
401
469
  // ============================================================================
402
470
  // Orchestrator
403
471
  // ============================================================================
472
+ function getProjectType(projectRoot) {
473
+ const configPath = path.join(projectRoot, ".jfl", "config.json");
474
+ if (fs.existsSync(configPath)) {
475
+ try {
476
+ const config = JSON.parse(fs.readFileSync(configPath, "utf-8"));
477
+ return config.type || "standalone";
478
+ }
479
+ catch { }
480
+ }
481
+ return "standalone";
482
+ }
404
483
  function getUnifiedContext(projectRoot, query, taskType) {
405
484
  const journalItems = readJournalEntries(projectRoot);
406
485
  const knowledgeItems = readKnowledgeDocs(projectRoot);
486
+ const projectType = getProjectType(projectRoot);
407
487
  const codeItems = readCodeContext(projectRoot);
408
- let items = [...journalItems, ...knowledgeItems, ...codeItems];
488
+ const contentItems = projectType === "gtm" ? readGtmContent(projectRoot) : [];
489
+ let items = [...journalItems, ...knowledgeItems, ...codeItems, ...contentItems];
409
490
  // Apply semantic search if query provided
410
491
  if (query) {
411
492
  items = semanticSearch(items, query);
@@ -416,6 +497,7 @@ function getUnifiedContext(projectRoot, query, taskType) {
416
497
  journal: journalItems.length > 0,
417
498
  knowledge: knowledgeItems.length > 0,
418
499
  code: codeItems.length > 0,
500
+ content: contentItems.length > 0,
419
501
  memory: fs.existsSync(path.join(projectRoot, ".jfl", "memory.db")),
420
502
  },
421
503
  query,
@@ -476,8 +558,11 @@ async function fetchChildContext(child, endpoint, body) {
476
558
  async function getPortfolioContext(projectRoot, query, taskType, maxItems) {
477
559
  const local = getUnifiedContext(projectRoot, query, taskType);
478
560
  const children = getChildHubs(projectRoot);
479
- if (children.length === 0)
561
+ if (children.length === 0) {
562
+ if (maxItems)
563
+ local.items = local.items.slice(0, maxItems);
480
564
  return local;
565
+ }
481
566
  const endpoint = query ? "/api/context/search" : "/api/context";
482
567
  const body = { maxItems: maxItems || 20 };
483
568
  if (query)
@@ -707,6 +792,7 @@ function createServer(projectRoot, port, eventBus, flowEngine) {
707
792
  journal_count: context.items.filter(i => i.source === 'journal').length,
708
793
  knowledge_count: context.items.filter(i => i.source === 'knowledge').length,
709
794
  code_count: context.items.filter(i => i.source === 'code').length,
795
+ content_count: context.items.filter(i => i.source === 'content').length,
710
796
  query_length: query ? query.length : 0,
711
797
  });
712
798
  res.writeHead(200, { "Content-Type": "application/json" });
@@ -2274,6 +2360,36 @@ function createServer(projectRoot, port, eventBus, flowEngine) {
2274
2360
  }
2275
2361
  return;
2276
2362
  }
2363
+ // POST /api/webhooks/linear — handle Linear webhook events
2364
+ if (url.pathname === "/api/webhooks/linear" && req.method === "POST") {
2365
+ let body = "";
2366
+ req.on("data", (chunk) => { body += chunk; });
2367
+ req.on("end", async () => {
2368
+ try {
2369
+ const payload = JSON.parse(body || "{}");
2370
+ const { handleLinearWebhook } = await import("../lib/linear-webhook.js");
2371
+ const result = handleLinearWebhook(payload, projectRoot);
2372
+ if (result.handled && eventBus) {
2373
+ eventBus.emit({
2374
+ type: "linear:document-sync",
2375
+ source: "linear-webhook",
2376
+ data: {
2377
+ action: payload.action,
2378
+ documentTitle: payload.data?.title,
2379
+ journalFile: result.journalFile,
2380
+ },
2381
+ });
2382
+ }
2383
+ res.writeHead(200, { "Content-Type": "application/json" });
2384
+ res.end(JSON.stringify(result));
2385
+ }
2386
+ catch (err) {
2387
+ res.writeHead(400, { "Content-Type": "application/json" });
2388
+ res.end(JSON.stringify({ error: err.message }));
2389
+ }
2390
+ });
2391
+ return;
2392
+ }
2277
2393
  // 404
2278
2394
  res.writeHead(404, { "Content-Type": "application/json" });
2279
2395
  res.end(JSON.stringify({ error: "Not found" }));