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.
- package/README.md +35 -4
- package/dist/commands/context-hub.d.ts.map +1 -1
- package/dist/commands/context-hub.js +118 -2
- package/dist/commands/context-hub.js.map +1 -1
- package/dist/commands/digest.d.ts +6 -0
- package/dist/commands/digest.d.ts.map +1 -1
- package/dist/commands/digest.js +70 -69
- package/dist/commands/digest.js.map +1 -1
- package/dist/commands/eval.d.ts +40 -0
- package/dist/commands/eval.d.ts.map +1 -1
- package/dist/commands/eval.js +8 -8
- package/dist/commands/eval.js.map +1 -1
- package/dist/commands/findings.d.ts +7 -0
- package/dist/commands/findings.d.ts.map +1 -1
- package/dist/commands/findings.js +4 -4
- package/dist/commands/findings.js.map +1 -1
- package/dist/commands/ide.d.ts.map +1 -1
- package/dist/commands/ide.js +25 -2
- package/dist/commands/ide.js.map +1 -1
- package/dist/commands/kanban.js +6 -6
- package/dist/commands/kanban.js.map +1 -1
- package/dist/commands/linear.d.ts.map +1 -1
- package/dist/commands/linear.js +24 -0
- package/dist/commands/linear.js.map +1 -1
- package/dist/commands/pi.d.ts +3 -0
- package/dist/commands/pi.d.ts.map +1 -1
- package/dist/commands/pi.js +19 -0
- package/dist/commands/pi.js.map +1 -1
- package/dist/commands/portfolio.d.ts +5 -0
- package/dist/commands/portfolio.d.ts.map +1 -1
- package/dist/commands/portfolio.js +193 -203
- package/dist/commands/portfolio.js.map +1 -1
- package/dist/commands/predict.d.ts +19 -0
- package/dist/commands/predict.d.ts.map +1 -1
- package/dist/commands/predict.js +4 -4
- package/dist/commands/predict.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +106 -7
- package/dist/commands/setup.js.map +1 -1
- package/dist/commands/start.d.ts +25 -0
- package/dist/commands/start.d.ts.map +1 -0
- package/dist/commands/start.js +191 -0
- package/dist/commands/start.js.map +1 -0
- package/dist/commands/tenet-agents.js +2 -2
- package/dist/commands/tenet-agents.js.map +1 -1
- package/dist/commands/tenet-setup.d.ts +2 -1
- package/dist/commands/tenet-setup.d.ts.map +1 -1
- package/dist/commands/tenet-setup.js +22 -18
- package/dist/commands/tenet-setup.js.map +1 -1
- package/dist/commands/viz.d.ts +33 -0
- package/dist/commands/viz.d.ts.map +1 -1
- package/dist/commands/viz.js +9 -9
- package/dist/commands/viz.js.map +1 -1
- package/dist/index.js +97 -43
- package/dist/index.js.map +1 -1
- package/dist/lib/advanced-setup.d.ts +1 -1
- package/dist/lib/advanced-setup.d.ts.map +1 -1
- package/dist/lib/advanced-setup.js +22 -22
- package/dist/lib/advanced-setup.js.map +1 -1
- package/dist/lib/discovery-agent.js +4 -4
- package/dist/lib/discovery-agent.js.map +1 -1
- package/dist/lib/linear-id-map.d.ts.map +1 -1
- package/dist/lib/linear-id-map.js +2 -0
- package/dist/lib/linear-id-map.js.map +1 -1
- package/dist/lib/linear-webhook.d.ts +50 -0
- package/dist/lib/linear-webhook.d.ts.map +1 -0
- package/dist/lib/linear-webhook.js +92 -0
- package/dist/lib/linear-webhook.js.map +1 -0
- package/dist/lib/onboarding.d.ts +1 -1
- package/dist/lib/onboarding.js +14 -14
- package/dist/lib/onboarding.js.map +1 -1
- package/dist/lib/rl-manager.d.ts +1 -1
- package/dist/lib/rl-manager.d.ts.map +1 -1
- package/dist/lib/rl-manager.js +5 -4
- package/dist/lib/rl-manager.js.map +1 -1
- package/dist/lib/setup/starter-intelligence.d.ts +25 -0
- package/dist/lib/setup/starter-intelligence.d.ts.map +1 -0
- package/dist/lib/setup/starter-intelligence.js +309 -0
- package/dist/lib/setup/starter-intelligence.js.map +1 -0
- package/dist/lib/tool-schemas.d.ts +35 -0
- package/dist/lib/tool-schemas.d.ts.map +1 -0
- package/dist/lib/tool-schemas.js +246 -0
- package/dist/lib/tool-schemas.js.map +1 -0
- package/dist/lib/workspace/data-pipeline.d.ts.map +1 -1
- package/dist/lib/workspace/data-pipeline.js +29 -20
- package/dist/lib/workspace/data-pipeline.js.map +1 -1
- package/dist/lib/workspace/engine.d.ts +1 -0
- package/dist/lib/workspace/engine.d.ts.map +1 -1
- package/dist/lib/workspace/engine.js +10 -0
- package/dist/lib/workspace/engine.js.map +1 -1
- package/dist/lib/workspace/surface-registry.d.ts.map +1 -1
- package/dist/lib/workspace/surface-registry.js +5 -0
- package/dist/lib/workspace/surface-registry.js.map +1 -1
- package/dist/lib/workspace/surfaces/sidebar.d.ts.map +1 -1
- package/dist/lib/workspace/surfaces/sidebar.js +5 -1
- package/dist/lib/workspace/surfaces/sidebar.js.map +1 -1
- package/dist/lib/workspace/tmux-adapter.d.ts +8 -5
- package/dist/lib/workspace/tmux-adapter.d.ts.map +1 -1
- package/dist/lib/workspace/tmux-adapter.js +38 -7
- package/dist/lib/workspace/tmux-adapter.js.map +1 -1
- package/dist/lib/workspace/tmux-sidebar.d.ts +14 -0
- package/dist/lib/workspace/tmux-sidebar.d.ts.map +1 -0
- package/dist/lib/workspace/tmux-sidebar.js +230 -0
- package/dist/lib/workspace/tmux-sidebar.js.map +1 -0
- package/dist/mcp/context-hub-mcp.js +7 -1
- package/dist/mcp/context-hub-mcp.js.map +1 -1
- package/dist/types/telemetry.d.ts +1 -0
- package/dist/types/telemetry.d.ts.map +1 -1
- package/dist/utils/jfl-config.d.ts +7 -2
- package/dist/utils/jfl-config.d.ts.map +1 -1
- package/dist/utils/jfl-config.js +14 -4
- package/dist/utils/jfl-config.js.map +1 -1
- package/package.json +1 -1
- package/packages/pi/assets/boot.mp3 +0 -0
- package/packages/pi/extensions/autoresearch.ts +3 -2
- package/packages/pi/extensions/context.ts +29 -66
- package/packages/pi/extensions/eval.ts +2 -1
- package/packages/pi/extensions/hub-tools.ts +267 -0
- package/packages/pi/extensions/hud-tool.ts +230 -69
- package/packages/pi/extensions/index.ts +43 -63
- package/packages/pi/extensions/jfl-resolve.ts +98 -0
- package/packages/pi/extensions/journal.ts +91 -6
- package/packages/pi/extensions/map-bridge.ts +31 -0
- package/packages/pi/extensions/memory-tool.ts +84 -4
- package/packages/pi/extensions/onboarding-v2.ts +367 -399
- package/packages/pi/extensions/peter-parker.ts +2 -1
- package/packages/pi/extensions/policy-head-tool.ts +3 -2
- package/packages/pi/extensions/portfolio-bridge.ts +3 -4
- package/packages/pi/extensions/service-skills.ts +214 -0
- package/packages/pi/extensions/session.ts +91 -15
- package/packages/pi/extensions/stratus-bridge.ts +2 -1
- package/packages/pi/extensions/synopsis-tool.ts +6 -1
- package/packages/pi/extensions/training-buffer-tool.ts +3 -2
- package/packages/pi/extensions/types.ts +2 -0
- package/packages/pi/package.json +3 -1
- package/packages/pi/skills/viz/SKILL.md +204 -0
package/dist/utils/jfl-config.js
CHANGED
|
@@ -101,10 +101,20 @@ export async function getCodeDirectory() {
|
|
|
101
101
|
// Project Detection
|
|
102
102
|
// ============================================================================
|
|
103
103
|
/**
|
|
104
|
-
* Get the project data directory name (.
|
|
105
|
-
*
|
|
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, ".
|
|
116
|
-
existsSync(join(cwd, ".
|
|
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
|
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
|
|
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
|
|
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
|
|
5
|
-
*
|
|
6
|
-
*
|
|
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 +
|
|
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
|
|
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
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
if (!
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
|
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
|
+
}
|