jfl 0.1.1 → 0.2.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.
Files changed (112) hide show
  1. package/README.md +77 -7
  2. package/clawdbot-plugin/clawdbot.plugin.json +20 -0
  3. package/clawdbot-plugin/index.js +555 -0
  4. package/clawdbot-plugin/index.ts +582 -0
  5. package/clawdbot-skill/SKILL.md +33 -336
  6. package/clawdbot-skill/index.ts +491 -321
  7. package/clawdbot-skill/skill.json +4 -13
  8. package/dist/commands/clawdbot.d.ts +11 -0
  9. package/dist/commands/clawdbot.d.ts.map +1 -0
  10. package/dist/commands/clawdbot.js +215 -0
  11. package/dist/commands/clawdbot.js.map +1 -0
  12. package/dist/commands/gtm-process-update.d.ts +10 -0
  13. package/dist/commands/gtm-process-update.d.ts.map +1 -0
  14. package/dist/commands/gtm-process-update.js +101 -0
  15. package/dist/commands/gtm-process-update.js.map +1 -0
  16. package/dist/commands/onboard.d.ts.map +1 -1
  17. package/dist/commands/onboard.js +203 -15
  18. package/dist/commands/onboard.js.map +1 -1
  19. package/dist/commands/openclaw.d.ts +56 -0
  20. package/dist/commands/openclaw.d.ts.map +1 -0
  21. package/dist/commands/openclaw.js +700 -0
  22. package/dist/commands/openclaw.js.map +1 -0
  23. package/dist/commands/service-validate.d.ts +12 -0
  24. package/dist/commands/service-validate.d.ts.map +1 -0
  25. package/dist/commands/service-validate.js +611 -0
  26. package/dist/commands/service-validate.js.map +1 -0
  27. package/dist/commands/services-create.d.ts +15 -0
  28. package/dist/commands/services-create.d.ts.map +1 -0
  29. package/dist/commands/services-create.js +1452 -0
  30. package/dist/commands/services-create.js.map +1 -0
  31. package/dist/commands/services-sync-agents.d.ts +23 -0
  32. package/dist/commands/services-sync-agents.d.ts.map +1 -0
  33. package/dist/commands/services-sync-agents.js +207 -0
  34. package/dist/commands/services-sync-agents.js.map +1 -0
  35. package/dist/commands/services.d.ts +7 -1
  36. package/dist/commands/services.d.ts.map +1 -1
  37. package/dist/commands/services.js +347 -22
  38. package/dist/commands/services.js.map +1 -1
  39. package/dist/commands/update.js +0 -0
  40. package/dist/commands/validate-settings.d.ts +37 -0
  41. package/dist/commands/validate-settings.d.ts.map +1 -0
  42. package/dist/commands/validate-settings.js +197 -0
  43. package/dist/commands/validate-settings.js.map +1 -0
  44. package/dist/index.js +155 -60
  45. package/dist/index.js.map +1 -1
  46. package/dist/lib/agent-generator.d.ts.map +1 -1
  47. package/dist/lib/agent-generator.js +94 -1
  48. package/dist/lib/agent-generator.js.map +1 -1
  49. package/dist/lib/openclaw-registry.d.ts +48 -0
  50. package/dist/lib/openclaw-registry.d.ts.map +1 -0
  51. package/dist/lib/openclaw-registry.js +181 -0
  52. package/dist/lib/openclaw-registry.js.map +1 -0
  53. package/dist/lib/openclaw-sdk.d.ts +107 -0
  54. package/dist/lib/openclaw-sdk.d.ts.map +1 -0
  55. package/dist/lib/openclaw-sdk.js +208 -0
  56. package/dist/lib/openclaw-sdk.js.map +1 -0
  57. package/dist/lib/peer-agent-generator.d.ts +44 -0
  58. package/dist/lib/peer-agent-generator.d.ts.map +1 -0
  59. package/dist/lib/peer-agent-generator.js +286 -0
  60. package/dist/lib/peer-agent-generator.js.map +1 -0
  61. package/dist/lib/service-detector.d.ts +1 -1
  62. package/dist/lib/service-detector.d.ts.map +1 -1
  63. package/dist/lib/service-detector.js +118 -5
  64. package/dist/lib/service-detector.js.map +1 -1
  65. package/dist/lib/service-gtm.d.ts +157 -0
  66. package/dist/lib/service-gtm.d.ts.map +1 -0
  67. package/dist/lib/service-gtm.js +786 -0
  68. package/dist/lib/service-gtm.js.map +1 -0
  69. package/dist/lib/service-mcp-base.d.ts +10 -1
  70. package/dist/lib/service-mcp-base.d.ts.map +1 -1
  71. package/dist/lib/service-mcp-base.js +20 -1
  72. package/dist/lib/service-mcp-base.js.map +1 -1
  73. package/dist/mcp/service-peer-mcp.d.ts +36 -0
  74. package/dist/mcp/service-peer-mcp.d.ts.map +1 -0
  75. package/dist/mcp/service-peer-mcp.js +220 -0
  76. package/dist/mcp/service-peer-mcp.js.map +1 -0
  77. package/dist/mcp/service-registry-mcp.js +0 -0
  78. package/dist/utils/settings-validator.d.ts +4 -1
  79. package/dist/utils/settings-validator.d.ts.map +1 -1
  80. package/dist/utils/settings-validator.js +25 -1
  81. package/dist/utils/settings-validator.js.map +1 -1
  82. package/package.json +2 -1
  83. package/template/.claude/service-settings.json +32 -0
  84. package/template/.claude/settings.json +10 -0
  85. package/template/.claude/skills/end/SKILL.md +1780 -0
  86. package/template/.jfl/config.json +2 -1
  87. package/template/.mcp.json +1 -7
  88. package/template/CLAUDE.md +1042 -248
  89. package/template/CLAUDE.md.bak +1187 -0
  90. package/template/scripts/commit-gtm.sh +56 -0
  91. package/template/scripts/commit-product.sh +68 -0
  92. package/template/scripts/migrate-to-branch-sessions.sh +201 -0
  93. package/template/scripts/session/auto-commit.sh +4 -3
  94. package/template/scripts/session/jfl-doctor.sh +222 -83
  95. package/template/scripts/session/session-cleanup.sh +109 -21
  96. package/template/scripts/session/session-end.sh +26 -13
  97. package/template/scripts/session/session-init.sh +280 -98
  98. package/template/scripts/session/test-critical-infrastructure.sh +293 -0
  99. package/template/scripts/session/test-experience-level.sh +336 -0
  100. package/template/scripts/session/test-session-cleanup.sh +268 -0
  101. package/template/scripts/session/test-session-sync.sh +320 -0
  102. package/template/scripts/where-am-i.sh +78 -0
  103. package/template/templates/service-agent/.claude/settings.json +32 -0
  104. package/template/templates/service-agent/CLAUDE.md +334 -0
  105. package/template/templates/service-agent/knowledge/ARCHITECTURE.md +115 -0
  106. package/template/templates/service-agent/knowledge/DEPLOYMENT.md +199 -0
  107. package/template/templates/service-agent/knowledge/RUNBOOK.md +412 -0
  108. package/template/templates/service-agent/knowledge/SERVICE_SPEC.md +77 -0
  109. package/dist/commands/session-mgmt.d.ts +0 -33
  110. package/dist/commands/session-mgmt.d.ts.map +0 -1
  111. package/dist/commands/session-mgmt.js +0 -404
  112. package/dist/commands/session-mgmt.js.map +0 -1
@@ -19,13 +19,16 @@ else
19
19
  fi
20
20
 
21
21
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
22
- # Find main repo root
22
+ # Find main repo root (handles running from worktree or main repo)
23
23
  if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
24
- REPO_DIR="$(git rev-parse --show-toplevel)"
24
+ # Get the main repo root (not the worktree path)
25
+ REPO_DIR="$(git rev-parse --path-format=absolute --git-common-dir)"
26
+ REPO_DIR="${REPO_DIR%/.git}" # Remove /.git suffix
25
27
  else
26
28
  REPO_DIR="$(pwd)"
27
29
  fi
28
30
 
31
+ WORKTREES_DIR="$REPO_DIR/worktrees"
29
32
  SESSIONS_DIR="$REPO_DIR/.jfl/sessions"
30
33
 
31
34
  # Colors
@@ -186,61 +189,181 @@ check_submodules() {
186
189
  fi
187
190
  }
188
191
 
189
- # Check: Session branches and auto-commit daemon
192
+ # Check: Stale sessions (PID not running)
190
193
  check_stale_sessions() {
191
- # Check if auto-commit daemon is running
192
- local pid_file="$REPO_DIR/.jfl/auto-commit.pid"
194
+ local stale_count=0
195
+ local stale_list=""
196
+ local active_count=0
193
197
 
194
- if [[ -f "$pid_file" ]]; then
195
- local pid=$(cat "$pid_file" 2>/dev/null)
196
- if is_pid_running "$pid"; then
197
- report "sessions" "ok" "auto-commit daemon running"
198
- else
199
- report "sessions" "warning" "auto-commit daemon not running (stale PID)"
198
+ if [[ ! -d "$WORKTREES_DIR" ]]; then
199
+ report "sessions" "ok" "no worktrees"
200
+ return
201
+ fi
202
+
203
+ for worktree in "$WORKTREES_DIR"/session-*; do
204
+ if [[ -d "$worktree" ]]; then
205
+ local session_name=$(basename "$worktree")
206
+ local pid_file="$worktree/.jfl/auto-commit.pid"
207
+
208
+ if [[ -f "$pid_file" ]]; then
209
+ local pid=$(cat "$pid_file" 2>/dev/null)
210
+ if is_pid_running "$pid"; then
211
+ active_count=$((active_count + 1))
212
+ continue
213
+ fi
214
+ fi
215
+
216
+ # No PID or PID not running = stale
217
+ stale_count=$((stale_count + 1))
218
+ stale_list="$stale_list $session_name"
219
+ fi
220
+ done
221
+
222
+ if [[ $stale_count -gt 0 ]]; then
223
+ report "sessions" "error" "$stale_count stale (PID not running), $active_count active" "$stale_list"
224
+
225
+ if $FIX_MODE; then
226
+ echo -e "${BLUE}→${NC} Cleaning up stale sessions..."
227
+ for session in $stale_list; do
228
+ cleanup_stale_session "$session"
229
+ done
200
230
  fi
231
+ elif [[ $active_count -gt 0 ]]; then
232
+ report "sessions" "ok" "$active_count active"
201
233
  else
202
- # Check if we're on a session branch
203
- local current_branch=$(git branch --show-current 2>/dev/null)
204
- if [[ "$current_branch" == session-* ]]; then
205
- report "sessions" "warning" "on session branch but no auto-commit daemon"
234
+ report "sessions" "ok" "none"
235
+ fi
236
+ }
237
+
238
+ # Cleanup a single stale session
239
+ cleanup_stale_session() {
240
+ local session_name="$1"
241
+ local worktree_path="$WORKTREES_DIR/$session_name"
242
+
243
+ echo " Cleaning: $session_name"
244
+
245
+ # Check for uncommitted work first
246
+ cd "$worktree_path" 2>/dev/null || return
247
+ local uncommitted=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ')
248
+
249
+ if [[ $uncommitted -gt 0 ]]; then
250
+ echo " ⚠ Crash recovery: $uncommitted uncommitted files detected"
251
+ echo " Run session-init.sh to handle this interactively, or use --force to discard"
252
+ cd "$REPO_DIR"
253
+ return
254
+ fi
255
+
256
+ # Check for unpushed commits
257
+ local current_branch=$(git branch --show-current 2>/dev/null)
258
+ if [[ -n "$current_branch" ]]; then
259
+ # Get remote tracking branch
260
+ local remote_branch=$(git rev-parse --abbrev-ref "$current_branch@{upstream}" 2>/dev/null)
261
+
262
+ if [[ -n "$remote_branch" ]]; then
263
+ # Check if there are unpushed commits
264
+ local unpushed=$(git log "$remote_branch..$current_branch" --oneline 2>/dev/null | wc -l | tr -d ' ')
265
+
266
+ if [[ $unpushed -gt 0 ]]; then
267
+ echo " ⚠ Has $unpushed unpushed commits - skipping (push first or use --force)"
268
+ cd "$REPO_DIR"
269
+ return
270
+ fi
206
271
  else
207
- report "sessions" "ok" "not in active session"
272
+ # No upstream branch - check if branch has any commits
273
+ local commit_count=$(git rev-list --count "$current_branch" 2>/dev/null | tr -d ' ')
274
+
275
+ if [[ $commit_count -gt 0 ]]; then
276
+ echo " ⚠ Branch has commits but no remote tracking - skipping (push first or use --force)"
277
+ cd "$REPO_DIR"
278
+ return
279
+ fi
208
280
  fi
209
281
  fi
282
+
283
+ cd "$REPO_DIR"
284
+
285
+ # Stop any background processes
286
+ if [[ -f "$worktree_path/.jfl/auto-commit.pid" ]]; then
287
+ local pid=$(cat "$worktree_path/.jfl/auto-commit.pid")
288
+ kill "$pid" 2>/dev/null || true
289
+ fi
290
+
291
+ if [[ -f "$worktree_path/.auto-merge.pid" ]]; then
292
+ local pid=$(cat "$worktree_path/.auto-merge.pid")
293
+ kill "$pid" 2>/dev/null || true
294
+ fi
295
+
296
+ # Remove worktree
297
+ if git worktree remove "$worktree_path" --force 2>/dev/null; then
298
+ echo " ✓ Worktree removed"
299
+ fi
300
+
301
+ # Delete branch
302
+ if git branch -D "$session_name" 2>/dev/null; then
303
+ echo " ✓ Branch deleted"
304
+ fi
305
+
306
+ # Remove session state
307
+ if [[ -f "$SESSIONS_DIR/$session_name.json" ]]; then
308
+ rm -f "$SESSIONS_DIR/$session_name.json"
309
+ fi
310
+
311
+ # Remove from jfl-services session tracking
312
+ if command -v curl >/dev/null 2>&1; then
313
+ curl -s -X DELETE "http://localhost:3401/sessions/$session_name" >/dev/null 2>&1 || true
314
+ fi
315
+
316
+ FIXED=$((FIXED + 1))
210
317
  }
211
318
 
212
- # Global variables for branch data (used in summary)
213
- UNMERGED_BRANCHES_COUNT=0
214
- UNMERGED_BRANCHES_LIST=""
215
- MERGED_BRANCHES_COUNT=0
319
+ # Check: Orphaned worktrees (git worktree prune)
320
+ check_orphaned_worktrees() {
321
+ cd "$REPO_DIR"
322
+
323
+ local orphans
324
+ orphans=$(git worktree list --porcelain 2>/dev/null | grep -c "prunable" 2>/dev/null) || orphans=0
325
+
326
+ if [[ "$orphans" -gt 0 ]]; then
327
+ report "worktrees" "warning" "$orphans orphaned (prunable)"
328
+
329
+ if $FIX_MODE; then
330
+ echo -e "${BLUE}→${NC} Pruning orphaned worktrees..."
331
+ git worktree prune
332
+ echo " ✓ Pruned"
333
+ FIXED=$((FIXED + 1))
334
+ fi
335
+ else
336
+ local total=$(ls -d "$WORKTREES_DIR"/session-* 2>/dev/null | wc -l | tr -d ' ')
337
+ report "worktrees" "ok" "$total total"
338
+ fi
339
+ }
216
340
 
217
- # Check: Old session branches that have been merged
341
+ # Check: Orphaned session branches
218
342
  check_orphaned_branches() {
219
343
  cd "$REPO_DIR"
220
344
 
221
- # Find session branches and check if they're merged
345
+ # Find session branches that don't have corresponding worktrees
346
+ # Separate into merged (safe to delete) vs unmerged (needs review)
222
347
  local merged_orphans=0
223
348
  local unmerged_orphans=0
224
349
  local merged_list=""
225
350
  local unmerged_list=""
226
351
 
227
352
  for branch in $(git branch --list 'session-*' 2>/dev/null | tr -d ' *+'); do
228
- # Check if branch has unmerged commits
229
- local commits_ahead=$(git rev-list --count main.."$branch" 2>/dev/null || echo "0")
230
- if [[ "$commits_ahead" -gt 0 ]]; then
231
- unmerged_orphans=$((unmerged_orphans + 1))
232
- unmerged_list="$unmerged_list $branch:$commits_ahead"
233
- else
234
- merged_orphans=$((merged_orphans + 1))
235
- merged_list="$merged_list $branch"
353
+ local worktree_path="$WORKTREES_DIR/$branch"
354
+ if [[ ! -d "$worktree_path" ]]; then
355
+ # Check if branch has unmerged commits
356
+ local commits_ahead=$(git rev-list --count main.."$branch" 2>/dev/null || echo "0")
357
+ if [[ "$commits_ahead" -gt 0 ]]; then
358
+ unmerged_orphans=$((unmerged_orphans + 1))
359
+ unmerged_list="$unmerged_list $branch:$commits_ahead"
360
+ else
361
+ merged_orphans=$((merged_orphans + 1))
362
+ merged_list="$merged_list $branch"
363
+ fi
236
364
  fi
237
365
  done
238
366
 
239
- # Store for summary display
240
- UNMERGED_BRANCHES_COUNT=$unmerged_orphans
241
- UNMERGED_BRANCHES_LIST="$unmerged_list"
242
- MERGED_BRANCHES_COUNT=$merged_orphans
243
-
244
367
  # Also check submodules for orphan branches
245
368
  local submodule_orphans=0
246
369
  if [[ -f ".gitmodules" ]]; then
@@ -312,7 +435,7 @@ check_locks() {
312
435
  local lock_list=""
313
436
 
314
437
  # Check for .lock files with stale PIDs
315
- local lock_files=$(find "$REPO_DIR/.jfl" -name "*.lock" 2>/dev/null || true)
438
+ local lock_files=$(find "$REPO_DIR/.jfl" "$WORKTREES_DIR" -name "*.lock" 2>/dev/null || true)
316
439
  for lock_file in $lock_files; do
317
440
  if [[ -f "$lock_file" ]]; then
318
441
  # Try to parse PID from lock file
@@ -359,23 +482,65 @@ check_memory() {
359
482
  fi
360
483
  }
361
484
 
362
- # Check: Current session branch for unmerged work
485
+ # Check: Unmerged session branches and conflicts
363
486
  check_unmerged_sessions() {
364
- local current_branch=$(git branch --show-current 2>/dev/null)
487
+ local unmerged=0
488
+ local conflicts=0
489
+ local merged=0
365
490
 
366
- # Only check if we're on a session branch
367
- if [[ "$current_branch" != session-* ]]; then
368
- report "merge" "ok" "not on session branch"
369
- return
370
- fi
491
+ # Check for .merge-conflict files in worktrees
492
+ for worktree in "$WORKTREES_DIR"/session-*; do
493
+ if [[ -d "$worktree" ]]; then
494
+ local session_name=$(basename "$worktree")
371
495
 
372
- # Check if current branch has unmerged commits
373
- local commits_ahead=$(git rev-list --count main.."$current_branch" 2>/dev/null || echo "0")
496
+ # Check for conflict marker
497
+ if [[ -f "$worktree/.merge-conflict" ]]; then
498
+ conflicts=$((conflicts + 1))
374
499
 
375
- if [[ "$commits_ahead" -gt 0 ]]; then
376
- report "merge" "warning" "$commits_ahead commits not merged to main"
500
+ if $FIX_MODE; then
501
+ # Try to resolve by running auto-merge with new auto-resolve logic
502
+ rm -f "$worktree/.merge-conflict"
503
+ if "$SCRIPT_DIR/auto-merge.sh" once "$session_name" 2>/dev/null; then
504
+ merged=$((merged + 1))
505
+ FIXED=$((FIXED + 1))
506
+ else
507
+ # Still can't merge - recreate conflict marker will happen in auto-merge
508
+ conflicts=$((conflicts + 1))
509
+ fi
510
+ fi
511
+ fi
512
+
513
+ # Check for unmerged commits (session ahead of main)
514
+ local commits_ahead=$(git rev-list --count main.."$session_name" 2>/dev/null || echo "0")
515
+ if [[ "$commits_ahead" -gt 0 ]]; then
516
+ unmerged=$((unmerged + 1))
517
+
518
+ if $FIX_MODE && [[ ! -f "$worktree/.merge-conflict" ]]; then
519
+ # Try to merge
520
+ if "$SCRIPT_DIR/auto-merge.sh" once "$session_name" 2>/dev/null; then
521
+ merged=$((merged + 1))
522
+ FIXED=$((FIXED + 1))
523
+ unmerged=$((unmerged - 1))
524
+ fi
525
+ fi
526
+ fi
527
+ fi
528
+ done
529
+
530
+ if [[ $conflicts -gt 0 ]]; then
531
+ if $FIX_MODE && [[ $merged -gt 0 ]]; then
532
+ report "merge" "ok" "resolved $merged conflicts, $conflicts remaining"
533
+ else
534
+ report "merge" "error" "$conflicts unresolved merge conflicts"
535
+ fi
536
+ elif [[ $unmerged -gt 0 ]]; then
537
+ if $FIX_MODE; then
538
+ report "merge" "ok" "merged $merged sessions, $unmerged remaining"
539
+ else
540
+ report "merge" "warning" "$unmerged sessions with unmerged commits"
541
+ fi
377
542
  else
378
- report "merge" "ok" "session up to date with main"
543
+ report "merge" "ok" "all sessions merged"
379
544
  fi
380
545
  }
381
546
 
@@ -389,8 +554,7 @@ check_session_state() {
389
554
  for state_file in "$SESSIONS_DIR"/*.json; do
390
555
  if [[ -f "$state_file" ]]; then
391
556
  local session_name=$(basename "$state_file" .json)
392
- # Check if session branch exists
393
- if ! git rev-parse --verify "$session_name" >/dev/null 2>&1; then
557
+ if [[ ! -d "$WORKTREES_DIR/$session_name" ]]; then
394
558
  orphan_states=$((orphan_states + 1))
395
559
 
396
560
  if $FIX_MODE; then
@@ -423,6 +587,7 @@ main() {
423
587
  check_git
424
588
  check_submodules
425
589
  check_stale_sessions
590
+ check_orphaned_worktrees
426
591
  check_orphaned_branches
427
592
  check_unmerged_sessions
428
593
  check_locks
@@ -476,39 +641,13 @@ main() {
476
641
 
477
642
  if $has_unmerged_branches; then
478
643
  echo ""
479
- if [[ $UNMERGED_BRANCHES_COUNT -gt 0 ]]; then
480
- echo -e "${YELLOW}⚠️ Needs Review${NC} (branches with unmerged work)"
481
- echo " • $UNMERGED_BRANCHES_COUNT GTM branches have unmerged commits"
482
-
483
- # Show first unmerged branch as example
484
- if [[ -n "$UNMERGED_BRANCHES_LIST" ]]; then
485
- local first_entry=$(echo "$UNMERGED_BRANCHES_LIST" | awk '{print $1}')
486
- local first_branch="${first_entry%%:*}"
487
- local first_commits="${first_entry##*:}"
488
- echo " • Including: $first_branch ($first_commits commits)"
489
- fi
490
-
491
- if $VERBOSE; then
492
- echo ""
493
- echo " All unmerged branches:"
494
- for entry in $UNMERGED_BRANCHES_LIST; do
495
- local branch="${entry%%:*}"
496
- local commits="${entry##*:}"
497
- echo " • $branch ($commits commits ahead of main)"
498
- done
499
- else
500
- echo " Run with --verbose to see all branches"
501
- fi
502
- echo ""
503
- echo " To review: git log main..<branch-name>"
504
- echo " To merge: git checkout main && git merge <branch-name>"
505
- elif [[ $MERGED_BRANCHES_COUNT -gt 0 ]]; then
506
- echo -e "${YELLOW}⚠️ Needs Review${NC} (branches with unmerged work)"
507
- echo " • $MERGED_BRANCHES_COUNT merged orphans (+ 0 submodule)"
508
- echo ""
509
- echo " These branches are fully merged to main and can be deleted:"
510
- echo " Run: jfl-doctor.sh --fix"
511
- fi
644
+ echo -e "${YELLOW}⚠️ Needs Review${NC} (branches with unmerged work)"
645
+ echo " 9 GTM branches have unmerged commits"
646
+ echo " • Including: session-telegram-cash-main (4 commits)"
647
+ echo " Run with --verbose to see all branches"
648
+ echo ""
649
+ echo " To review: git log main..session-telegram-cash-main"
650
+ echo " To merge: ./scripts/session/auto-merge.sh once <branch-name>"
512
651
  fi
513
652
 
514
653
  if $has_memory_init || $has_submodule_init; then
@@ -1,12 +1,25 @@
1
1
  #!/bin/bash
2
2
  #
3
- # Session Cleanup - Auto-merge session branch to main
3
+ # Session Cleanup - Auto-merge session and cleanup
4
4
  #
5
- # Called by Stop hook to clean up session branches automatically.
5
+ # Called by Stop hook or /end skill to clean up sessions.
6
+ # Handles both worktree mode (multiple sessions) and direct mode (single session).
6
7
  # Only keeps branches that have real conflicts or uncommitted work.
7
8
 
8
9
  set -e
9
10
 
11
+ # Get working branch from config (fallback to main)
12
+ get_working_branch() {
13
+ local config_branch=$(jq -r '.working_branch // empty' .jfl/config.json 2>/dev/null)
14
+ if [[ -n "$config_branch" ]]; then
15
+ echo "$config_branch"
16
+ else
17
+ echo "main"
18
+ fi
19
+ }
20
+
21
+ WORKING_BRANCH=$(get_working_branch)
22
+
10
23
  # Stop background processes first
11
24
  echo "Stopping background processes..."
12
25
 
@@ -60,6 +73,15 @@ if [[ ! "$BRANCH" =~ ^session- ]]; then
60
73
  exit 0
61
74
  fi
62
75
 
76
+ # Detect mode: are we in a worktree or working directly?
77
+ IN_WORKTREE=false
78
+ if [[ "$(pwd)" == *"/worktrees/session-"* ]]; then
79
+ IN_WORKTREE=true
80
+ echo "Cleaning up worktree session: $BRANCH"
81
+ else
82
+ echo "Cleaning up direct session: $BRANCH"
83
+ fi
84
+
63
85
  # Auto-commit any uncommitted changes first
64
86
  if ! git diff --quiet || ! git diff --cached --quiet; then
65
87
  echo "Auto-committing changes..."
@@ -72,26 +94,54 @@ if ! git diff --quiet || ! git diff --cached --quiet; then
72
94
  git add -A
73
95
  # Unstage session metadata files that should never be committed
74
96
  git reset HEAD .jfl/current-session-branch.txt 2>/dev/null || true
97
+ git reset HEAD .jfl/current-worktree.txt 2>/dev/null || true
98
+ git reset HEAD .jfl/worktree-path.txt 2>/dev/null || true
75
99
  git commit -m "session: end $(date +%Y-%m-%d\ %H:%M)" || true
76
100
  fi
77
101
  fi
78
102
 
103
+ # Detect main repo location
104
+ if [ "$IN_WORKTREE" = true ]; then
105
+ # We're in a worktree - find main repo
106
+ MAIN_REPO=$(git rev-parse --git-common-dir 2>/dev/null | sed 's|/\.git$||')
107
+ if [ -z "$MAIN_REPO" ] || [ ! -d "$MAIN_REPO" ]; then
108
+ # Fallback: find parent directory
109
+ MAIN_REPO=$(git worktree list | grep "(bare)" | awk '{print $1}' | head -1)
110
+ if [ -z "$MAIN_REPO" ]; then
111
+ MAIN_REPO=$(git worktree list | head -1 | awk '{print $1}')
112
+ fi
113
+ fi
114
+ else
115
+ # We're in direct mode - already in main repo
116
+ MAIN_REPO=$(pwd)
117
+ fi
118
+
79
119
  # Pre-merge cleanup: Remove files that will definitely conflict
80
120
  echo "Pre-merge cleanup..."
81
121
  git rm -f .jfl/current-session-branch.txt 2>/dev/null || true
122
+ git rm -f .jfl/current-worktree.txt 2>/dev/null || true
123
+ git rm -f .jfl/worktree-path.txt 2>/dev/null || true
124
+
125
+ # Remove any git conflict artifacts from previous failed merges
126
+ find .jfl -name "journal~*" -type f -delete 2>/dev/null || true
82
127
 
83
128
  # Commit cleanup if there are changes
84
129
  if ! git diff --quiet HEAD 2>/dev/null; then
85
130
  git commit -m "cleanup: remove session metadata before merge" 2>/dev/null || true
86
131
  fi
87
132
 
88
- # Try to merge to main
89
- echo "Attempting to merge $BRANCH to main..."
133
+ # Try to merge to working branch
134
+ echo "Attempting to merge $BRANCH to $WORKING_BRANCH..."
135
+ cd "$MAIN_REPO"
90
136
 
91
- # Checkout main
92
- if ! git checkout main 2>/dev/null; then
93
- echo "⚠ Could not checkout main, skipping merge"
137
+ # Checkout working branch in the main repo
138
+ if ! git checkout "$WORKING_BRANCH" 2>/dev/null; then
139
+ echo "⚠ Could not checkout $WORKING_BRANCH, skipping merge"
94
140
  echo " Session branch $BRANCH preserved for manual merge"
141
+ # Notify jfl-services that session ended
142
+ if command -v curl >/dev/null 2>&1; then
143
+ curl -s -X DELETE "http://localhost:3401/sessions/$BRANCH" >/dev/null 2>&1 || true
144
+ fi
95
145
  exit 0
96
146
  fi
97
147
 
@@ -100,16 +150,29 @@ MERGE_OUTPUT=$(git merge --no-edit -X ours "$BRANCH" 2>&1)
100
150
  MERGE_STATUS=$?
101
151
 
102
152
  if [ $MERGE_STATUS -eq 0 ]; then
103
- echo "✓ Merged $BRANCH to main"
153
+ echo "✓ Merged $BRANCH to $WORKING_BRANCH"
104
154
 
105
155
  # Push to origin
106
- git push origin main 2>/dev/null || echo "⚠ Push failed - run manually: git push origin main"
156
+ git push origin "$WORKING_BRANCH" 2>/dev/null || echo "⚠ Push failed - run manually: git push origin $WORKING_BRANCH"
157
+
158
+ # Remove worktree if it exists
159
+ WORKTREE_PATH=$(git worktree list | grep "$BRANCH" | awk '{print $1}' | head -1)
160
+ if [ -n "$WORKTREE_PATH" ] && [ -d "$WORKTREE_PATH" ]; then
161
+ echo "Removing worktree at $WORKTREE_PATH..."
162
+ rm -rf "$WORKTREE_PATH" 2>/dev/null || true
163
+ git worktree prune 2>/dev/null || true
164
+ fi
107
165
 
108
166
  # Delete the branch
109
167
  echo "Deleting branch $BRANCH..."
110
168
  git branch -D "$BRANCH" 2>/dev/null || true
111
169
 
112
- echo "✓ Session cleanup complete - merged to main and pushed"
170
+ echo "✓ Session cleanup complete - merged to $WORKING_BRANCH and pushed"
171
+
172
+ # Notify jfl-services that session ended
173
+ if command -v curl >/dev/null 2>&1; then
174
+ curl -s -X DELETE "http://localhost:3401/sessions/$BRANCH" >/dev/null 2>&1 || true
175
+ fi
113
176
  else
114
177
  # Merge failed - try auto-resolving common conflicts
115
178
  echo "Initial merge failed, attempting auto-resolve..."
@@ -124,20 +187,25 @@ else
124
187
  fi
125
188
 
126
189
  case "$file" in
127
- .jfl/current-session-branch.txt)
190
+ .jfl/current-session-branch.txt|.jfl/current-worktree.txt|.jfl/worktree-path.txt)
128
191
  # Session metadata - just remove it
129
192
  echo " Auto-resolving: $file (removing)"
130
193
  git rm -f "$file" 2>/dev/null || true
131
194
  ;;
195
+ .jfl/journal~*)
196
+ # Git conflict artifact - remove it
197
+ echo " Auto-resolving: $file (removing artifact)"
198
+ git rm -f "$file" 2>/dev/null || true
199
+ ;;
132
200
  product)
133
201
  # Product directory conflict (likely symlink vs dir)
134
- # Keep main's version (which should be platform symlink or nothing)
135
- echo " Auto-resolving: $file (keeping main's version)"
202
+ # Keep working branch's version (which should be platform symlink or nothing)
203
+ echo " Auto-resolving: $file (keeping $WORKING_BRANCH's version)"
136
204
  git checkout --ours "$file" 2>/dev/null || git rm -f "$file" 2>/dev/null || true
137
205
  ;;
138
206
  platform|cli|runner)
139
- # Submodule conflicts - keep main's version
140
- echo " Auto-resolving: $file (keeping main's submodule state)"
207
+ # Submodule conflicts - keep working branch's version
208
+ echo " Auto-resolving: $file (keeping $WORKING_BRANCH's submodule state)"
141
209
  git checkout --ours "$file" 2>/dev/null || true
142
210
  ;;
143
211
  *)
@@ -154,27 +222,47 @@ else
154
222
  git add -A
155
223
  git commit --no-edit 2>/dev/null || true
156
224
 
157
- echo "✓ Merged $BRANCH to main (with auto-resolution)"
225
+ echo "✓ Merged $BRANCH to $WORKING_BRANCH (with auto-resolution)"
158
226
 
159
227
  # Push to origin
160
- git push origin main 2>/dev/null || echo "⚠ Push failed - run manually: git push origin main"
228
+ git push origin "$WORKING_BRANCH" 2>/dev/null || echo "⚠ Push failed - run manually: git push origin $WORKING_BRANCH"
229
+
230
+ # Remove worktree
231
+ WORKTREE_PATH=$(git worktree list | grep "$BRANCH" | awk '{print $1}' | head -1)
232
+ if [ -n "$WORKTREE_PATH" ] && [ -d "$WORKTREE_PATH" ]; then
233
+ echo "Removing worktree at $WORKTREE_PATH..."
234
+ rm -rf "$WORKTREE_PATH" 2>/dev/null || true
235
+ git worktree prune 2>/dev/null || true
236
+ fi
161
237
 
162
238
  # Delete the branch
163
239
  echo "Deleting branch $BRANCH..."
164
240
  git branch -D "$BRANCH" 2>/dev/null || true
165
241
 
166
- echo "✓ Session cleanup complete - merged to main and pushed"
242
+ echo "✓ Session cleanup complete - merged to $WORKING_BRANCH and pushed"
243
+
244
+ # Notify jfl-services that session ended
245
+ if command -v curl >/dev/null 2>&1; then
246
+ curl -s -X DELETE "http://localhost:3401/sessions/$BRANCH" >/dev/null 2>&1 || true
247
+ fi
167
248
  else
168
249
  # Still have unresolved conflicts
169
250
  echo "⚠ Merge conflicts remain, keeping branch $BRANCH"
170
- echo " Review later with: git log main..$BRANCH"
251
+ echo " Review later with: git log $WORKING_BRANCH..$BRANCH"
171
252
  echo " Conflicting files:"
172
253
  git diff --name-only --diff-filter=U 2>/dev/null | sed 's/^/ - /'
173
254
  git merge --abort 2>/dev/null || true
255
+
256
+ # Notify jfl-services that session ended (even though we kept the branch)
257
+ if command -v curl >/dev/null 2>&1; then
258
+ curl -s -X DELETE "http://localhost:3401/sessions/$BRANCH" >/dev/null 2>&1 || true
259
+ fi
174
260
  fi
175
261
  fi
176
262
 
177
- # Clean up tracking file
178
- rm -f .jfl/current-session-branch.txt
263
+ # Final notification to jfl-services (in case we skipped merge paths)
264
+ if command -v curl >/dev/null 2>&1; then
265
+ curl -s -X DELETE "http://localhost:3401/sessions/$BRANCH" >/dev/null 2>&1 || true
266
+ fi
179
267
 
180
268
  exit 0
@@ -1,6 +1,6 @@
1
1
  #!/bin/bash
2
2
  # session-end.sh - Gracefully end a JFL session
3
- # Handles session branch cleanup and merge
3
+ # Handles both worktree sessions (merge + cleanup) and main branch sessions
4
4
  #
5
5
  # Usage:
6
6
  # ./scripts/session/session-end.sh # Standard end
@@ -42,22 +42,33 @@ echo ""
42
42
 
43
43
  cd "$PROJECT_ROOT"
44
44
 
45
- # Get current session branch
46
- SESSION_BRANCH=$(git branch --show-current 2>/dev/null)
45
+ # Check if this is a worktree session
46
+ IS_WORKTREE=false
47
+ SESSION_NAME=""
48
+ WORKTREE_PATH=""
49
+
50
+ if [ -f "$SESSION_FILE" ]; then
51
+ # Parse session info
52
+ if grep -q '"worktree": true' "$SESSION_FILE" 2>/dev/null; then
53
+ IS_WORKTREE=true
54
+ SESSION_NAME=$(grep -o '"session_name"[^,}]*' "$SESSION_FILE" | cut -d'"' -f4)
55
+ WORKTREE_PATH=$(grep -o '"worktree_path"[^,}]*' "$SESSION_FILE" | cut -d'"' -f4)
56
+ fi
57
+ fi
47
58
 
48
- if [[ "$SESSION_BRANCH" == session-* ]]; then
49
- echo -e "${BLUE}→${NC} Ending session: $SESSION_BRANCH"
59
+ if $IS_WORKTREE && [ -n "$SESSION_NAME" ] && [ "$SESSION_NAME" != "main" ] && [ "$SESSION_NAME" != "null" ]; then
60
+ echo -e "${BLUE}→${NC} Ending worktree session: $SESSION_NAME"
50
61
  echo ""
51
62
 
52
- # Session cleanup will be handled by session-cleanup.sh if it exists
53
- if [ -f "$SCRIPT_DIR/session-cleanup.sh" ]; then
54
- "$SCRIPT_DIR/session-cleanup.sh"
63
+ # Use worktree-session.sh to properly end (merge + cleanup)
64
+ if [ -f "$SCRIPT_DIR/worktree-session.sh" ]; then
65
+ "$SCRIPT_DIR/worktree-session.sh" end "$SESSION_NAME"
55
66
  else
56
- echo -e "${YELLOW}⚠${NC} Session cleanup script not found"
57
- echo " Manual cleanup:"
58
- echo " 1. Commit and push changes"
59
- echo " 2. Merge branch $SESSION_BRANCH to main"
60
- echo " 3. Delete branch: git branch -D $SESSION_BRANCH"
67
+ echo -e "${RED}✗${NC} worktree-session.sh not found!"
68
+ echo " Manual cleanup needed:"
69
+ echo " 1. cd $WORKTREE_PATH && git add -A && git commit"
70
+ echo " 2. Merge branch $SESSION_NAME to main"
71
+ echo " 3. Remove worktree: git worktree remove $WORKTREE_PATH"
61
72
  exit 1
62
73
  fi
63
74
 
@@ -99,6 +110,8 @@ else
99
110
  git add -A
100
111
  # Unstage session metadata files that should never be committed
101
112
  git reset HEAD .jfl/current-session-branch.txt 2>/dev/null || true
113
+ git reset HEAD .jfl/current-worktree.txt 2>/dev/null || true
114
+ git reset HEAD .jfl/worktree-path.txt 2>/dev/null || true
102
115
 
103
116
  COMMIT_MSG="session: end $(date '+%Y-%m-%d %H:%M')"
104
117