create-merlin-brain 2.3.3 → 2.4.0

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.
@@ -0,0 +1,725 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # Merlin Loop - Sights Integration
4
+ # Fetches recent changes, trajectory, and records commits
5
+ # Part of the Sights Memory Layer for Merlin Pro
6
+ #
7
+
8
+ # ═══════════════════════════════════════════════════════════════════════════════
9
+ # Configuration
10
+ # ═══════════════════════════════════════════════════════════════════════════════
11
+
12
+ SIGHTS_API_URL="${MERLIN_API_URL:-https://auth.merlin.build}"
13
+ RECENT_CHANGES_LIMIT="${MERLIN_RECENT_LIMIT:-5}"
14
+
15
+ # Cache for current session
16
+ CACHED_RECENT_CHANGES=""
17
+ CACHED_TRAJECTORY=""
18
+
19
+ # ═══════════════════════════════════════════════════════════════════════════════
20
+ # API Key Management
21
+ # ═══════════════════════════════════════════════════════════════════════════════
22
+
23
+ get_api_key() {
24
+ # Check environment variable first
25
+ if [ -n "$MERLIN_API_KEY" ]; then
26
+ echo "$MERLIN_API_KEY"
27
+ return
28
+ fi
29
+
30
+ # Check ~/.merlin/config.json
31
+ local config_file="$HOME/.merlin/config.json"
32
+ if [ -f "$config_file" ] && command -v jq &> /dev/null; then
33
+ local key
34
+ key=$(jq -r '.apiKey // empty' "$config_file")
35
+ if [ -n "$key" ]; then
36
+ echo "$key"
37
+ return
38
+ fi
39
+ fi
40
+
41
+ # Not found
42
+ echo ""
43
+ }
44
+
45
+ # ═══════════════════════════════════════════════════════════════════════════════
46
+ # Repo ID Management
47
+ # ═══════════════════════════════════════════════════════════════════════════════
48
+
49
+ # Get current repo ID (stored in state or detected)
50
+ get_current_repo_id() {
51
+ # Check state file first
52
+ if [ -f "$STATE_FILE" ] && command -v jq &> /dev/null; then
53
+ local repo_id
54
+ repo_id=$(jq -r '.sights_repo_id // empty' "$STATE_FILE")
55
+ if [ -n "$repo_id" ]; then
56
+ echo "$repo_id"
57
+ return
58
+ fi
59
+ fi
60
+
61
+ # Check ~/.merlin/config.json for selected repo
62
+ local config_file="$HOME/.merlin/config.json"
63
+ if [ -f "$config_file" ] && command -v jq &> /dev/null; then
64
+ local repo_id
65
+ repo_id=$(jq -r '.selectedRepo.id // empty' "$config_file")
66
+ if [ -n "$repo_id" ]; then
67
+ echo "$repo_id"
68
+ return
69
+ fi
70
+ fi
71
+
72
+ # Not found
73
+ echo ""
74
+ }
75
+
76
+ # Set repo ID in state
77
+ set_current_repo_id() {
78
+ local repo_id="$1"
79
+
80
+ if [ -f "$STATE_FILE" ] && command -v jq &> /dev/null; then
81
+ local tmp
82
+ tmp=$(mktemp)
83
+ jq --arg id "$repo_id" '.sights_repo_id = $id' "$STATE_FILE" > "$tmp"
84
+ mv "$tmp" "$STATE_FILE"
85
+ fi
86
+ }
87
+
88
+ # ═══════════════════════════════════════════════════════════════════════════════
89
+ # Recent Changes
90
+ # ═══════════════════════════════════════════════════════════════════════════════
91
+
92
+ # Fetch recent changes from Sights
93
+ fetch_recent_changes() {
94
+ local api_key
95
+ api_key=$(get_api_key)
96
+
97
+ if [ -z "$api_key" ]; then
98
+ echo "# Recent Changes: Not available (no API key)"
99
+ return 1
100
+ fi
101
+
102
+ local repo_id
103
+ repo_id=$(get_current_repo_id)
104
+
105
+ if [ -z "$repo_id" ]; then
106
+ echo "# Recent Changes: Not available (no repo selected)"
107
+ return 1
108
+ fi
109
+
110
+ local response
111
+ response=$(curl -sf "${SIGHTS_API_URL}/api/agent/${repo_id}/activity/recent?limit=${RECENT_CHANGES_LIMIT}" \
112
+ -H "Authorization: Bearer $api_key" 2>/dev/null)
113
+
114
+ if [ -z "$response" ]; then
115
+ echo "# Recent Changes: Could not fetch"
116
+ return 1
117
+ fi
118
+
119
+ CACHED_RECENT_CHANGES="$response"
120
+
121
+ # Format for prompt injection
122
+ format_recent_changes "$response"
123
+ }
124
+
125
+ # Format recent changes for prompt injection
126
+ format_recent_changes() {
127
+ local json="$1"
128
+
129
+ if ! command -v jq &> /dev/null; then
130
+ echo "# Recent Changes: Install jq for formatted output"
131
+ return
132
+ fi
133
+
134
+ local trajectory
135
+ trajectory=$(echo "$json" | jq -r '.trajectory // "Not detected"')
136
+
137
+ local active_areas
138
+ active_areas=$(echo "$json" | jq -r '.activeAreas // [] | join(", ")')
139
+
140
+ echo "## Recent Changes (from Sights)"
141
+ echo ""
142
+ echo "**Trajectory:** $trajectory"
143
+ echo "**Active Areas:** ${active_areas:-None}"
144
+ echo ""
145
+ echo "### Recent Commits"
146
+
147
+ echo "$json" | jq -r '.commits[]? | "- **\(.hash[0:7])**: \(.message) (\(.filesChanged | length) files)"'
148
+
149
+ echo ""
150
+ echo "### Hot Files (frequently modified)"
151
+ echo "$json" | jq -r '.hotFiles[]? | "- **\(.path)**: \(.changeCount) changes"'
152
+ }
153
+
154
+ # ═══════════════════════════════════════════════════════════════════════════════
155
+ # Trajectory
156
+ # ═══════════════════════════════════════════════════════════════════════════════
157
+
158
+ # Get trajectory only
159
+ fetch_trajectory() {
160
+ local api_key
161
+ api_key=$(get_api_key)
162
+
163
+ if [ -z "$api_key" ]; then
164
+ return 1
165
+ fi
166
+
167
+ local repo_id
168
+ repo_id=$(get_current_repo_id)
169
+
170
+ if [ -z "$repo_id" ]; then
171
+ return 1
172
+ fi
173
+
174
+ local response
175
+ response=$(curl -sf "${SIGHTS_API_URL}/api/agent/${repo_id}/activity/trajectory" \
176
+ -H "Authorization: Bearer $api_key" 2>/dev/null)
177
+
178
+ if [ -z "$response" ]; then
179
+ return 1
180
+ fi
181
+
182
+ CACHED_TRAJECTORY=$(echo "$response" | jq -r '.trajectory // "Not detected"')
183
+ echo "$CACHED_TRAJECTORY"
184
+ }
185
+
186
+ # Get cached trajectory (doesn't fetch)
187
+ get_cached_trajectory() {
188
+ echo "$CACHED_TRAJECTORY"
189
+ }
190
+
191
+ # ═══════════════════════════════════════════════════════════════════════════════
192
+ # Record Commits
193
+ # ═══════════════════════════════════════════════════════════════════════════════
194
+
195
+ # Record a commit to Sights
196
+ record_commit_to_sights() {
197
+ local hash="$1"
198
+ local message="$2"
199
+ local files_changed="$3" # JSON array string
200
+ local author="${4:-claude}"
201
+ local author_type="${5:-loop-agent}"
202
+ local task_id="$6"
203
+
204
+ local api_key
205
+ api_key=$(get_api_key)
206
+
207
+ if [ -z "$api_key" ]; then
208
+ return 1
209
+ fi
210
+
211
+ local repo_id
212
+ repo_id=$(get_current_repo_id)
213
+
214
+ if [ -z "$repo_id" ]; then
215
+ return 1
216
+ fi
217
+
218
+ local body
219
+ body=$(jq -n \
220
+ --arg hash "$hash" \
221
+ --arg message "$message" \
222
+ --argjson filesChanged "$files_changed" \
223
+ --arg author "$author" \
224
+ --arg authorType "$author_type" \
225
+ --arg taskId "$task_id" \
226
+ '{hash: $hash, message: $message, filesChanged: $filesChanged, author: $author, authorType: $authorType, taskId: $taskId}')
227
+
228
+ curl -sf -X POST "${SIGHTS_API_URL}/api/agent/${repo_id}/activity/commit" \
229
+ -H "Authorization: Bearer $api_key" \
230
+ -H "Content-Type: application/json" \
231
+ -d "$body" > /dev/null 2>&1
232
+ }
233
+
234
+ # Record the last git commit to Sights
235
+ record_last_commit() {
236
+ local task_id="${1:-}"
237
+ local author="${2:-claude}"
238
+ local author_type="${3:-loop-agent}"
239
+
240
+ # Get last commit info
241
+ local last_commit
242
+ last_commit=$(git log -1 --pretty=format:"%H|%s" 2>/dev/null || echo "")
243
+
244
+ if [ -z "$last_commit" ]; then
245
+ return 1
246
+ fi
247
+
248
+ local hash message
249
+ hash=$(echo "$last_commit" | cut -d'|' -f1)
250
+ message=$(echo "$last_commit" | cut -d'|' -f2-)
251
+
252
+ # Get files changed
253
+ local files_json
254
+ files_json=$(git diff-tree --no-commit-id --name-only -r "$hash" 2>/dev/null | jq -R -s 'split("\n") | map(select(. != ""))' || echo "[]")
255
+
256
+ # Record to Sights
257
+ record_commit_to_sights "$hash" "$message" "$files_json" "$author" "$author_type" "$task_id"
258
+ }
259
+
260
+ # ═══════════════════════════════════════════════════════════════════════════════
261
+ # Sights Status Check
262
+ # ═══════════════════════════════════════════════════════════════════════════════
263
+
264
+ # Check if Sights is connected and ready
265
+ check_sights_connection() {
266
+ local api_key
267
+ api_key=$(get_api_key)
268
+
269
+ if [ -z "$api_key" ]; then
270
+ echo "not_configured"
271
+ return 1
272
+ fi
273
+
274
+ local repo_id
275
+ repo_id=$(get_current_repo_id)
276
+
277
+ if [ -z "$repo_id" ]; then
278
+ echo "no_repo"
279
+ return 1
280
+ fi
281
+
282
+ # Try to fetch trajectory as a health check
283
+ local response
284
+ response=$(curl -sf "${SIGHTS_API_URL}/api/agent/${repo_id}/activity/trajectory" \
285
+ -H "Authorization: Bearer $api_key" 2>/dev/null)
286
+
287
+ if [ -z "$response" ]; then
288
+ echo "unreachable"
289
+ return 1
290
+ fi
291
+
292
+ echo "connected"
293
+ return 0
294
+ }
295
+
296
+ # Print Sights status for loop startup
297
+ print_sights_status() {
298
+ local status
299
+ status=$(check_sights_connection)
300
+
301
+ case "$status" in
302
+ "connected")
303
+ echo -e "${GREEN}✓ Sights: Connected${RESET}"
304
+ ;;
305
+ "not_configured")
306
+ echo -e "${YELLOW}⚠ Sights: No API key configured${RESET}"
307
+ ;;
308
+ "no_repo")
309
+ echo -e "${YELLOW}⚠ Sights: No repository selected${RESET}"
310
+ ;;
311
+ "unreachable")
312
+ echo -e "${RED}✗ Sights: Could not connect${RESET}"
313
+ ;;
314
+ *)
315
+ echo -e "${YELLOW}⚠ Sights: Unknown status${RESET}"
316
+ ;;
317
+ esac
318
+ }
319
+
320
+ # ═══════════════════════════════════════════════════════════════════════════════
321
+ # Cloud Checkpoints (v3.0)
322
+ # ═══════════════════════════════════════════════════════════════════════════════
323
+
324
+ # Fetch latest checkpoint from cloud
325
+ fetch_checkpoint() {
326
+ local api_key
327
+ api_key=$(get_api_key)
328
+
329
+ if [ -z "$api_key" ]; then
330
+ return 1
331
+ fi
332
+
333
+ local repo_id
334
+ repo_id=$(get_current_repo_id)
335
+
336
+ if [ -z "$repo_id" ]; then
337
+ return 1
338
+ fi
339
+
340
+ local response
341
+ response=$(curl -sf "${SIGHTS_API_URL}/api/agent/${repo_id}/checkpoints/latest" \
342
+ -H "Authorization: Bearer $api_key" 2>/dev/null)
343
+
344
+ echo "$response"
345
+ }
346
+
347
+ # Format checkpoint for display
348
+ format_checkpoint() {
349
+ local json="$1"
350
+
351
+ if ! command -v jq &> /dev/null; then
352
+ echo "Install jq for checkpoint display"
353
+ return
354
+ fi
355
+
356
+ local checkpoint
357
+ checkpoint=$(echo "$json" | jq -r '.checkpoint // empty')
358
+
359
+ if [ -z "$checkpoint" ] || [ "$checkpoint" == "null" ]; then
360
+ echo "No checkpoint found"
361
+ return
362
+ fi
363
+
364
+ echo "## Checkpoint"
365
+ echo ""
366
+ echo "**Created By:** $(echo "$checkpoint" | jq -r '.createdBy // "Unknown"') ($(echo "$checkpoint" | jq -r '.createdByType // "unknown"'))"
367
+ echo "**Created At:** $(echo "$checkpoint" | jq -r '.createdAt // "Unknown"')"
368
+
369
+ local current_phase
370
+ current_phase=$(echo "$checkpoint" | jq -r '.currentPhase // empty')
371
+ if [ -n "$current_phase" ]; then
372
+ echo "**Phase:** $current_phase"
373
+ fi
374
+
375
+ local current_task
376
+ current_task=$(echo "$checkpoint" | jq -r '.currentTask // empty')
377
+ if [ -n "$current_task" ] && [ "$current_task" != "null" ]; then
378
+ echo "**Task:** $current_task"
379
+ fi
380
+
381
+ local summary
382
+ summary=$(echo "$checkpoint" | jq -r '.summary // empty')
383
+ if [ -n "$summary" ]; then
384
+ echo ""
385
+ echo "**Summary:** $summary"
386
+ fi
387
+ }
388
+
389
+ # Save checkpoint to cloud
390
+ save_checkpoint() {
391
+ local summary="$1"
392
+ local current_phase="${2:-}"
393
+ local current_task="${3:-}"
394
+ local working_files="${4:-[]}" # JSON array
395
+ local created_by="${5:-loop-agent}"
396
+
397
+ local api_key
398
+ api_key=$(get_api_key)
399
+
400
+ if [ -z "$api_key" ]; then
401
+ return 1
402
+ fi
403
+
404
+ local repo_id
405
+ repo_id=$(get_current_repo_id)
406
+
407
+ if [ -z "$repo_id" ]; then
408
+ return 1
409
+ fi
410
+
411
+ local body
412
+ body=$(jq -n \
413
+ --arg summary "$summary" \
414
+ --arg phase "$current_phase" \
415
+ --arg task "$current_task" \
416
+ --argjson files "$working_files" \
417
+ --arg by "$created_by" \
418
+ '{
419
+ createdBy: $by,
420
+ createdByType: "loop-agent",
421
+ currentPhase: (if $phase != "" then $phase else null end),
422
+ currentTask: (if $task != "" then ($task | tonumber) else null end),
423
+ summary: $summary,
424
+ workingFiles: $files
425
+ }')
426
+
427
+ curl -sf -X POST "${SIGHTS_API_URL}/api/agent/${repo_id}/checkpoints" \
428
+ -H "Authorization: Bearer $api_key" \
429
+ -H "Content-Type: application/json" \
430
+ -d "$body" > /dev/null 2>&1
431
+ }
432
+
433
+ # ═══════════════════════════════════════════════════════════════════════════════
434
+ # Decisions
435
+ # ═══════════════════════════════════════════════════════════════════════════════
436
+
437
+ # Record a decision
438
+ record_decision() {
439
+ local description="$1"
440
+ local reasoning="${2:-}"
441
+ local task_id="${3:-}"
442
+ local made_by="${4:-loop-agent}"
443
+
444
+ local api_key
445
+ api_key=$(get_api_key)
446
+
447
+ if [ -z "$api_key" ]; then
448
+ return 1
449
+ fi
450
+
451
+ local repo_id
452
+ repo_id=$(get_current_repo_id)
453
+
454
+ if [ -z "$repo_id" ]; then
455
+ return 1
456
+ fi
457
+
458
+ local body
459
+ body=$(jq -n \
460
+ --arg desc "$description" \
461
+ --arg reason "$reasoning" \
462
+ --arg task "$task_id" \
463
+ --arg by "$made_by" \
464
+ '{
465
+ description: $desc,
466
+ reasoning: (if $reason != "" then $reason else null end),
467
+ taskId: (if $task != "" then $task else null end),
468
+ madeBy: $by,
469
+ madeByType: "loop-agent"
470
+ }')
471
+
472
+ curl -sf -X POST "${SIGHTS_API_URL}/api/agent/${repo_id}/decisions" \
473
+ -H "Authorization: Bearer $api_key" \
474
+ -H "Content-Type: application/json" \
475
+ -d "$body" > /dev/null 2>&1
476
+ }
477
+
478
+ # Get recent decisions
479
+ get_decisions() {
480
+ local limit="${1:-5}"
481
+
482
+ local api_key
483
+ api_key=$(get_api_key)
484
+
485
+ if [ -z "$api_key" ]; then
486
+ return 1
487
+ fi
488
+
489
+ local repo_id
490
+ repo_id=$(get_current_repo_id)
491
+
492
+ if [ -z "$repo_id" ]; then
493
+ return 1
494
+ fi
495
+
496
+ curl -sf "${SIGHTS_API_URL}/api/agent/${repo_id}/decisions/recent?limit=${limit}" \
497
+ -H "Authorization: Bearer $api_key" 2>/dev/null
498
+ }
499
+
500
+ # ═══════════════════════════════════════════════════════════════════════════════
501
+ # Worker Coordination
502
+ # ═══════════════════════════════════════════════════════════════════════════════
503
+
504
+ # Worker session ID for this loop instance
505
+ WORKER_SESSION_ID="${LOOP_SESSION_ID:-loop-$$-$(date +%s)}"
506
+ HEARTBEAT_PID=""
507
+
508
+ # Register worker session
509
+ register_worker() {
510
+ local current_task="${1:-}"
511
+
512
+ local api_key
513
+ api_key=$(get_api_key)
514
+
515
+ if [ -z "$api_key" ]; then
516
+ return 1
517
+ fi
518
+
519
+ local repo_id
520
+ repo_id=$(get_current_repo_id)
521
+
522
+ if [ -z "$repo_id" ]; then
523
+ return 1
524
+ fi
525
+
526
+ local body
527
+ body=$(jq -n \
528
+ --arg agent "merlin-loop" \
529
+ --arg session "$WORKER_SESSION_ID" \
530
+ --arg task "$current_task" \
531
+ '{
532
+ agentId: $agent,
533
+ sessionId: $session,
534
+ currentTask: (if $task != "" then $task else null end)
535
+ }')
536
+
537
+ curl -sf -X POST "${SIGHTS_API_URL}/api/agent/${repo_id}/workers/heartbeat" \
538
+ -H "Authorization: Bearer $api_key" \
539
+ -H "Content-Type: application/json" \
540
+ -d "$body" > /dev/null 2>&1
541
+ }
542
+
543
+ # Send heartbeat
544
+ worker_heartbeat() {
545
+ local current_task="${1:-}"
546
+ register_worker "$current_task"
547
+ }
548
+
549
+ # Start background heartbeat process
550
+ start_heartbeat() {
551
+ local interval="${1:-30}"
552
+
553
+ # Kill existing heartbeat if running
554
+ stop_heartbeat
555
+
556
+ # Start background process
557
+ (
558
+ while true; do
559
+ worker_heartbeat
560
+ sleep "$interval"
561
+ done
562
+ ) &
563
+
564
+ HEARTBEAT_PID=$!
565
+ }
566
+
567
+ # Stop background heartbeat
568
+ stop_heartbeat() {
569
+ if [ -n "$HEARTBEAT_PID" ] && kill -0 "$HEARTBEAT_PID" 2>/dev/null; then
570
+ kill "$HEARTBEAT_PID" 2>/dev/null
571
+ HEARTBEAT_PID=""
572
+ fi
573
+ }
574
+
575
+ # Get active workers
576
+ get_active_workers() {
577
+ local api_key
578
+ api_key=$(get_api_key)
579
+
580
+ if [ -z "$api_key" ]; then
581
+ return 1
582
+ fi
583
+
584
+ local repo_id
585
+ repo_id=$(get_current_repo_id)
586
+
587
+ if [ -z "$repo_id" ]; then
588
+ return 1
589
+ fi
590
+
591
+ curl -sf "${SIGHTS_API_URL}/api/agent/${repo_id}/workers/active" \
592
+ -H "Authorization: Bearer $api_key" 2>/dev/null
593
+ }
594
+
595
+ # Claim a task
596
+ claim_task() {
597
+ local task_id="$1"
598
+
599
+ local api_key
600
+ api_key=$(get_api_key)
601
+
602
+ if [ -z "$api_key" ]; then
603
+ return 1
604
+ fi
605
+
606
+ local repo_id
607
+ repo_id=$(get_current_repo_id)
608
+
609
+ if [ -z "$repo_id" ]; then
610
+ return 1
611
+ fi
612
+
613
+ local body
614
+ body=$(jq -n \
615
+ --arg agent "merlin-loop" \
616
+ --arg session "$WORKER_SESSION_ID" \
617
+ '{
618
+ agentId: $agent,
619
+ sessionId: $session
620
+ }')
621
+
622
+ local response
623
+ response=$(curl -sf -X POST "${SIGHTS_API_URL}/api/agent/${repo_id}/tasks/${task_id}/claim" \
624
+ -H "Authorization: Bearer $api_key" \
625
+ -H "Content-Type: application/json" \
626
+ -d "$body" 2>/dev/null)
627
+
628
+ local success
629
+ success=$(echo "$response" | jq -r '.success // false')
630
+
631
+ if [ "$success" == "true" ]; then
632
+ return 0
633
+ else
634
+ return 1
635
+ fi
636
+ }
637
+
638
+ # Release a task
639
+ release_task() {
640
+ local task_id="$1"
641
+
642
+ local api_key
643
+ api_key=$(get_api_key)
644
+
645
+ if [ -z "$api_key" ]; then
646
+ return 1
647
+ fi
648
+
649
+ local repo_id
650
+ repo_id=$(get_current_repo_id)
651
+
652
+ if [ -z "$repo_id" ]; then
653
+ return 1
654
+ fi
655
+
656
+ curl -sf -X DELETE "${SIGHTS_API_URL}/api/agent/${repo_id}/tasks/${task_id}/claim" \
657
+ -H "Authorization: Bearer $api_key" > /dev/null 2>&1
658
+ }
659
+
660
+ # ═══════════════════════════════════════════════════════════════════════════════
661
+ # Sights Refresh
662
+ # ═══════════════════════════════════════════════════════════════════════════════
663
+
664
+ # Trigger Sights re-analysis when context is stale
665
+ # Usage: trigger_sights_refresh "reason"
666
+ trigger_sights_refresh() {
667
+ local reason="${1:-Manual refresh}"
668
+
669
+ local api_key
670
+ api_key=$(get_api_key)
671
+
672
+ if [ -z "$api_key" ]; then
673
+ echo "no-auth"
674
+ return 1
675
+ fi
676
+
677
+ local repo_id
678
+ repo_id=$(get_current_repo_id)
679
+
680
+ if [ -z "$repo_id" ]; then
681
+ echo "no-repo"
682
+ return 1
683
+ fi
684
+
685
+ local response
686
+ response=$(curl -sf -X POST "${SIGHTS_API_URL}/api/repos/${repo_id}/sync" \
687
+ -H "Authorization: Bearer $api_key" \
688
+ -H "Content-Type: application/json" 2>/dev/null || echo "")
689
+
690
+ if [ -n "$response" ]; then
691
+ local success
692
+ success=$(echo "$response" | jq -r '.success // false' 2>/dev/null || echo "false")
693
+
694
+ if [ "$success" == "true" ]; then
695
+ echo "queued"
696
+ return 0
697
+ fi
698
+ fi
699
+
700
+ echo "failed"
701
+ return 1
702
+ }
703
+
704
+ # ═══════════════════════════════════════════════════════════════════════════════
705
+ # Initialization
706
+ # ═══════════════════════════════════════════════════════════════════════════════
707
+
708
+ # Initialize Sights for this loop session
709
+ init_sights() {
710
+ # Check connection
711
+ local status
712
+ status=$(check_sights_connection)
713
+
714
+ if [ "$status" != "connected" ]; then
715
+ return 1
716
+ fi
717
+
718
+ # Pre-fetch trajectory
719
+ fetch_trajectory > /dev/null 2>&1
720
+
721
+ # Register this worker
722
+ register_worker > /dev/null 2>&1
723
+
724
+ return 0
725
+ }