jfl 0.8.1 โ 0.9.1
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/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/doctor.d.ts +1 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/doctor.js +30 -1
- package/dist/commands/doctor.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 +2 -1
- package/dist/commands/ide.d.ts.map +1 -1
- package/dist/commands/ide.js +61 -1
- package/dist/commands/ide.js.map +1 -1
- package/dist/commands/init-from-service.d.ts +15 -0
- package/dist/commands/init-from-service.d.ts.map +1 -0
- package/dist/commands/init-from-service.js +541 -0
- package/dist/commands/init-from-service.js.map +1 -0
- package/dist/commands/init.d.ts +1 -0
- package/dist/commands/init.d.ts.map +1 -1
- package/dist/commands/init.js +32 -1
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/kanban.d.ts.map +1 -1
- package/dist/commands/kanban.js +13 -4
- package/dist/commands/kanban.js.map +1 -1
- package/dist/commands/linear.d.ts +41 -0
- package/dist/commands/linear.d.ts.map +1 -0
- package/dist/commands/linear.js +715 -0
- package/dist/commands/linear.js.map +1 -0
- package/dist/commands/peter.d.ts.map +1 -1
- package/dist/commands/peter.js +232 -25
- package/dist/commands/peter.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/services.d.ts.map +1 -1
- package/dist/commands/services.js +146 -0
- package/dist/commands/services.js.map +1 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/commands/setup.js +279 -20
- 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/telemetry-monitor.d.ts +11 -0
- package/dist/commands/telemetry-monitor.d.ts.map +1 -0
- package/dist/commands/telemetry-monitor.js +224 -0
- package/dist/commands/telemetry-monitor.js.map +1 -0
- package/dist/commands/telemetry-test.d.ts +11 -0
- package/dist/commands/telemetry-test.d.ts.map +1 -0
- package/dist/commands/telemetry-test.js +67 -0
- package/dist/commands/telemetry-test.js.map +1 -0
- package/dist/commands/tenet-agents.d.ts +13 -0
- package/dist/commands/tenet-agents.d.ts.map +1 -0
- package/dist/commands/tenet-agents.js +191 -0
- package/dist/commands/tenet-agents.js.map +1 -0
- package/dist/commands/tenet-setup.d.ts +20 -0
- package/dist/commands/tenet-setup.d.ts.map +1 -0
- package/dist/commands/tenet-setup.js +135 -0
- package/dist/commands/tenet-setup.js.map +1 -0
- package/dist/commands/train.d.ts +18 -0
- package/dist/commands/train.d.ts.map +1 -1
- package/dist/commands/train.js +182 -0
- package/dist/commands/train.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/commands/whoami.d.ts +2 -0
- package/dist/commands/whoami.d.ts.map +1 -0
- package/dist/commands/whoami.js +24 -0
- package/dist/commands/whoami.js.map +1 -0
- package/dist/index.js +230 -30
- package/dist/index.js.map +1 -1
- package/dist/lib/advanced-setup.d.ts +78 -0
- package/dist/lib/advanced-setup.d.ts.map +1 -0
- package/dist/lib/advanced-setup.js +433 -0
- package/dist/lib/advanced-setup.js.map +1 -0
- package/dist/lib/agent-config.d.ts +33 -0
- package/dist/lib/agent-config.d.ts.map +1 -1
- package/dist/lib/agent-config.js +26 -0
- package/dist/lib/agent-config.js.map +1 -1
- package/dist/lib/counterfactual-training-bridge.d.ts +114 -0
- package/dist/lib/counterfactual-training-bridge.d.ts.map +1 -0
- package/dist/lib/counterfactual-training-bridge.js +322 -0
- package/dist/lib/counterfactual-training-bridge.js.map +1 -0
- package/dist/lib/discovery-agent.d.ts +48 -0
- package/dist/lib/discovery-agent.d.ts.map +1 -0
- package/dist/lib/discovery-agent.js +111 -0
- package/dist/lib/discovery-agent.js.map +1 -0
- package/dist/lib/flow-engine.d.ts.map +1 -1
- package/dist/lib/flow-engine.js +46 -8
- package/dist/lib/flow-engine.js.map +1 -1
- package/dist/lib/gtm-generator.d.ts +29 -0
- package/dist/lib/gtm-generator.d.ts.map +1 -0
- package/dist/lib/gtm-generator.js +252 -0
- package/dist/lib/gtm-generator.js.map +1 -0
- package/dist/lib/hub-health.d.ts +40 -0
- package/dist/lib/hub-health.d.ts.map +1 -0
- package/dist/lib/hub-health.js +89 -0
- package/dist/lib/hub-health.js.map +1 -0
- package/dist/lib/invariant-monitor.d.ts +6 -2
- package/dist/lib/invariant-monitor.d.ts.map +1 -1
- package/dist/lib/invariant-monitor.js +89 -2
- package/dist/lib/invariant-monitor.js.map +1 -1
- package/dist/lib/journal-analyzer.d.ts +71 -0
- package/dist/lib/journal-analyzer.d.ts.map +1 -0
- package/dist/lib/journal-analyzer.js +306 -0
- package/dist/lib/journal-analyzer.js.map +1 -0
- package/dist/lib/linear-client.d.ts +73 -0
- package/dist/lib/linear-client.d.ts.map +1 -0
- package/dist/lib/linear-client.js +112 -0
- package/dist/lib/linear-client.js.map +1 -0
- package/dist/lib/linear-id-map.d.ts +20 -0
- package/dist/lib/linear-id-map.d.ts.map +1 -0
- package/dist/lib/linear-id-map.js +59 -0
- package/dist/lib/linear-id-map.js.map +1 -0
- package/dist/lib/linear-kanban.d.ts +66 -0
- package/dist/lib/linear-kanban.d.ts.map +1 -0
- package/dist/lib/linear-kanban.js +175 -0
- package/dist/lib/linear-kanban.js.map +1 -0
- package/dist/lib/onboarding.d.ts +40 -0
- package/dist/lib/onboarding.d.ts.map +1 -0
- package/dist/lib/onboarding.js +213 -0
- package/dist/lib/onboarding.js.map +1 -0
- package/dist/lib/physical-world-model.d.ts +50 -0
- package/dist/lib/physical-world-model.d.ts.map +1 -0
- package/dist/lib/physical-world-model.js +251 -0
- package/dist/lib/physical-world-model.js.map +1 -0
- package/dist/lib/planning-loop.d.ts +157 -0
- package/dist/lib/planning-loop.d.ts.map +1 -0
- package/dist/lib/planning-loop.js +537 -0
- package/dist/lib/planning-loop.js.map +1 -0
- package/dist/lib/policy-head.d.ts +13 -0
- package/dist/lib/policy-head.d.ts.map +1 -1
- package/dist/lib/policy-head.js +168 -2
- package/dist/lib/policy-head.js.map +1 -1
- package/dist/lib/resource-optimizer-middleware.d.ts +39 -0
- package/dist/lib/resource-optimizer-middleware.d.ts.map +1 -0
- package/dist/lib/resource-optimizer-middleware.js +222 -0
- package/dist/lib/resource-optimizer-middleware.js.map +1 -0
- package/dist/lib/resource-optimizer.d.ts +71 -0
- package/dist/lib/resource-optimizer.d.ts.map +1 -0
- package/dist/lib/resource-optimizer.js +228 -0
- package/dist/lib/resource-optimizer.js.map +1 -0
- package/dist/lib/rl-manager.d.ts +74 -0
- package/dist/lib/rl-manager.d.ts.map +1 -0
- package/dist/lib/rl-manager.js +245 -0
- package/dist/lib/rl-manager.js.map +1 -0
- package/dist/lib/service-analyzer.d.ts +76 -0
- package/dist/lib/service-analyzer.d.ts.map +1 -0
- package/dist/lib/service-analyzer.js +704 -0
- package/dist/lib/service-analyzer.js.map +1 -0
- package/dist/lib/service-gtm.js +2 -2
- package/dist/lib/service-gtm.js.map +1 -1
- package/dist/lib/service-questionnaire.d.ts +11 -0
- package/dist/lib/service-questionnaire.d.ts.map +1 -0
- package/dist/lib/service-questionnaire.js +89 -0
- package/dist/lib/service-questionnaire.js.map +1 -0
- package/dist/lib/setup/agent-generator.d.ts +2 -0
- package/dist/lib/setup/agent-generator.d.ts.map +1 -1
- package/dist/lib/setup/agent-generator.js +128 -4
- package/dist/lib/setup/agent-generator.js.map +1 -1
- package/dist/lib/setup/flow-generator.d.ts +10 -0
- package/dist/lib/setup/flow-generator.d.ts.map +1 -0
- package/dist/lib/setup/flow-generator.js +113 -0
- package/dist/lib/setup/flow-generator.js.map +1 -0
- package/dist/lib/setup/invariant-bridge.d.ts +91 -0
- package/dist/lib/setup/invariant-bridge.d.ts.map +1 -0
- package/dist/lib/setup/invariant-bridge.js +384 -0
- package/dist/lib/setup/invariant-bridge.js.map +1 -0
- package/dist/lib/setup/spec-generator.d.ts +41 -5
- package/dist/lib/setup/spec-generator.d.ts.map +1 -1
- package/dist/lib/setup/spec-generator.js +503 -29
- package/dist/lib/setup/spec-generator.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/stratus-client.js +1 -1
- package/dist/lib/stratus-client.js.map +1 -1
- package/dist/lib/surface-agent.d.ts +78 -0
- package/dist/lib/surface-agent.d.ts.map +1 -0
- package/dist/lib/surface-agent.js +105 -0
- package/dist/lib/surface-agent.js.map +1 -0
- package/dist/lib/surface-coordination-example.d.ts +30 -0
- package/dist/lib/surface-coordination-example.d.ts.map +1 -0
- package/dist/lib/surface-coordination-example.js +164 -0
- package/dist/lib/surface-coordination-example.js.map +1 -0
- package/dist/lib/telemetry/physical-world-collector.d.ts +15 -0
- package/dist/lib/telemetry/physical-world-collector.d.ts.map +1 -0
- package/dist/lib/telemetry/physical-world-collector.js +177 -0
- package/dist/lib/telemetry/physical-world-collector.js.map +1 -0
- package/dist/lib/telemetry/training-bridge.d.ts +51 -0
- package/dist/lib/telemetry/training-bridge.d.ts.map +1 -0
- package/dist/lib/telemetry/training-bridge.js +185 -0
- package/dist/lib/telemetry/training-bridge.js.map +1 -0
- package/dist/lib/telemetry.d.ts +2 -1
- package/dist/lib/telemetry.d.ts.map +1 -1
- package/dist/lib/telemetry.js +23 -2
- package/dist/lib/telemetry.js.map +1 -1
- package/dist/lib/tenet-board-agent.d.ts +52 -0
- package/dist/lib/tenet-board-agent.d.ts.map +1 -0
- package/dist/lib/tenet-board-agent.js +226 -0
- package/dist/lib/tenet-board-agent.js.map +1 -0
- package/dist/lib/tenet-ide-agent.d.ts +40 -0
- package/dist/lib/tenet-ide-agent.d.ts.map +1 -0
- package/dist/lib/tenet-ide-agent.js +199 -0
- package/dist/lib/tenet-ide-agent.js.map +1 -0
- package/dist/lib/workspace/data-pipeline.d.ts.map +1 -1
- package/dist/lib/workspace/data-pipeline.js +27 -5
- package/dist/lib/workspace/data-pipeline.js.map +1 -1
- package/dist/lib/workspace/sidebar-runner.d.ts +13 -0
- package/dist/lib/workspace/sidebar-runner.d.ts.map +1 -0
- package/dist/lib/workspace/sidebar-runner.js +419 -0
- package/dist/lib/workspace/sidebar-runner.js.map +1 -0
- package/dist/lib/workspace/surface-registry.d.ts.map +1 -1
- package/dist/lib/workspace/surface-registry.js +9 -1
- package/dist/lib/workspace/surface-registry.js.map +1 -1
- package/dist/lib/workspace/surfaces/agent-overview.d.ts +3 -3
- package/dist/lib/workspace/surfaces/agent-overview.d.ts.map +1 -1
- package/dist/lib/workspace/surfaces/agent-overview.js +3 -3
- package/dist/lib/workspace/surfaces/agent-overview.js.map +1 -1
- package/dist/lib/workspace/surfaces/index.d.ts +3 -0
- package/dist/lib/workspace/surfaces/index.d.ts.map +1 -1
- package/dist/lib/workspace/surfaces/index.js +3 -0
- package/dist/lib/workspace/surfaces/index.js.map +1 -1
- package/dist/lib/workspace/surfaces/kanban.d.ts +15 -0
- package/dist/lib/workspace/surfaces/kanban.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/kanban.js +43 -0
- package/dist/lib/workspace/surfaces/kanban.js.map +1 -0
- package/dist/lib/workspace/surfaces/physical-world.d.ts +15 -0
- package/dist/lib/workspace/surfaces/physical-world.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/physical-world.js +37 -0
- package/dist/lib/workspace/surfaces/physical-world.js.map +1 -0
- package/dist/lib/workspace/surfaces/sidebar.d.ts +22 -0
- package/dist/lib/workspace/surfaces/sidebar.d.ts.map +1 -0
- package/dist/lib/workspace/surfaces/sidebar.js +94 -0
- package/dist/lib/workspace/surfaces/sidebar.js.map +1 -0
- 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/types/flows.d.ts +2 -1
- package/dist/types/flows.d.ts.map +1 -1
- package/dist/types/physical-world-model.d.ts +65 -0
- package/dist/types/physical-world-model.d.ts.map +1 -0
- package/dist/types/physical-world-model.js +43 -0
- package/dist/types/physical-world-model.js.map +1 -0
- package/dist/types/telemetry.d.ts +37 -0
- package/dist/types/telemetry.d.ts.map +1 -1
- package/dist/types/world-model.d.ts.map +1 -1
- package/dist/types/world-model.js +14 -7
- package/dist/types/world-model.js.map +1 -1
- package/dist/utils/context-hub-port.d.ts.map +1 -1
- package/dist/utils/context-hub-port.js +6 -1
- package/dist/utils/context-hub-port.js.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 +3 -2
- package/packages/pi/extensions/context.ts +51 -1
- package/packages/pi/extensions/hub-tools.ts +247 -0
- package/packages/pi/extensions/index.ts +38 -6
- package/packages/pi/extensions/memory-tool.ts +84 -4
- package/packages/pi/extensions/service-skills.ts +214 -0
- package/scripts/telemetry-dashboard.sh +44 -0
- package/scripts/test-planning-loop-e2e.ts +181 -0
- package/scripts/test-server-inference.ts +49 -0
- package/scripts/test-state-sensitivity.ts +32 -0
- package/scripts/train/v2/benchmark.py +661 -0
- package/scripts/train/v2/generate_balanced.py +439 -0
- package/scripts/train/v2/generate_hard_negatives.py +219 -0
- package/scripts/train/v2/infer.py +149 -36
- package/scripts/train/v2/infer_server.py +224 -0
- package/scripts/train/v2/online_train.py +576 -0
- package/scripts/train/v2/precompute.py +24 -6
- package/template/CLAUDE.md +74 -132
|
@@ -0,0 +1,247 @@
|
|
|
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
|
+
|
|
16
|
+
async function hubFetch(path: string, method: "GET" | "POST" = "GET", body?: unknown): Promise<any> {
|
|
17
|
+
const resp = await fetch(`${hubUrl}${path}`, {
|
|
18
|
+
method,
|
|
19
|
+
headers: {
|
|
20
|
+
"Content-Type": "application/json",
|
|
21
|
+
...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
|
|
22
|
+
},
|
|
23
|
+
...(body ? { body: JSON.stringify(body) } : {}),
|
|
24
|
+
signal: AbortSignal.timeout(5000),
|
|
25
|
+
})
|
|
26
|
+
if (!resp.ok) throw new Error(`Hub ${path}: ${resp.status}`)
|
|
27
|
+
return resp.json()
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export async function setupHubTools(ctx: PiContext, _config: JflConfig): Promise<void> {
|
|
31
|
+
|
|
32
|
+
// โโโ jfl_events_publish โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
33
|
+
ctx.registerTool({
|
|
34
|
+
name: "jfl_events_publish",
|
|
35
|
+
description: "Publish an event to the MAP event bus. Use to signal state changes, completions, or trigger flows.",
|
|
36
|
+
promptSnippet: "Publish event to MAP bus",
|
|
37
|
+
inputSchema: {
|
|
38
|
+
type: "object",
|
|
39
|
+
properties: {
|
|
40
|
+
type: {
|
|
41
|
+
type: "string",
|
|
42
|
+
description: "Event type (e.g. 'task:completed', 'eval:scored', 'custom:my-event')",
|
|
43
|
+
},
|
|
44
|
+
data: {
|
|
45
|
+
type: "string",
|
|
46
|
+
description: "Event data as JSON string",
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
required: ["type"],
|
|
50
|
+
},
|
|
51
|
+
async handler(input) {
|
|
52
|
+
const { type, data } = input as { type: string; data?: string }
|
|
53
|
+
try {
|
|
54
|
+
let eventData: unknown = {}
|
|
55
|
+
if (data) {
|
|
56
|
+
try { eventData = JSON.parse(data) } catch { eventData = { message: data } }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
await hubFetch("/api/events", "POST", {
|
|
60
|
+
type,
|
|
61
|
+
source: `pi:${ctx.session.id}`,
|
|
62
|
+
data: eventData,
|
|
63
|
+
ts: new Date().toISOString(),
|
|
64
|
+
})
|
|
65
|
+
return `Event published: ${type}`
|
|
66
|
+
} catch {
|
|
67
|
+
return "Event publish failed โ Context Hub may not be running."
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
// โโโ jfl_events_recent โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
73
|
+
ctx.registerTool({
|
|
74
|
+
name: "jfl_events_recent",
|
|
75
|
+
description: "Get recent events from the MAP event bus. Check what happened in the system โ agent actions, eval results, flow triggers.",
|
|
76
|
+
promptSnippet: "View recent MAP events",
|
|
77
|
+
inputSchema: {
|
|
78
|
+
type: "object",
|
|
79
|
+
properties: {
|
|
80
|
+
limit: {
|
|
81
|
+
type: "number",
|
|
82
|
+
description: "Number of events to return (default: 20)",
|
|
83
|
+
},
|
|
84
|
+
type: {
|
|
85
|
+
type: "string",
|
|
86
|
+
description: "Filter by event type prefix (e.g. 'eval:', 'hook:', 'task:')",
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
async handler(input) {
|
|
91
|
+
const { limit, type } = input as { limit?: number; type?: string }
|
|
92
|
+
try {
|
|
93
|
+
const params = new URLSearchParams()
|
|
94
|
+
if (limit) params.set("limit", String(limit))
|
|
95
|
+
if (type) params.set("type", type)
|
|
96
|
+
|
|
97
|
+
const data = await hubFetch(`/api/events?${params}`)
|
|
98
|
+
const events = data.events ?? data ?? []
|
|
99
|
+
|
|
100
|
+
if (!Array.isArray(events) || events.length === 0) return "No recent events."
|
|
101
|
+
|
|
102
|
+
return events
|
|
103
|
+
.slice(0, limit ?? 20)
|
|
104
|
+
.map((e: any) => {
|
|
105
|
+
const ts = e.ts ? new Date(e.ts).toLocaleTimeString() : "?"
|
|
106
|
+
const src = e.source ?? "?"
|
|
107
|
+
return `[${ts}] ${e.type} (${src})${e.data ? " โ " + JSON.stringify(e.data).slice(0, 100) : ""}`
|
|
108
|
+
})
|
|
109
|
+
.join("\n")
|
|
110
|
+
} catch {
|
|
111
|
+
return "Events unavailable โ Context Hub may not be running."
|
|
112
|
+
}
|
|
113
|
+
},
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
// โโโ jfl_context_status โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
117
|
+
ctx.registerTool({
|
|
118
|
+
name: "jfl_context_status",
|
|
119
|
+
description: "Check Context Hub status โ available sources, memory index health, connected services.",
|
|
120
|
+
promptSnippet: "Check Context Hub health",
|
|
121
|
+
inputSchema: {
|
|
122
|
+
type: "object",
|
|
123
|
+
properties: {},
|
|
124
|
+
},
|
|
125
|
+
async handler() {
|
|
126
|
+
try {
|
|
127
|
+
const data = await hubFetch("/api/context/status")
|
|
128
|
+
|
|
129
|
+
const lines: string[] = ["Context Hub Status:"]
|
|
130
|
+
for (const [k, v] of Object.entries(data)) {
|
|
131
|
+
if (typeof v === "object" && v !== null) {
|
|
132
|
+
lines.push(` ${k}: ${JSON.stringify(v)}`)
|
|
133
|
+
} else {
|
|
134
|
+
lines.push(` ${k}: ${v}`)
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return lines.join("\n")
|
|
138
|
+
} catch {
|
|
139
|
+
return "Context Hub status unavailable."
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
// โโโ jfl_context_sessions โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
145
|
+
ctx.registerTool({
|
|
146
|
+
name: "jfl_context_sessions",
|
|
147
|
+
description: "See activity from other sessions โ what other agents/users are working on. Informational only.",
|
|
148
|
+
promptSnippet: "View other session activity",
|
|
149
|
+
inputSchema: {
|
|
150
|
+
type: "object",
|
|
151
|
+
properties: {
|
|
152
|
+
limit: {
|
|
153
|
+
type: "number",
|
|
154
|
+
description: "Number of sessions to return (default: 5)",
|
|
155
|
+
},
|
|
156
|
+
},
|
|
157
|
+
},
|
|
158
|
+
async handler(input) {
|
|
159
|
+
const { limit } = input as { limit?: number }
|
|
160
|
+
try {
|
|
161
|
+
// Try the sessions endpoint
|
|
162
|
+
const data = await hubFetch(`/api/context/sessions?limit=${limit ?? 5}`)
|
|
163
|
+
const sessions = data.sessions ?? data ?? []
|
|
164
|
+
|
|
165
|
+
if (!Array.isArray(sessions) || sessions.length === 0) return "No other sessions found."
|
|
166
|
+
|
|
167
|
+
return sessions
|
|
168
|
+
.map((s: any) => {
|
|
169
|
+
const age = s.lastSeen ? `(${new Date(s.lastSeen).toLocaleString()})` : ""
|
|
170
|
+
return `${s.branch ?? s.id ?? "?"} ${age} โ ${s.summary ?? s.status ?? "active"}`
|
|
171
|
+
})
|
|
172
|
+
.join("\n")
|
|
173
|
+
} catch {
|
|
174
|
+
// Fallback: read journal files to find other sessions
|
|
175
|
+
try {
|
|
176
|
+
const { execSync } = await import("child_process")
|
|
177
|
+
const journalDir = `${ctx.session.projectRoot}/.jfl/journal`
|
|
178
|
+
const files = execSync(`ls -t ${journalDir}/*.jsonl 2>/dev/null | head -5`, {
|
|
179
|
+
encoding: "utf-8",
|
|
180
|
+
}).trim()
|
|
181
|
+
|
|
182
|
+
if (!files) return "No other sessions found."
|
|
183
|
+
|
|
184
|
+
return files.split("\n")
|
|
185
|
+
.map(f => {
|
|
186
|
+
const branch = f.split("/").pop()?.replace(".jsonl", "") ?? "?"
|
|
187
|
+
return `Session: ${branch}`
|
|
188
|
+
})
|
|
189
|
+
.join("\n")
|
|
190
|
+
} catch {
|
|
191
|
+
return "Sessions unavailable."
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
// โโโ jfl_experiment_history โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
198
|
+
ctx.registerTool({
|
|
199
|
+
name: "jfl_experiment_history",
|
|
200
|
+
description: "Search past experiments, their outcomes, and what was learned. Useful for avoiding repeat experiments.",
|
|
201
|
+
promptSnippet: "Search experiment history",
|
|
202
|
+
inputSchema: {
|
|
203
|
+
type: "object",
|
|
204
|
+
properties: {
|
|
205
|
+
query: {
|
|
206
|
+
type: "string",
|
|
207
|
+
description: "Search query for experiment history",
|
|
208
|
+
},
|
|
209
|
+
limit: {
|
|
210
|
+
type: "number",
|
|
211
|
+
description: "Max results (default: 10)",
|
|
212
|
+
},
|
|
213
|
+
},
|
|
214
|
+
required: ["query"],
|
|
215
|
+
},
|
|
216
|
+
async handler(input) {
|
|
217
|
+
const { query, limit } = input as { query: string; limit?: number }
|
|
218
|
+
try {
|
|
219
|
+
const data = await hubFetch("/api/eval/trajectory", "GET")
|
|
220
|
+
const entries = data.entries ?? data ?? []
|
|
221
|
+
|
|
222
|
+
if (!Array.isArray(entries) || entries.length === 0) return "No experiment history found."
|
|
223
|
+
|
|
224
|
+
// Filter by query (simple text match)
|
|
225
|
+
const queryLower = query.toLowerCase()
|
|
226
|
+
const filtered = entries
|
|
227
|
+
.filter((e: any) => {
|
|
228
|
+
const text = JSON.stringify(e).toLowerCase()
|
|
229
|
+
return text.includes(queryLower)
|
|
230
|
+
})
|
|
231
|
+
.slice(0, limit ?? 10)
|
|
232
|
+
|
|
233
|
+
if (filtered.length === 0) return `No experiments matching "${query}".`
|
|
234
|
+
|
|
235
|
+
return filtered
|
|
236
|
+
.map((e: any) => {
|
|
237
|
+
const ts = e.ts ? new Date(e.ts).toLocaleDateString() : "?"
|
|
238
|
+
const score = e.composite != null ? ` (score: ${e.composite})` : ""
|
|
239
|
+
return `[${ts}]${score} ${e.label ?? e.summary ?? JSON.stringify(e).slice(0, 100)}`
|
|
240
|
+
})
|
|
241
|
+
.join("\n")
|
|
242
|
+
} catch {
|
|
243
|
+
return "Experiment history unavailable โ Context Hub may not be running."
|
|
244
|
+
}
|
|
245
|
+
},
|
|
246
|
+
})
|
|
247
|
+
}
|
|
@@ -32,6 +32,8 @@ import { setupEvalTool } from "./eval-tool.js"
|
|
|
32
32
|
import { setupPolicyHeadTool } from "./policy-head-tool.js"
|
|
33
33
|
import { setupTrainingBufferTool, onTrainingAgentEnd } from "./training-buffer-tool.js"
|
|
34
34
|
import { setupAutoresearch } from "./autoresearch.js"
|
|
35
|
+
import { setupServiceSkills } from "./service-skills.js"
|
|
36
|
+
import { setupHubTools } from "./hub-tools.js"
|
|
35
37
|
import { setupFooter } from "./footer.js"
|
|
36
38
|
import { setupShortcuts } from "./shortcuts.js"
|
|
37
39
|
import { setupNotifications } from "./notifications.js"
|
|
@@ -319,6 +321,8 @@ export default async function jflExtension(pi: any): Promise<void> {
|
|
|
319
321
|
await setupPolicyHeadTool(ctx, config)
|
|
320
322
|
await setupTrainingBufferTool(ctx, config)
|
|
321
323
|
await setupAutoresearch(ctx, config)
|
|
324
|
+
await setupServiceSkills(ctx, config)
|
|
325
|
+
await setupHubTools(ctx, config)
|
|
322
326
|
|
|
323
327
|
initStratusBridge(projectCwd)
|
|
324
328
|
initAgentNames(projectCwd)
|
|
@@ -347,15 +351,19 @@ export default async function jflExtension(pi: any): Promise<void> {
|
|
|
347
351
|
content: [
|
|
348
352
|
`JFL session ready: "${projectName}" on branch ${ctx.session.branch}.`,
|
|
349
353
|
"",
|
|
350
|
-
"
|
|
351
|
-
"
|
|
354
|
+
"Your system prompt already contains full project context: CLAUDE.md, recent journal entries, knowledge docs, and code headers.",
|
|
355
|
+
"Everything was loaded during the boot screen. You do NOT need to call any tools.",
|
|
352
356
|
"",
|
|
353
|
-
"
|
|
354
|
-
"
|
|
357
|
+
"DO NOT call jfl_context, jfl_hud, jfl_memory_search, or run any bash commands at startup.",
|
|
358
|
+
"DO NOT run session-sync.sh, jfl-doctor.sh, or read journal files.",
|
|
359
|
+
"All of that is already done and injected into your system prompt.",
|
|
360
|
+
"",
|
|
361
|
+
"Just greet the user naturally with a brief status (3-5 lines):",
|
|
362
|
+
"- What was worked on recently (from the journal entries in your system prompt)",
|
|
355
363
|
"- Current phase and any blockers",
|
|
356
364
|
"- A suggested next action",
|
|
357
365
|
"",
|
|
358
|
-
"
|
|
366
|
+
"No tool calls. No setup noise. Just talk.",
|
|
359
367
|
"Write journal entries as you work. Capture decisions immediately.",
|
|
360
368
|
].join("\n"),
|
|
361
369
|
display: false,
|
|
@@ -380,7 +388,31 @@ export default async function jflExtension(pi: any): Promise<void> {
|
|
|
380
388
|
latestPiCtx = piCtx
|
|
381
389
|
const result = await injectContext(ctx, event)
|
|
382
390
|
if (result?.systemPromptAddition) {
|
|
383
|
-
|
|
391
|
+
let current = piCtx.getSystemPrompt?.() ?? ""
|
|
392
|
+
|
|
393
|
+
// Strip Path B (Claude Code manual startup) from system prompt.
|
|
394
|
+
// We're running in Pi with the extension โ Path B instructions are
|
|
395
|
+
// noise that can confuse the LLM into running manual startup commands.
|
|
396
|
+
const pathBStart = "### Path B: Claude Code / No Extension"
|
|
397
|
+
const pathBEnd = "### How to Tell Which Path You're On"
|
|
398
|
+
const startIdx = current.indexOf(pathBStart)
|
|
399
|
+
const endIdx = current.indexOf(pathBEnd)
|
|
400
|
+
if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
|
|
401
|
+
current = current.slice(0, startIdx) + current.slice(endIdx)
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
// Also strip the "How to Tell" section โ it references Path B
|
|
405
|
+
const howToTell = "### How to Tell Which Path You're On"
|
|
406
|
+
const howToTellIdx = current.indexOf(howToTell)
|
|
407
|
+
if (howToTellIdx !== -1) {
|
|
408
|
+
// Find the next ### or ## heading after it
|
|
409
|
+
const afterHowToTell = current.slice(howToTellIdx + howToTell.length)
|
|
410
|
+
const nextHeading = afterHowToTell.search(/\n###? /)
|
|
411
|
+
if (nextHeading !== -1) {
|
|
412
|
+
current = current.slice(0, howToTellIdx) + afterHowToTell.slice(nextHeading)
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
384
416
|
return {
|
|
385
417
|
systemPrompt: current
|
|
386
418
|
? `${current}\n\n${result.systemPromptAddition}`
|
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Memory Tool Extension
|
|
3
3
|
*
|
|
4
|
-
* Registers jfl_memory_search
|
|
5
|
-
*
|
|
6
|
-
* type-colored headers and collapsible sections.
|
|
4
|
+
* Registers jfl_memory_search, jfl_memory_add, and jfl_memory_status tools.
|
|
5
|
+
* Full parity with the Context Hub MCP server's memory tools.
|
|
7
6
|
*
|
|
8
|
-
* @purpose
|
|
7
|
+
* @purpose Memory tools โ search, add, and status for project memory
|
|
9
8
|
*/
|
|
10
9
|
|
|
11
10
|
import type { PiContext } from "./types.js"
|
|
@@ -13,6 +12,7 @@ import { hubUrl, authToken } from "./map-bridge.js"
|
|
|
13
12
|
import { memoryRenderCall, memoryRenderResult } from "./tool-renderers.js"
|
|
14
13
|
|
|
15
14
|
export function setupMemoryTool(ctx: PiContext): void {
|
|
15
|
+
// โโโ jfl_memory_search โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
16
16
|
ctx.registerTool({
|
|
17
17
|
name: "jfl_memory_search",
|
|
18
18
|
description: "Search JFL project memory โ find past decisions, learnings, and patterns across all sessions",
|
|
@@ -70,4 +70,84 @@ export function setupMemoryTool(ctx: PiContext): void {
|
|
|
70
70
|
renderCall: memoryRenderCall,
|
|
71
71
|
renderResult: memoryRenderResult,
|
|
72
72
|
})
|
|
73
|
+
|
|
74
|
+
// โโโ jfl_memory_add โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
75
|
+
ctx.registerTool({
|
|
76
|
+
name: "jfl_memory_add",
|
|
77
|
+
description: "Manually add a memory or note to the project memory system. Use to capture insights, decisions, or important context that should persist across sessions.",
|
|
78
|
+
promptSnippet: "Add a memory/note to project memory",
|
|
79
|
+
inputSchema: {
|
|
80
|
+
type: "object",
|
|
81
|
+
properties: {
|
|
82
|
+
title: {
|
|
83
|
+
type: "string",
|
|
84
|
+
description: "Title for the memory entry",
|
|
85
|
+
},
|
|
86
|
+
content: {
|
|
87
|
+
type: "string",
|
|
88
|
+
description: "Content of the memory โ the insight, decision, or note",
|
|
89
|
+
},
|
|
90
|
+
type: {
|
|
91
|
+
type: "string",
|
|
92
|
+
description: "Type of memory entry",
|
|
93
|
+
enum: ["decision", "discovery", "insight", "note"],
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
required: ["title", "content"],
|
|
97
|
+
},
|
|
98
|
+
async handler(input) {
|
|
99
|
+
const { title, content, type } = input as { title: string; content: string; type?: string }
|
|
100
|
+
|
|
101
|
+
try {
|
|
102
|
+
const resp = await fetch(`${hubUrl}/api/memory/add`, {
|
|
103
|
+
method: "POST",
|
|
104
|
+
headers: {
|
|
105
|
+
"Content-Type": "application/json",
|
|
106
|
+
...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
|
|
107
|
+
},
|
|
108
|
+
body: JSON.stringify({ title, content, type: type ?? "note" }),
|
|
109
|
+
signal: AbortSignal.timeout(5000),
|
|
110
|
+
})
|
|
111
|
+
|
|
112
|
+
if (!resp.ok) return "Failed to add memory โ hub returned error."
|
|
113
|
+
const data = await resp.json() as { ok?: boolean; id?: string }
|
|
114
|
+
return data.ok
|
|
115
|
+
? `Memory added: "${title}" (${type ?? "note"})`
|
|
116
|
+
: "Memory add returned unexpected response."
|
|
117
|
+
} catch {
|
|
118
|
+
return "Memory add unavailable โ Context Hub may not be running."
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
})
|
|
122
|
+
|
|
123
|
+
// โโโ jfl_memory_status โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
124
|
+
ctx.registerTool({
|
|
125
|
+
name: "jfl_memory_status",
|
|
126
|
+
description: "Get memory system statistics and health โ total entries, indexed count, embedding status.",
|
|
127
|
+
promptSnippet: "Check memory system health and stats",
|
|
128
|
+
inputSchema: {
|
|
129
|
+
type: "object",
|
|
130
|
+
properties: {},
|
|
131
|
+
},
|
|
132
|
+
async handler() {
|
|
133
|
+
try {
|
|
134
|
+
const resp = await fetch(`${hubUrl}/api/memory/status`, {
|
|
135
|
+
method: "GET",
|
|
136
|
+
headers: {
|
|
137
|
+
...(authToken ? { Authorization: `Bearer ${authToken}` } : {}),
|
|
138
|
+
},
|
|
139
|
+
signal: AbortSignal.timeout(5000),
|
|
140
|
+
})
|
|
141
|
+
|
|
142
|
+
if (!resp.ok) return "Memory status unavailable."
|
|
143
|
+
const data = await resp.json() as Record<string, unknown>
|
|
144
|
+
|
|
145
|
+
return Object.entries(data)
|
|
146
|
+
.map(([k, v]) => `${k}: ${typeof v === "object" ? JSON.stringify(v) : v}`)
|
|
147
|
+
.join("\n")
|
|
148
|
+
} catch {
|
|
149
|
+
return "Memory status unavailable โ Context Hub may not be running."
|
|
150
|
+
}
|
|
151
|
+
},
|
|
152
|
+
})
|
|
73
153
|
}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service Skills Bridge
|
|
3
|
+
*
|
|
4
|
+
* Scans .claude/skills/ for service-type skills (generated during jfl init
|
|
5
|
+
* service onboarding) and registers them as Pi commands + tools.
|
|
6
|
+
* This gives Pi parity with Claude Code's service agent access.
|
|
7
|
+
*
|
|
8
|
+
* Service skills in .claude/skills/ have frontmatter `type: service` and
|
|
9
|
+
* include commands like status, logs, start, stop, restart, health.
|
|
10
|
+
*
|
|
11
|
+
* @purpose Bridge .claude/skills/ service agents into Pi sessions
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { existsSync, readdirSync, readFileSync } from "fs"
|
|
15
|
+
import { join } from "path"
|
|
16
|
+
import { execSync } from "child_process"
|
|
17
|
+
import type { PiContext, JflConfig } from "./types.js"
|
|
18
|
+
|
|
19
|
+
interface ServiceSkill {
|
|
20
|
+
name: string
|
|
21
|
+
description: string
|
|
22
|
+
servicePath: string
|
|
23
|
+
commands: string[]
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function parseFrontmatter(content: string): Record<string, string> {
|
|
27
|
+
const match = content.match(/^---\n([\s\S]*?)\n---/)
|
|
28
|
+
if (!match) return {}
|
|
29
|
+
const fm: Record<string, string> = {}
|
|
30
|
+
for (const line of match[1].split("\n")) {
|
|
31
|
+
const [key, ...rest] = line.split(":")
|
|
32
|
+
if (key && rest.length) {
|
|
33
|
+
fm[key.trim()] = rest.join(":").trim()
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return fm
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function discoverServiceSkills(projectRoot: string): ServiceSkill[] {
|
|
40
|
+
const claudeSkillsDir = join(projectRoot, ".claude", "skills")
|
|
41
|
+
if (!existsSync(claudeSkillsDir)) return []
|
|
42
|
+
|
|
43
|
+
const skills: ServiceSkill[] = []
|
|
44
|
+
|
|
45
|
+
try {
|
|
46
|
+
const entries = readdirSync(claudeSkillsDir, { withFileTypes: true })
|
|
47
|
+
|
|
48
|
+
for (const entry of entries) {
|
|
49
|
+
if (!entry.isDirectory()) continue
|
|
50
|
+
|
|
51
|
+
const skillPath = join(claudeSkillsDir, entry.name, "SKILL.md")
|
|
52
|
+
if (!existsSync(skillPath)) continue
|
|
53
|
+
|
|
54
|
+
try {
|
|
55
|
+
const content = readFileSync(skillPath, "utf-8")
|
|
56
|
+
const fm = parseFrontmatter(content)
|
|
57
|
+
|
|
58
|
+
if (fm.type !== "service") continue
|
|
59
|
+
|
|
60
|
+
// Extract available commands from the markdown table
|
|
61
|
+
const commands: string[] = []
|
|
62
|
+
const cmdMatch = content.match(/\| `(\w+)` \|/g)
|
|
63
|
+
if (cmdMatch) {
|
|
64
|
+
for (const m of cmdMatch) {
|
|
65
|
+
const cmd = m.match(/`(\w+)`/)
|
|
66
|
+
if (cmd) commands.push(cmd[1])
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// Fallback defaults
|
|
70
|
+
if (commands.length === 0) {
|
|
71
|
+
commands.push("status", "logs", "start", "stop", "restart", "health", "recent")
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
skills.push({
|
|
75
|
+
name: entry.name,
|
|
76
|
+
description: fm.description || `${entry.name} service agent`,
|
|
77
|
+
servicePath: fm.service_path || "",
|
|
78
|
+
commands,
|
|
79
|
+
})
|
|
80
|
+
} catch {
|
|
81
|
+
// Skip unparseable skills
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
} catch {
|
|
85
|
+
// .claude/skills not readable
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return skills
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function runServiceCommand(serviceName: string, command: string, servicePath: string, projectRoot: string): string {
|
|
92
|
+
// Try jfl service-agent first
|
|
93
|
+
try {
|
|
94
|
+
const result = execSync(
|
|
95
|
+
`jfl service-agent run ${serviceName} ${command}`,
|
|
96
|
+
{ cwd: projectRoot, encoding: "utf-8", timeout: 15000, stdio: ["pipe", "pipe", "pipe"] }
|
|
97
|
+
)
|
|
98
|
+
return result.trim()
|
|
99
|
+
} catch {
|
|
100
|
+
// Fallback: direct execution based on command type
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (!servicePath || !existsSync(servicePath)) {
|
|
104
|
+
return `Service path not found: ${servicePath}`
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
try {
|
|
108
|
+
switch (command) {
|
|
109
|
+
case "status": {
|
|
110
|
+
const branch = execSync("git branch --show-current", { cwd: servicePath, encoding: "utf-8" }).trim()
|
|
111
|
+
const lastCommit = execSync("git log -1 --pretty='%h %s (%cr)'", { cwd: servicePath, encoding: "utf-8" }).trim()
|
|
112
|
+
const dirty = execSync("git status --porcelain", { cwd: servicePath, encoding: "utf-8" }).trim()
|
|
113
|
+
return [
|
|
114
|
+
`Service: ${serviceName}`,
|
|
115
|
+
`Path: ${servicePath}`,
|
|
116
|
+
`Branch: ${branch}`,
|
|
117
|
+
`Last commit: ${lastCommit}`,
|
|
118
|
+
`Working tree: ${dirty ? `${dirty.split("\n").length} changes` : "clean"}`,
|
|
119
|
+
].join("\n")
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
case "logs": {
|
|
123
|
+
return execSync("git log --oneline -10", { cwd: servicePath, encoding: "utf-8" }).trim()
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
case "recent": {
|
|
127
|
+
const since = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString()
|
|
128
|
+
try {
|
|
129
|
+
return execSync(`git log --oneline --since="${since}"`, { cwd: servicePath, encoding: "utf-8" }).trim() || "No changes in last 24h"
|
|
130
|
+
} catch {
|
|
131
|
+
return "No changes in last 24h"
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
case "health": {
|
|
136
|
+
// Check if package.json exists and has a test script
|
|
137
|
+
const pkgPath = join(servicePath, "package.json")
|
|
138
|
+
if (existsSync(pkgPath)) {
|
|
139
|
+
const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"))
|
|
140
|
+
const hasTests = !!pkg.scripts?.test
|
|
141
|
+
const hasBuild = !!pkg.scripts?.build
|
|
142
|
+
return [
|
|
143
|
+
`Service: ${serviceName}`,
|
|
144
|
+
`Has tests: ${hasTests ? "yes" : "no"}`,
|
|
145
|
+
`Has build: ${hasBuild ? "yes" : "no"}`,
|
|
146
|
+
`Dependencies: ${Object.keys(pkg.dependencies || {}).length}`,
|
|
147
|
+
`Dev dependencies: ${Object.keys(pkg.devDependencies || {}).length}`,
|
|
148
|
+
].join("\n")
|
|
149
|
+
}
|
|
150
|
+
return `No package.json found at ${servicePath}`
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
default:
|
|
154
|
+
return `Unknown command: ${command}. Available: status, logs, recent, health`
|
|
155
|
+
}
|
|
156
|
+
} catch (err) {
|
|
157
|
+
return `Error running ${command} on ${serviceName}: ${err}`
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export async function setupServiceSkills(ctx: PiContext, _config: JflConfig): Promise<void> {
|
|
162
|
+
const root = ctx.session.projectRoot
|
|
163
|
+
const services = discoverServiceSkills(root)
|
|
164
|
+
|
|
165
|
+
if (services.length === 0) return
|
|
166
|
+
|
|
167
|
+
ctx.log(`Discovered ${services.length} service skill(s): ${services.map(s => s.name).join(", ")}`, "debug")
|
|
168
|
+
|
|
169
|
+
// Register a unified service tool
|
|
170
|
+
ctx.registerTool({
|
|
171
|
+
name: "jfl_service",
|
|
172
|
+
description: `Query registered service agents. Available services: ${services.map(s => s.name).join(", ")}. Commands: status, logs, recent, health, start, stop, restart.`,
|
|
173
|
+
promptSnippet: `Service agents: ${services.map(s => s.name).join(", ")}`,
|
|
174
|
+
promptGuidelines: [
|
|
175
|
+
`Use this tool to interact with registered services: ${services.map(s => s.name).join(", ")}`,
|
|
176
|
+
"Common commands: status (git info), logs (recent commits), recent (24h changes), health (package info)",
|
|
177
|
+
],
|
|
178
|
+
inputSchema: {
|
|
179
|
+
type: "object",
|
|
180
|
+
properties: {
|
|
181
|
+
service: {
|
|
182
|
+
type: "string",
|
|
183
|
+
description: `Service name: ${services.map(s => s.name).join(", ")}`,
|
|
184
|
+
enum: services.map(s => s.name),
|
|
185
|
+
},
|
|
186
|
+
command: {
|
|
187
|
+
type: "string",
|
|
188
|
+
description: "Command to run on the service",
|
|
189
|
+
enum: ["status", "logs", "recent", "health", "start", "stop", "restart"],
|
|
190
|
+
},
|
|
191
|
+
},
|
|
192
|
+
required: ["service", "command"],
|
|
193
|
+
},
|
|
194
|
+
async handler(input) {
|
|
195
|
+
const { service, command } = input as { service: string; command: string }
|
|
196
|
+
const svc = services.find(s => s.name === service)
|
|
197
|
+
if (!svc) return `Unknown service: ${service}. Available: ${services.map(s => s.name).join(", ")}`
|
|
198
|
+
return runServiceCommand(service, command, svc.servicePath, root)
|
|
199
|
+
},
|
|
200
|
+
})
|
|
201
|
+
|
|
202
|
+
// Register per-service commands (e.g., /jfl-cli status, /jfl-platform logs)
|
|
203
|
+
for (const svc of services) {
|
|
204
|
+
ctx.registerCommand({
|
|
205
|
+
name: svc.name,
|
|
206
|
+
description: `${svc.description} โ commands: ${svc.commands.join(", ")}`,
|
|
207
|
+
async handler(args: string, _ctx: PiContext) {
|
|
208
|
+
const command = args.trim().split(/\s+/)[0] || "status"
|
|
209
|
+
const result = runServiceCommand(svc.name, command, svc.servicePath, root)
|
|
210
|
+
ctx.ui.notify(result, { level: "info" })
|
|
211
|
+
},
|
|
212
|
+
})
|
|
213
|
+
}
|
|
214
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
|
|
3
|
+
# Physical-World Telemetry Dashboard
|
|
4
|
+
# Quick overview of telemetry monitoring capabilities
|
|
5
|
+
|
|
6
|
+
set -euo pipefail
|
|
7
|
+
|
|
8
|
+
echo "๐ Physical-World Telemetry Dashboard"
|
|
9
|
+
echo "====================================="
|
|
10
|
+
echo
|
|
11
|
+
|
|
12
|
+
# Check if telemetry is enabled
|
|
13
|
+
echo "๐ Telemetry Status:"
|
|
14
|
+
jfl telemetry status
|
|
15
|
+
echo
|
|
16
|
+
|
|
17
|
+
# Generate some sample data if none exists
|
|
18
|
+
echo "๐งช Generating sample data..."
|
|
19
|
+
jfl telemetry test --count 3 --interval 100 --categories "command,performance"
|
|
20
|
+
echo
|
|
21
|
+
|
|
22
|
+
# Show current snapshot
|
|
23
|
+
echo "๐ Current Physical-World Metrics:"
|
|
24
|
+
jfl telemetry monitor --compact
|
|
25
|
+
echo
|
|
26
|
+
|
|
27
|
+
echo "๐ Available Monitoring Options:"
|
|
28
|
+
echo " jfl telemetry monitor --live # Real-time updates"
|
|
29
|
+
echo " jfl telemetry monitor --filter cpu # Filter for CPU-related events"
|
|
30
|
+
echo " jfl telemetry monitor --compact # Compact display"
|
|
31
|
+
echo
|
|
32
|
+
|
|
33
|
+
echo "๐ก Physical-World Data Captured:"
|
|
34
|
+
echo " โข System: CPU usage, memory, thermal state, system load"
|
|
35
|
+
echo " โข Hardware: battery level, disk space, sensors"
|
|
36
|
+
echo " โข Workflow: git branch, dirty files, terminal count, IDE"
|
|
37
|
+
echo " โข Network: latency, connectivity, local dev servers"
|
|
38
|
+
echo " โข Performance: build times, test durations, command timing"
|
|
39
|
+
echo
|
|
40
|
+
|
|
41
|
+
echo "๐ฏ Competitive Advantage:"
|
|
42
|
+
echo " This data is impossible for web UIs to access!"
|
|
43
|
+
echo " You own the edge - local machine context during live operations."
|
|
44
|
+
echo
|