jfl 0.2.0 → 0.2.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.
@@ -75,19 +75,40 @@ function setState(threadId: string, s: SessionState) {
75
75
  // OpenClaw helpers
76
76
  // ============================================================================
77
77
 
78
- async function openclaw(cmd: string): Promise<string> {
78
+ async function openclaw(cmd: string, cwd?: string): Promise<string> {
79
79
  const { stdout } = await execAsync(`jfl openclaw ${cmd}`, {
80
80
  timeout: 30000,
81
81
  env: { ...process.env },
82
+ cwd,
82
83
  })
83
84
  return stdout.trim()
84
85
  }
85
86
 
86
- async function openclawJSON(cmd: string): Promise<any> {
87
- const raw = await openclaw(`${cmd} --json`)
87
+ async function openclawJSON(cmd: string, cwd?: string): Promise<any> {
88
+ const raw = await openclaw(`${cmd} --json`, cwd)
88
89
  return JSON.parse(raw)
89
90
  }
90
91
 
92
+ async function ensureContextHub(gtmPath: string): Promise<boolean> {
93
+ try {
94
+ // Check if hub is healthy
95
+ const status = await openclawJSON("status", gtmPath)
96
+ if (status.context_hub?.healthy) return true
97
+ } catch { /* hub not running */ }
98
+
99
+ // Start hub from within GTM directory
100
+ try {
101
+ await execAsync("jfl context-hub start", {
102
+ cwd: gtmPath,
103
+ timeout: 15000,
104
+ env: { ...process.env },
105
+ })
106
+ return true
107
+ } catch {
108
+ return false
109
+ }
110
+ }
111
+
91
112
  async function ensureJFL(): Promise<boolean> {
92
113
  try {
93
114
  await execAsync("jfl --version", { timeout: 5000 })
@@ -200,10 +221,13 @@ async function activateGTM(gtmPath: string, ctx: Context) {
200
221
 
201
222
  try {
202
223
  // Register agent with GTM (idempotent)
203
- await openclawJSON(`register -g "${gtmPath}" -a ${agent}`)
224
+ await openclawJSON(`register -g "${gtmPath}" -a ${agent}`, gtmPath)
225
+
226
+ // Ensure context hub is running from the GTM directory
227
+ await ensureContextHub(gtmPath)
204
228
 
205
229
  // Start session
206
- const session = await openclawJSON(`session-start -a ${agent} -g "${gtmPath}"`)
230
+ const session = await openclawJSON(`session-start -a ${agent} -g "${gtmPath}"`, gtmPath)
207
231
 
208
232
  setState(ctx.threadId, {
209
233
  gtmPath,
@@ -522,8 +546,11 @@ export async function onSessionStart(event: { agentId: string; platform: string;
522
546
  const agent = existing.agentId || event.agentId || `clawd-${event.platform}`
523
547
 
524
548
  try {
549
+ // Ensure context hub is running from the GTM directory
550
+ await ensureContextHub(existing.gtmPath)
551
+
525
552
  // Try to resume or start a new session
526
- const session = await openclawJSON(`session-start -a ${agent} -g "${existing.gtmPath}"`)
553
+ const session = await openclawJSON(`session-start -a ${agent} -g "${existing.gtmPath}"`, existing.gtmPath)
527
554
  setState(event.threadId, { ...existing, sessionBranch: session.session_id })
528
555
 
529
556
  return {
@@ -564,13 +591,13 @@ export async function onSessionStart(event: { agentId: string; platform: string;
564
591
  */
565
592
  export async function onBeforeTurn(event: { agentId: string; threadId: string; message?: string }) {
566
593
  const s = getState(event.threadId)
567
- if (!s?.activated || !s?.sessionBranch) return { prependContext: "" }
594
+ if (!s?.activated || !s?.sessionBranch || !s?.gtmPath) return { prependContext: "" }
568
595
 
569
596
  let contextBlock = ""
570
597
  try {
571
598
  const query = event.message?.slice(0, 100) || ""
572
599
  if (query.length > 10) {
573
- const items = await openclawJSON(`context -q "${query.replace(/"/g, '\\"')}"`)
600
+ const items = await openclawJSON(`context -q "${query.replace(/"/g, '\\"')}"`, s.gtmPath)
574
601
  if (Array.isArray(items) && items.length > 0) {
575
602
  const relevant = items.slice(0, 3).map((i: any) =>
576
603
  `- [${i.type}] ${i.title}: ${(i.content || "").slice(0, 150)}`
@@ -594,11 +621,11 @@ export async function onAfterTurn(event: {
594
621
  detectedIntent?: string
595
622
  }) {
596
623
  const s = getState(event.threadId)
597
- if (!s?.activated || !s?.sessionBranch) return
624
+ if (!s?.activated || !s?.sessionBranch || !s?.gtmPath) return
598
625
 
599
626
  // Heartbeat (auto-commit)
600
627
  try {
601
- await openclaw("heartbeat --json")
628
+ await openclaw("heartbeat --json", s.gtmPath)
602
629
  } catch { /* non-fatal */ }
603
630
 
604
631
  // Auto-capture decisions/completions
@@ -613,7 +640,7 @@ export async function onAfterTurn(event: {
613
640
  if (type) {
614
641
  const title = event.response.slice(0, 80).replace(/\n/g, " ")
615
642
  try {
616
- await openclaw(`journal --type ${type} --title "${title.replace(/"/g, '\\"')}" --summary "${title.replace(/"/g, '\\"')}"`)
643
+ await openclaw(`journal --type ${type} --title "${title.replace(/"/g, '\\"')}" --summary "${title.replace(/"/g, '\\"')}"`, s.gtmPath)
617
644
  } catch { /* non-fatal */ }
618
645
  }
619
646
  }
@@ -625,10 +652,10 @@ export async function onAfterTurn(event: {
625
652
  */
626
653
  export async function onSessionEnd(event: { agentId: string; threadId: string }) {
627
654
  const s = getState(event.threadId)
628
- if (!s?.activated || !s?.sessionBranch) return
655
+ if (!s?.activated || !s?.sessionBranch || !s?.gtmPath) return
629
656
 
630
657
  try {
631
- await openclawJSON("session-end --sync")
658
+ await openclawJSON("session-end --sync", s.gtmPath)
632
659
  } catch { /* never block shutdown */ }
633
660
 
634
661
  // Keep activated state but clear session
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jfl",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Just Fucking Launch - CLI for AI-powered GTM and development",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",