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
@@ -101,10 +101,20 @@ export async function getCodeDirectory() {
101
101
  // Project Detection
102
102
  // ============================================================================
103
103
  /**
104
- * Get the project data directory name (.tenet/ or .jfl/)
105
- * Checks .tenet/ first (migrated), falls back to .jfl/ (legacy)
104
+ * Get the project data directory name (.jfl/ by default, .tenet/ only after migration)
105
+ *
106
+ * .jfl/ is the canonical directory — progressive disclosure metadata,
107
+ * agents, eval scripts, training data all live here.
108
+ *
109
+ * .tenet/ only exists after `jfl migrate` (cosmetic rebrand).
110
+ * We check .jfl/ first because that's the default state.
106
111
  */
107
112
  export function getProjectDataDir(cwd = process.cwd()) {
113
+ if (existsSync(join(cwd, ".jfl", "config.json")))
114
+ return ".jfl";
115
+ if (existsSync(join(cwd, ".jfl")))
116
+ return ".jfl";
117
+ // Post-migration: .tenet/ exists after `jfl migrate`
108
118
  if (existsSync(join(cwd, ".tenet", "config.json")))
109
119
  return ".tenet";
110
120
  if (existsSync(join(cwd, ".tenet")))
@@ -112,8 +122,8 @@ export function getProjectDataDir(cwd = process.cwd()) {
112
122
  return ".jfl";
113
123
  }
114
124
  export function isInJFLProject(cwd = process.cwd()) {
115
- return existsSync(join(cwd, ".tenet", "config.json")) ||
116
- existsSync(join(cwd, ".jfl", "config.json"));
125
+ return existsSync(join(cwd, ".jfl", "config.json")) ||
126
+ existsSync(join(cwd, ".tenet", "config.json"));
117
127
  }
118
128
  export function findProjectRoot(startPath = process.cwd()) {
119
129
  let currentPath = startPath;
@@ -1 +1 @@
1
- {"version":3,"file":"jfl-config.js","sourceRoot":"","sources":["../../src/utils/jfl-config.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"jfl-config.js","sourceRoot":"","sources":["../../src/utils/jfl-config.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jfl",
3
- "version": "0.9.0",
3
+ "version": "0.9.2",
4
4
  "description": "Just Fucking Launch - CLI for AI-powered GTM and development",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
Binary file
@@ -15,6 +15,7 @@ import { execSync, spawnSync } from "child_process"
15
15
  import { existsSync, readFileSync, mkdirSync, writeFileSync, appendFileSync } from "fs"
16
16
  import { join, dirname } from "path"
17
17
  import type { PiContext, JflConfig } from "./types.js"
18
+ import { jflImport } from "./jfl-resolve.js"
18
19
  import { emitCustomEvent } from "./map-bridge.js"
19
20
 
20
21
  let projectRoot = ""
@@ -65,7 +66,7 @@ function gh(args: string[]): { ok: boolean; output: string } {
65
66
  async function getPolicyHead(): Promise<any> {
66
67
  try {
67
68
  // @ts-ignore — resolved from jfl package at runtime
68
- const { PolicyHeadInference } = await import("../../src/lib/policy-head.js")
69
+ const { PolicyHeadInference } = await jflImport("policy-head")
69
70
  return new PolicyHeadInference(projectRoot)
70
71
  } catch {
71
72
  return null
@@ -75,7 +76,7 @@ async function getPolicyHead(): Promise<any> {
75
76
  async function getTrainingBuffer(): Promise<any> {
76
77
  try {
77
78
  // @ts-ignore — resolved from jfl package at runtime
78
- const { TrainingBuffer } = await import("../../src/lib/training-buffer.js")
79
+ const { TrainingBuffer } = await jflImport("training-buffer")
79
80
  return new TrainingBuffer(projectRoot)
80
81
  } catch {
81
82
  return null
@@ -1,11 +1,12 @@
1
1
  /**
2
2
  * Context Extension
3
3
  *
4
- * Ensures Context Hub is running, injects CLAUDE.md + recent context before
5
- * each agent turn via POST /api/prompt, and registers the jfl_context tool.
6
- * Hub is the single source of truth no local CLAUDE.md reading.
4
+ * Ensures Context Hub is running and registers the jfl_context tool.
5
+ * On first turn, injects a small amount of recent journal context so
6
+ * the model's greeting is contextual. The model reads CLAUDE.md itself
7
+ * via AGENTS.md instructions — no 45K system prompt injection needed.
7
8
  *
8
- * @purpose Context Hub + system prompt injection via Hub API (parity with Claude Code)
9
+ * @purpose Context Hub startup + jfl_context tool registration
9
10
  */
10
11
 
11
12
  import { existsSync, readFileSync } from "fs"
@@ -18,7 +19,11 @@ import { readHubUrl, readToken } from "./hub-resolver.js"
18
19
  let hubBaseUrl = "http://localhost:4242"
19
20
  let hubToken: string | null = null
20
21
  let projectRoot = ""
21
- let cachedPrompt: string | null = null
22
+ let promptInjected = false
23
+
24
+ export function resetPromptInjected(): void {
25
+ promptInjected = false
26
+ }
22
27
 
23
28
  function refreshHubUrl(): void {
24
29
  hubBaseUrl = readHubUrl(projectRoot)
@@ -28,10 +33,6 @@ function refreshHubUrl(): void {
28
33
  async function fetchContext(query?: string, limit = 10): Promise<string> {
29
34
  for (let attempt = 0; attempt < 2; attempt++) {
30
35
  try {
31
- const params = new URLSearchParams()
32
- if (query) params.set("query", query)
33
- params.set("limit", String(limit))
34
-
35
36
  const body: Record<string, unknown> = { maxItems: limit }
36
37
  if (query) body.query = query
37
38
 
@@ -67,31 +68,12 @@ async function fetchContext(query?: string, limit = 10): Promise<string> {
67
68
  return ""
68
69
  }
69
70
 
70
- async function fetchPrompt(taskType?: string): Promise<string> {
71
- try {
72
- const resp = await fetch(`${hubBaseUrl}/api/prompt`, {
73
- method: "POST",
74
- headers: {
75
- "Content-Type": "application/json",
76
- ...(hubToken ? { Authorization: `Bearer ${hubToken}` } : {}),
77
- },
78
- body: JSON.stringify({ taskType: taskType ?? "general", maxItems: 20 }),
79
- signal: AbortSignal.timeout(10000),
80
- })
81
-
82
- if (!resp.ok) return ""
83
- const data = await resp.json() as { prompt?: string }
84
- return data.prompt ?? ""
85
- } catch {
86
- return ""
87
- }
88
- }
89
-
90
71
  export async function setupContext(ctx: PiContext, _config: JflConfig): Promise<void> {
91
72
  const root = ctx.session.projectRoot
92
73
  projectRoot = root
74
+ promptInjected = false // reset on each new session
93
75
 
94
- // Start Context Hub FIRST, then read the port it wrote
76
+ // Start Context Hub
95
77
  try {
96
78
  execSync("jfl context-hub ensure", { cwd: root, stdio: "pipe", timeout: 15000 })
97
79
  ctx.log("Context Hub ensured", "debug")
@@ -100,17 +82,11 @@ export async function setupContext(ctx: PiContext, _config: JflConfig): Promise<
100
82
  ctx.log(`Context Hub ensure failed: ${msg}`, "debug")
101
83
  }
102
84
 
103
- // Now read the port (hub may have written .jfl/context-hub.port during ensure)
85
+ // Read the port hub wrote
104
86
  hubBaseUrl = readHubUrl(root)
105
87
  hubToken = readToken(root)
106
88
  ctx.log(`Context Hub URL: ${hubBaseUrl}`, "debug")
107
89
 
108
- // Pre-fetch prompt for first turn (cache it)
109
- cachedPrompt = await fetchPrompt("general")
110
- if (cachedPrompt) {
111
- ctx.log(`System prompt loaded via Hub (${cachedPrompt.length} chars)`, "debug")
112
- }
113
-
114
90
  ctx.registerTool({
115
91
  name: "jfl_context",
116
92
  description: "Get unified project context: journal entries, knowledge docs, code headers. Use at session start and when you need project state. Equivalent to MCP context_get.",
@@ -139,15 +115,7 @@ export async function setupContext(ctx: PiContext, _config: JflConfig): Promise<
139
115
  },
140
116
  },
141
117
  async handler(input) {
142
- const { query, limit, taskType } = input as { query?: string; limit?: number; taskType?: string }
143
-
144
- // If asking for general context with no query, use the prompt endpoint
145
- if (!query && taskType) {
146
- const prompt = await fetchPrompt(taskType)
147
- return prompt || "No context available."
148
- }
149
-
150
- // Otherwise use the search endpoint
118
+ const { query, limit } = input as { query?: string; limit?: number; taskType?: string }
151
119
  const result = await fetchContext(query, limit ?? 30)
152
120
  return result || "No relevant context found."
153
121
  },
@@ -160,25 +128,20 @@ export async function injectContext(
160
128
  _ctx: PiContext,
161
129
  _event: AgentStartEvent
162
130
  ): Promise<{ systemPromptAddition?: string } | void> {
163
- // Use cached prompt from setup, or fetch fresh
164
- const prompt = cachedPrompt || await fetchPrompt("general")
165
-
166
- // Clear cache after first use — subsequent turns get fresh context
167
- cachedPrompt = null
168
-
169
- if (!prompt) {
170
- // Fallback: just inject recent context
171
- const context = await fetchContext(undefined, 10)
172
- if (!context) return
173
- return {
174
- systemPromptAddition: [
175
- "## JFL Project Context",
176
- "(Recent journal entries and project knowledge)",
177
- "",
178
- context,
179
- ].join("\n"),
180
- }
131
+ // Only on first turn inject recent journal context so greeting is contextual.
132
+ // The model reads CLAUDE.md itself (AGENTS.md tells it to). No 45K dump needed.
133
+ if (promptInjected) return
134
+ promptInjected = true
135
+
136
+ const context = await fetchContext(undefined, 10)
137
+ if (!context) return
138
+
139
+ return {
140
+ systemPromptAddition: [
141
+ "## JFL Project Context",
142
+ "(Recent journal entries and project knowledge)",
143
+ "",
144
+ context,
145
+ ].join("\n"),
181
146
  }
182
-
183
- return { systemPromptAddition: prompt }
184
147
  }
@@ -9,6 +9,7 @@
9
9
 
10
10
  import { randomUUID } from "crypto"
11
11
  import type { PiContext, JflConfig, AgentEndEvent } from "./types.js"
12
+ import { jflImport } from "./jfl-resolve.js"
12
13
  import { emitCustomEvent } from "./map-bridge.js"
13
14
 
14
15
  interface EvalEntry {
@@ -50,7 +51,7 @@ export async function onAgentEnd(ctx: PiContext, event: AgentEndEvent): Promise<
50
51
 
51
52
  try {
52
53
  // @ts-ignore — resolved from jfl package at runtime
53
- const { appendEval } = await import("../../src/lib/eval-store.js")
54
+ const { appendEval } = await jflImport("eval-store")
54
55
  appendEval(entry as Parameters<typeof appendEval>[0], projectRoot)
55
56
  } catch {
56
57
  // eval-store may not be available in all contexts — non-fatal
@@ -0,0 +1,267 @@
1
+ /**
2
+ * Hub Tools Extension
3
+ *
4
+ * Registers tools for direct Context Hub interaction that were previously
5
+ * only available via MCP: events_publish, events_recent, context_status,
6
+ * context_sessions, query_experiment_history.
7
+ *
8
+ * Closes the parity gap between Claude Code (MCP) and Pi (extensions).
9
+ *
10
+ * @purpose Hub API tools — events, status, sessions, experiments (MCP parity)
11
+ */
12
+
13
+ import type { PiContext, JflConfig } from "./types.js"
14
+ import { hubUrl, authToken } from "./map-bridge.js"
15
+ import { readHubUrl, readToken } from "./hub-resolver.js"
16
+
17
+ let projectRoot = ""
18
+
19
+ async function hubFetch(path: string, method: "GET" | "POST" = "GET", body?: unknown): Promise<any> {
20
+ // Try current hubUrl, then fall back to fresh port file read
21
+ const urls = [hubUrl]
22
+ if (projectRoot) {
23
+ const fresh = readHubUrl(projectRoot)
24
+ if (fresh && fresh !== hubUrl) urls.push(fresh)
25
+ }
26
+
27
+ let lastErr: Error | null = null
28
+ for (const url of urls) {
29
+ const token = url === hubUrl ? authToken : (projectRoot ? readToken(projectRoot) : authToken)
30
+ try {
31
+ const resp = await fetch(`${url}${path}`, {
32
+ method,
33
+ headers: {
34
+ "Content-Type": "application/json",
35
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
36
+ },
37
+ ...(body ? { body: JSON.stringify(body) } : {}),
38
+ signal: AbortSignal.timeout(5000),
39
+ })
40
+ if (!resp.ok) throw new Error(`Hub ${path}: ${resp.status}`)
41
+ return await resp.json()
42
+ } catch (err) {
43
+ lastErr = err as Error
44
+ }
45
+ }
46
+ throw lastErr ?? new Error(`Hub ${path}: unreachable`)
47
+ }
48
+
49
+ export async function setupHubTools(ctx: PiContext, _config: JflConfig): Promise<void> {
50
+ projectRoot = ctx.session.projectRoot
51
+
52
+ // ─── jfl_events_publish ──────────────────────────────────────────────────
53
+ ctx.registerTool({
54
+ name: "jfl_events_publish",
55
+ description: "Publish an event to the MAP event bus. Use to signal state changes, completions, or trigger flows.",
56
+ promptSnippet: "Publish event to MAP bus",
57
+ inputSchema: {
58
+ type: "object",
59
+ properties: {
60
+ type: {
61
+ type: "string",
62
+ description: "Event type (e.g. 'task:completed', 'eval:scored', 'custom:my-event')",
63
+ },
64
+ data: {
65
+ type: "string",
66
+ description: "Event data as JSON string",
67
+ },
68
+ },
69
+ required: ["type"],
70
+ },
71
+ async handler(input) {
72
+ const { type, data } = input as { type: string; data?: string }
73
+ try {
74
+ let eventData: unknown = {}
75
+ if (data) {
76
+ try { eventData = JSON.parse(data) } catch { eventData = { message: data } }
77
+ }
78
+
79
+ await hubFetch("/api/events", "POST", {
80
+ type,
81
+ source: `pi:${ctx.session.id}`,
82
+ data: eventData,
83
+ ts: new Date().toISOString(),
84
+ })
85
+ return `Event published: ${type}`
86
+ } catch {
87
+ return "Event publish failed — Context Hub may not be running."
88
+ }
89
+ },
90
+ })
91
+
92
+ // ─── jfl_events_recent ──────────────────────────────────────────────────
93
+ ctx.registerTool({
94
+ name: "jfl_events_recent",
95
+ description: "Get recent events from the MAP event bus. Check what happened in the system — agent actions, eval results, flow triggers.",
96
+ promptSnippet: "View recent MAP events",
97
+ inputSchema: {
98
+ type: "object",
99
+ properties: {
100
+ limit: {
101
+ type: "number",
102
+ description: "Number of events to return (default: 20)",
103
+ },
104
+ type: {
105
+ type: "string",
106
+ description: "Filter by event type prefix (e.g. 'eval:', 'hook:', 'task:')",
107
+ },
108
+ },
109
+ },
110
+ async handler(input) {
111
+ const { limit, type } = input as { limit?: number; type?: string }
112
+ try {
113
+ const params = new URLSearchParams()
114
+ if (limit) params.set("limit", String(limit))
115
+ if (type) params.set("type", type)
116
+
117
+ const data = await hubFetch(`/api/events?${params}`)
118
+ const events = data.events ?? data ?? []
119
+
120
+ if (!Array.isArray(events) || events.length === 0) return "No recent events."
121
+
122
+ return events
123
+ .slice(0, limit ?? 20)
124
+ .map((e: any) => {
125
+ const ts = e.ts ? new Date(e.ts).toLocaleTimeString() : "?"
126
+ const src = e.source ?? "?"
127
+ return `[${ts}] ${e.type} (${src})${e.data ? " — " + JSON.stringify(e.data).slice(0, 100) : ""}`
128
+ })
129
+ .join("\n")
130
+ } catch {
131
+ return "Events unavailable — Context Hub may not be running."
132
+ }
133
+ },
134
+ })
135
+
136
+ // ─── jfl_context_status ─────────────────────────────────────────────────
137
+ ctx.registerTool({
138
+ name: "jfl_context_status",
139
+ description: "Check Context Hub status — available sources, memory index health, connected services.",
140
+ promptSnippet: "Check Context Hub health",
141
+ inputSchema: {
142
+ type: "object",
143
+ properties: {},
144
+ },
145
+ async handler() {
146
+ try {
147
+ const data = await hubFetch("/api/context/status")
148
+
149
+ const lines: string[] = ["Context Hub Status:"]
150
+ for (const [k, v] of Object.entries(data)) {
151
+ if (typeof v === "object" && v !== null) {
152
+ lines.push(` ${k}: ${JSON.stringify(v)}`)
153
+ } else {
154
+ lines.push(` ${k}: ${v}`)
155
+ }
156
+ }
157
+ return lines.join("\n")
158
+ } catch {
159
+ return "Context Hub status unavailable."
160
+ }
161
+ },
162
+ })
163
+
164
+ // ─── jfl_context_sessions ────────────────────────────────────────────────
165
+ ctx.registerTool({
166
+ name: "jfl_context_sessions",
167
+ description: "See activity from other sessions — what other agents/users are working on. Informational only.",
168
+ promptSnippet: "View other session activity",
169
+ inputSchema: {
170
+ type: "object",
171
+ properties: {
172
+ limit: {
173
+ type: "number",
174
+ description: "Number of sessions to return (default: 5)",
175
+ },
176
+ },
177
+ },
178
+ async handler(input) {
179
+ const { limit } = input as { limit?: number }
180
+ try {
181
+ // Try the sessions endpoint
182
+ const data = await hubFetch(`/api/context/sessions?limit=${limit ?? 5}`)
183
+ const sessions = data.sessions ?? data ?? []
184
+
185
+ if (!Array.isArray(sessions) || sessions.length === 0) return "No other sessions found."
186
+
187
+ return sessions
188
+ .map((s: any) => {
189
+ const age = s.lastSeen ? `(${new Date(s.lastSeen).toLocaleString()})` : ""
190
+ return `${s.branch ?? s.id ?? "?"} ${age} — ${s.summary ?? s.status ?? "active"}`
191
+ })
192
+ .join("\n")
193
+ } catch {
194
+ // Fallback: read journal files to find other sessions
195
+ try {
196
+ const { execSync } = await import("child_process")
197
+ const journalDir = `${ctx.session.projectRoot}/.jfl/journal`
198
+ const files = execSync(`ls -t ${journalDir}/*.jsonl 2>/dev/null | head -5`, {
199
+ encoding: "utf-8",
200
+ }).trim()
201
+
202
+ if (!files) return "No other sessions found."
203
+
204
+ return files.split("\n")
205
+ .map(f => {
206
+ const branch = f.split("/").pop()?.replace(".jsonl", "") ?? "?"
207
+ return `Session: ${branch}`
208
+ })
209
+ .join("\n")
210
+ } catch {
211
+ return "Sessions unavailable."
212
+ }
213
+ }
214
+ },
215
+ })
216
+
217
+ // ─── jfl_experiment_history ─────────────────────────────────────────────
218
+ ctx.registerTool({
219
+ name: "jfl_experiment_history",
220
+ description: "Search past experiments, their outcomes, and what was learned. Useful for avoiding repeat experiments.",
221
+ promptSnippet: "Search experiment history",
222
+ inputSchema: {
223
+ type: "object",
224
+ properties: {
225
+ query: {
226
+ type: "string",
227
+ description: "Search query for experiment history",
228
+ },
229
+ limit: {
230
+ type: "number",
231
+ description: "Max results (default: 10)",
232
+ },
233
+ },
234
+ required: ["query"],
235
+ },
236
+ async handler(input) {
237
+ const { query, limit } = input as { query: string; limit?: number }
238
+ try {
239
+ const data = await hubFetch("/api/eval/trajectory", "GET")
240
+ const entries = data.entries ?? data ?? []
241
+
242
+ if (!Array.isArray(entries) || entries.length === 0) return "No experiment history found."
243
+
244
+ // Filter by query (simple text match)
245
+ const queryLower = query.toLowerCase()
246
+ const filtered = entries
247
+ .filter((e: any) => {
248
+ const text = JSON.stringify(e).toLowerCase()
249
+ return text.includes(queryLower)
250
+ })
251
+ .slice(0, limit ?? 10)
252
+
253
+ if (filtered.length === 0) return `No experiments matching "${query}".`
254
+
255
+ return filtered
256
+ .map((e: any) => {
257
+ const ts = e.ts ? new Date(e.ts).toLocaleDateString() : "?"
258
+ const score = e.composite != null ? ` (score: ${e.composite})` : ""
259
+ return `[${ts}]${score} ${e.label ?? e.summary ?? JSON.stringify(e).slice(0, 100)}`
260
+ })
261
+ .join("\n")
262
+ } catch {
263
+ return "Experiment history unavailable — Context Hub may not be running."
264
+ }
265
+ },
266
+ })
267
+ }