juno-code 1.0.41 → 1.0.42

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/dist/index.js CHANGED
@@ -75,7 +75,7 @@ var __export = (target, all) => {
75
75
  exports.version = void 0;
76
76
  var init_version = __esm({
77
77
  "src/version.ts"() {
78
- exports.version = "1.0.41";
78
+ exports.version = "1.0.42";
79
79
  }
80
80
  });
81
81
  function isHeadlessEnvironment() {
package/dist/index.mjs CHANGED
@@ -44,7 +44,7 @@ var __export = (target, all) => {
44
44
  var version;
45
45
  var init_version = __esm({
46
46
  "src/version.ts"() {
47
- version = "1.0.41";
47
+ version = "1.0.42";
48
48
  }
49
49
  });
50
50
  function isHeadlessEnvironment() {
@@ -52,10 +52,9 @@ BLUE='\033[0;34m'
52
52
  NC='\033[0m' # No Color
53
53
 
54
54
  # Required packages
55
- REQUIRED_PACKAGES=("juno-kanban" "roundtable-ai")
56
-
57
- # Slack integration dependencies (optional, only installed when Slack scripts are used)
58
- SLACK_PACKAGES=("slack_sdk" "python-dotenv")
55
+ # Note: requests and python-dotenv are required by github.py
56
+ # slack_sdk is required by Slack integration scripts (slack_fetch.py, slack_respond.py)
57
+ REQUIRED_PACKAGES=("juno-kanban" "roundtable-ai" "requests" "python-dotenv" "slack_sdk")
59
58
 
60
59
  # Version check cache configuration
61
60
  # This ensures we don't check PyPI on every run (performance optimization per Task RTafs5)
@@ -4,11 +4,10 @@
4
4
  #
5
5
  # Purpose: Continuously run juno-code until all kanban tasks are completed
6
6
  #
7
- # This script uses a do-while loop pattern: it runs juno-code at least once,
8
- # then checks the kanban board for tasks in backlog, todo, or in_progress status.
9
- # If tasks remain, it continues running juno-code. This ensures juno-code's
10
- # internal task management systems get a chance to operate even if kanban.sh
11
- # doesn't initially detect any tasks.
7
+ # This script uses a while loop pattern: it ALWAYS runs pre-run hooks/commands,
8
+ # then checks the kanban board for tasks BEFORE running juno-code. If no tasks
9
+ # exist, juno-code is NOT executed. This allows pre-run hooks (e.g., Slack sync,
10
+ # GitHub sync) to create tasks that will then be processed by juno-code.
12
11
  #
13
12
  # Usage: ./.juno_task/scripts/run_until_completion.sh [options] [juno-code arguments]
14
13
  # Example: ./.juno_task/scripts/run_until_completion.sh -s claude -i 5 -v
@@ -33,6 +32,9 @@
33
32
  # - Using the flag multiple times: --pre-run-hook A --pre-run-hook B
34
33
  # - Comma-separated: --pre-run-hook "A,B,C"
35
34
  # - Pipe-separated: --pre-run-hook "A|B|C"
35
+ # --stale-threshold <n> - Number of stale iterations before exiting (default: 3)
36
+ # Set to 0 to disable stale detection
37
+ # --no-stale-check - Alias for --stale-threshold 0
36
38
  #
37
39
  # All other arguments are forwarded to juno-code.
38
40
  # The script shows all stdout/stderr from juno-code in real-time.
@@ -42,8 +44,16 @@
42
44
  # JUNO_VERBOSE=true - Show [RUN_UNTIL] informational messages
43
45
  # JUNO_PRE_RUN - Alternative way to specify pre-run command (env var)
44
46
  # JUNO_PRE_RUN_HOOK - Alternative way to specify pre-run hook name (env var)
47
+ # JUNO_STALE_THRESHOLD - Number of stale iterations before exiting (default: 3)
48
+ # Set to 0 to disable stale detection
45
49
  # (JUNO_DEBUG and JUNO_VERBOSE default to false for silent operation)
46
50
  #
51
+ # Stale Iteration Detection:
52
+ # The script tracks kanban state (task IDs and statuses) between iterations.
53
+ # If no changes are detected for JUNO_STALE_THRESHOLD consecutive iterations,
54
+ # the script will exit to prevent infinite loops where the agent doesn't
55
+ # process any tasks.
56
+ #
47
57
  # Created by: juno-code init command
48
58
  # Date: Auto-generated during project initialization
49
59
 
@@ -66,6 +76,12 @@ NC='\033[0m' # No Color
66
76
  SCRIPTS_DIR=".juno_task/scripts"
67
77
  KANBAN_SCRIPT="${SCRIPTS_DIR}/kanban.sh"
68
78
 
79
+ # Stale iteration detection configuration
80
+ # Number of consecutive iterations without kanban changes before exiting
81
+ STALE_THRESHOLD="${JUNO_STALE_THRESHOLD:-3}"
82
+ STALE_COUNTER=0
83
+ PREVIOUS_KANBAN_STATE=""
84
+
69
85
  # Arrays to store pre-run commands, hooks, and juno-code arguments
70
86
  declare -a PRE_RUN_CMDS=()
71
87
  declare -a PRE_RUN_HOOKS=()
@@ -86,6 +102,22 @@ parse_arguments() {
86
102
  PRE_RUN_CMDS+=("$2")
87
103
  shift 2
88
104
  ;;
105
+ --stale-threshold)
106
+ if [[ -z "${2:-}" ]]; then
107
+ echo "[ERROR] --stale-threshold requires a number argument" >&2
108
+ exit 1
109
+ fi
110
+ if ! [[ "$2" =~ ^[0-9]+$ ]]; then
111
+ echo "[ERROR] --stale-threshold must be a non-negative integer, got: $2" >&2
112
+ exit 1
113
+ fi
114
+ STALE_THRESHOLD="$2"
115
+ shift 2
116
+ ;;
117
+ --no-stale-check)
118
+ STALE_THRESHOLD=0
119
+ shift
120
+ ;;
89
121
  --pre-run-hook|--pre-run-hooks|--run-pre-hook|--run-pre-hooks)
90
122
  if [[ -z "${2:-}" ]]; then
91
123
  echo "[ERROR] $1 requires a hook name argument" >&2
@@ -327,6 +359,63 @@ PROJECT_ROOT="$( cd "$SCRIPT_DIR/../.." && pwd )"
327
359
  # Change to project root
328
360
  cd "$PROJECT_ROOT"
329
361
 
362
+ # Function to get a snapshot of kanban state for comparison
363
+ # Returns a sorted string of "task_id:status" pairs
364
+ get_kanban_state_snapshot() {
365
+ local snapshot=""
366
+
367
+ # Check if kanban script exists
368
+ if [ ! -f "$KANBAN_SCRIPT" ]; then
369
+ echo ""
370
+ return
371
+ fi
372
+
373
+ # Get all non-done/archive tasks as JSON
374
+ local kanban_output
375
+ if kanban_output=$("$KANBAN_SCRIPT" list --status backlog todo in_progress 2>/dev/null); then
376
+ # Extract just the JSON array part (skip the SUMMARY section)
377
+ local json_part
378
+ json_part=$(echo "$kanban_output" | grep -E '^\[' | head -1)
379
+
380
+ if [[ -z "$json_part" ]]; then
381
+ # Try to find JSON array in the output
382
+ json_part=$(echo "$kanban_output" | sed -n '/^\[/,/^\]/p' | head -1)
383
+ fi
384
+
385
+ # If we have JSON output, extract task IDs and statuses
386
+ if [[ -n "$json_part" ]] && command -v jq &> /dev/null; then
387
+ # Create a deterministic snapshot: sorted "id:status" pairs
388
+ snapshot=$(echo "$kanban_output" | jq -r 'if type == "array" then .[] | "\(.id):\(.status)" else empty end' 2>/dev/null | sort | tr '\n' '|')
389
+ else
390
+ # Fallback: use the raw output as state (less precise but still detects changes)
391
+ snapshot=$(echo "$kanban_output" | grep -E '"id"|"status"' | tr -d ' \n')
392
+ fi
393
+ fi
394
+
395
+ echo "$snapshot"
396
+ }
397
+
398
+ # Function to check if kanban state has changed
399
+ # Returns 0 if state changed, 1 if stale (no change)
400
+ check_kanban_state_changed() {
401
+ local current_state
402
+ current_state=$(get_kanban_state_snapshot)
403
+
404
+ if [ "${JUNO_DEBUG:-false}" = "true" ]; then
405
+ echo "[DEBUG] Previous kanban state: $PREVIOUS_KANBAN_STATE" >&2
406
+ echo "[DEBUG] Current kanban state: $current_state" >&2
407
+ fi
408
+
409
+ if [[ "$current_state" == "$PREVIOUS_KANBAN_STATE" ]]; then
410
+ # State is the same - no changes detected
411
+ return 1
412
+ else
413
+ # State changed
414
+ PREVIOUS_KANBAN_STATE="$current_state"
415
+ return 0
416
+ fi
417
+ }
418
+
330
419
  # Function to check if there are tasks remaining
331
420
  has_remaining_tasks() {
332
421
  log_info "Checking kanban for remaining tasks..."
@@ -391,19 +480,37 @@ main() {
391
480
  log_status "Maximum iterations: unlimited"
392
481
  fi
393
482
 
483
+ if [ "$STALE_THRESHOLD" -gt 0 ]; then
484
+ log_status "Stale iteration threshold: $STALE_THRESHOLD"
485
+ else
486
+ log_status "Stale iteration detection: disabled"
487
+ fi
488
+
489
+ # Capture initial kanban state before first iteration
490
+ PREVIOUS_KANBAN_STATE=$(get_kanban_state_snapshot)
491
+
394
492
  # Check if we have any arguments for juno-code
395
493
  if [[ ${#JUNO_ARGS[@]} -eq 0 ]]; then
396
494
  log_warning "No arguments provided. Running juno-code with no arguments."
397
495
  fi
398
496
 
399
- # Execute pre-run hooks and commands before entering the main loop
400
- # Hooks run first, then explicit commands
497
+ # ALWAYS execute pre-run hooks and commands before checking for tasks
498
+ # This ensures hooks (e.g., Slack sync, GitHub sync) can create tasks
499
+ # that will then be processed by juno-code
401
500
  execute_pre_run_hooks
402
501
  execute_pre_run_commands
403
502
 
404
- # Do-while loop pattern: Run juno-code at least once, then continue while tasks remain
405
- # This ensures juno-code's internal task management systems get a chance to operate
406
- # even if kanban.sh doesn't initially detect any tasks
503
+ # Check for tasks BEFORE entering the main loop
504
+ # If no tasks exist after running pre-run hooks, exit gracefully
505
+ if ! has_remaining_tasks; then
506
+ log_success ""
507
+ log_success "=========================================="
508
+ log_success "No tasks found in kanban. Pre-run hooks executed, juno-code skipped."
509
+ log_success "=========================================="
510
+ exit 0
511
+ fi
512
+
513
+ # While loop pattern: Only run juno-code if there are tasks to process
407
514
  while true; do
408
515
  iteration=$((iteration + 1))
409
516
 
@@ -425,7 +532,6 @@ main() {
425
532
  log_status "------------------------------------------"
426
533
 
427
534
  # Run juno-code with parsed arguments (excluding --pre-run which was already processed)
428
- # We run juno-code FIRST (do-while pattern), then check for remaining tasks
429
535
  if juno-code "${JUNO_ARGS[@]}"; then
430
536
  log_success "juno-code completed successfully"
431
537
  else
@@ -441,9 +547,52 @@ main() {
441
547
  # Small delay to prevent rapid-fire execution and allow user to Ctrl+C if needed
442
548
  sleep 1
443
549
 
444
- # Check for remaining tasks AFTER running juno-code (do-while pattern)
445
- # This ensures juno-code runs at least once, allowing its internal task
446
- # management systems to check kanban for updates
550
+ # Check for stale iterations (no kanban state changes)
551
+ # This prevents infinite loops where agent doesn't process any tasks
552
+ if [ "$STALE_THRESHOLD" -gt 0 ]; then
553
+ if check_kanban_state_changed; then
554
+ # State changed - reset the stale counter
555
+ STALE_COUNTER=0
556
+ log_info "Kanban state changed. Stale counter reset."
557
+ else
558
+ # State unchanged - increment stale counter
559
+ STALE_COUNTER=$((STALE_COUNTER + 1))
560
+ log_warning "No kanban changes detected. Stale iteration count: $STALE_COUNTER/$STALE_THRESHOLD"
561
+
562
+ if [ "$STALE_COUNTER" -ge "$STALE_THRESHOLD" ]; then
563
+ log_error ""
564
+ log_error "=========================================="
565
+ log_error "STALE ITERATION LIMIT REACHED"
566
+ log_error "=========================================="
567
+ log_error ""
568
+ log_error "The script has run $STALE_COUNTER consecutive iterations"
569
+ log_error "without any changes to the kanban board state."
570
+ log_error ""
571
+ log_error "This typically happens when:"
572
+ log_error " 1. The agent doesn't recognize or prioritize remaining tasks"
573
+ log_error " 2. Tasks are stuck in a state the agent cannot process"
574
+ log_error " 3. There's a configuration or prompt issue"
575
+ log_error ""
576
+ log_error "Remaining tasks are still in the kanban system but"
577
+ log_error "the agent is not making progress on them."
578
+ log_error ""
579
+ log_error "To adjust this threshold, set JUNO_STALE_THRESHOLD"
580
+ log_error "environment variable (current: $STALE_THRESHOLD, default: 3)"
581
+ log_error "Set to 0 to disable stale detection."
582
+ log_error ""
583
+ log_error "=========================================="
584
+ # Also print to stdout so it's visible in all contexts
585
+ echo ""
586
+ echo "STALE ITERATION LIMIT REACHED: No kanban changes detected for $STALE_COUNTER iterations."
587
+ echo "The agent is not processing remaining tasks. Exiting to prevent infinite loop."
588
+ echo "Set JUNO_STALE_THRESHOLD=0 to disable this check, or increase the threshold value."
589
+ echo ""
590
+ exit 2
591
+ fi
592
+ fi
593
+ fi
594
+
595
+ # Check for remaining tasks AFTER running juno-code
447
596
  if ! has_remaining_tasks; then
448
597
  log_success ""
449
598
  log_success "=========================================="
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "juno-code",
3
- "version": "1.0.41",
3
+ "version": "1.0.42",
4
4
  "description": "Ralph Wiggum meet Kanban! Ralph style execution for [Claude Code, Codex, Gemini, Cursor]. One task per iteration, automatic progress tracking, and git commits. Set it and let it run.",
5
5
  "keywords": [
6
6
  "Ralph",