juno-code 1.0.35 → 1.0.37

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.
File without changes
@@ -38,6 +38,9 @@ NC='\033[0m' # No Color
38
38
  # Required packages
39
39
  REQUIRED_PACKAGES=("juno-kanban" "roundtable-ai")
40
40
 
41
+ # Slack integration dependencies (optional, only installed when Slack scripts are used)
42
+ SLACK_PACKAGES=("slack_sdk" "python-dotenv")
43
+
41
44
  # Logging functions
42
45
  log_info() {
43
46
  echo -e "${BLUE}[INFO]${NC} $1"
@@ -0,0 +1,418 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # run_until_completion.sh
4
+ #
5
+ # Purpose: Continuously run juno-code until all kanban tasks are completed
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.
12
+ #
13
+ # Usage: ./.juno_task/scripts/run_until_completion.sh [options] [juno-code arguments]
14
+ # Example: ./.juno_task/scripts/run_until_completion.sh -s claude -i 5 -v
15
+ # Example: ./.juno_task/scripts/run_until_completion.sh -b shell -s claude -m :opus
16
+ # Example: ./.juno_task/scripts/run_until_completion.sh --pre-run "./slack/sync.sh" -s claude -i 5
17
+ # Example: ./.juno_task/scripts/run_until_completion.sh --pre-run-hook START_ITERATION -s claude -i 5
18
+ #
19
+ # Options (for run_until_completion.sh):
20
+ # --pre-run <cmd> - Execute command before entering the main loop
21
+ # Can be specified multiple times for multiple commands
22
+ # Commands are executed in order before juno-code starts
23
+ # --pre-run-hook <name> - Execute a named hook from .juno_task/config.json
24
+ # --pre-run-hooks <name> (alias for --pre-run-hook)
25
+ # --run-pre-hook <name> (alias for --pre-run-hook)
26
+ # --run-pre-hooks <name> (alias for --pre-run-hook)
27
+ # The hook should be defined in config.json under "hooks"
28
+ # with a "commands" array. All commands in the hook are
29
+ # executed before the main loop.
30
+ # Can be specified multiple times for multiple hooks.
31
+ #
32
+ # All other arguments are forwarded to juno-code.
33
+ # The script shows all stdout/stderr from juno-code in real-time.
34
+ #
35
+ # Environment Variables:
36
+ # JUNO_DEBUG=true - Show [DEBUG] diagnostic messages
37
+ # JUNO_VERBOSE=true - Show [RUN_UNTIL] informational messages
38
+ # JUNO_PRE_RUN - Alternative way to specify pre-run command (env var)
39
+ # JUNO_PRE_RUN_HOOK - Alternative way to specify pre-run hook name (env var)
40
+ # (JUNO_DEBUG and JUNO_VERBOSE default to false for silent operation)
41
+ #
42
+ # Created by: juno-code init command
43
+ # Date: Auto-generated during project initialization
44
+
45
+ set -euo pipefail # Exit on error, undefined variable, or pipe failure
46
+
47
+ # DEBUG OUTPUT: Show that run_until_completion.sh is being executed
48
+ if [ "${JUNO_DEBUG:-false}" = "true" ]; then
49
+ echo "[DEBUG] run_until_completion.sh is being executed from: $(pwd)" >&2
50
+ fi
51
+
52
+ # Color output for better readability
53
+ RED='\033[0;31m'
54
+ GREEN='\033[0;32m'
55
+ YELLOW='\033[1;33m'
56
+ BLUE='\033[0;34m'
57
+ CYAN='\033[0;36m'
58
+ NC='\033[0m' # No Color
59
+
60
+ # Configuration
61
+ SCRIPTS_DIR=".juno_task/scripts"
62
+ KANBAN_SCRIPT="${SCRIPTS_DIR}/kanban.sh"
63
+
64
+ # Arrays to store pre-run commands, hooks, and juno-code arguments
65
+ declare -a PRE_RUN_CMDS=()
66
+ declare -a PRE_RUN_HOOKS=()
67
+ declare -a JUNO_ARGS=()
68
+
69
+ # Configuration file path
70
+ CONFIG_FILE=".juno_task/config.json"
71
+
72
+ # Parse arguments to extract --pre-run and --pre-run-hook commands
73
+ parse_arguments() {
74
+ while [[ $# -gt 0 ]]; do
75
+ case $1 in
76
+ --pre-run)
77
+ if [[ -z "${2:-}" ]]; then
78
+ echo "[ERROR] --pre-run requires a command argument" >&2
79
+ exit 1
80
+ fi
81
+ PRE_RUN_CMDS+=("$2")
82
+ shift 2
83
+ ;;
84
+ --pre-run-hook|--pre-run-hooks|--run-pre-hook|--run-pre-hooks)
85
+ if [[ -z "${2:-}" ]]; then
86
+ echo "[ERROR] $1 requires a hook name argument" >&2
87
+ exit 1
88
+ fi
89
+ PRE_RUN_HOOKS+=("$2")
90
+ shift 2
91
+ ;;
92
+ *)
93
+ JUNO_ARGS+=("$1")
94
+ shift
95
+ ;;
96
+ esac
97
+ done
98
+
99
+ # Also check JUNO_PRE_RUN environment variable
100
+ if [[ -n "${JUNO_PRE_RUN:-}" ]]; then
101
+ # Prepend env var command (runs first)
102
+ PRE_RUN_CMDS=("$JUNO_PRE_RUN" "${PRE_RUN_CMDS[@]}")
103
+ fi
104
+
105
+ # Also check JUNO_PRE_RUN_HOOK environment variable
106
+ if [[ -n "${JUNO_PRE_RUN_HOOK:-}" ]]; then
107
+ # Prepend env var hook (runs first)
108
+ PRE_RUN_HOOKS=("$JUNO_PRE_RUN_HOOK" "${PRE_RUN_HOOKS[@]}")
109
+ fi
110
+ }
111
+
112
+ # Execute commands from a hook defined in config.json
113
+ execute_hook_commands() {
114
+ local hook_name="$1"
115
+
116
+ # Check if config file exists
117
+ if [[ ! -f "$CONFIG_FILE" ]]; then
118
+ log_error "Config file not found: $CONFIG_FILE"
119
+ log_error "Cannot execute hook: $hook_name"
120
+ return 1
121
+ fi
122
+
123
+ # Check if jq is available
124
+ if ! command -v jq &> /dev/null; then
125
+ log_error "jq is required for --pre-run-hook but not installed"
126
+ log_error "Please install jq: brew install jq (macOS) or apt-get install jq (Linux)"
127
+ return 1
128
+ fi
129
+
130
+ # Check if hook exists in config
131
+ local hook_exists
132
+ hook_exists=$(jq -e ".hooks.\"$hook_name\"" "$CONFIG_FILE" 2>/dev/null)
133
+ if [[ $? -ne 0 ]] || [[ "$hook_exists" == "null" ]]; then
134
+ log_error "Hook '$hook_name' not found in $CONFIG_FILE"
135
+ log_error "Available hooks: $(jq -r '.hooks | keys | join(", ")' "$CONFIG_FILE" 2>/dev/null || echo "none")"
136
+ return 1
137
+ fi
138
+
139
+ # Get commands array from hook
140
+ local commands_json
141
+ commands_json=$(jq -r ".hooks.\"$hook_name\".commands // []" "$CONFIG_FILE" 2>/dev/null)
142
+
143
+ # Get number of commands
144
+ local num_commands
145
+ num_commands=$(echo "$commands_json" | jq 'length')
146
+
147
+ if [[ "$num_commands" -eq 0 ]]; then
148
+ log_warning "Hook '$hook_name' has no commands defined"
149
+ return 0
150
+ fi
151
+
152
+ log_status ""
153
+ log_status "Executing hook '$hook_name' ($num_commands command(s))"
154
+ log_status "------------------------------------------"
155
+
156
+ # Execute each command in the hook
157
+ local idx=0
158
+ while [[ $idx -lt $num_commands ]]; do
159
+ local cmd
160
+ cmd=$(echo "$commands_json" | jq -r ".[$idx]")
161
+ idx=$((idx + 1))
162
+
163
+ log_status "Hook command [$idx/$num_commands]: $cmd"
164
+
165
+ if eval "$cmd"; then
166
+ log_success "Hook command [$idx/$num_commands] completed successfully"
167
+ else
168
+ local exit_code=$?
169
+ log_error "Hook command [$idx/$num_commands] failed with exit code $exit_code"
170
+ log_error "Command was: $cmd"
171
+ # Continue with next command even if one fails
172
+ fi
173
+ done
174
+
175
+ return 0
176
+ }
177
+
178
+ # Execute all pre-run hooks
179
+ execute_pre_run_hooks() {
180
+ local hook_count=${#PRE_RUN_HOOKS[@]}
181
+
182
+ if [[ $hook_count -eq 0 ]]; then
183
+ return 0
184
+ fi
185
+
186
+ log_status ""
187
+ log_status "=========================================="
188
+ log_status "Executing $hook_count pre-run hook(s)"
189
+ log_status "=========================================="
190
+
191
+ local idx=0
192
+ for hook_name in "${PRE_RUN_HOOKS[@]}"; do
193
+ idx=$((idx + 1))
194
+ log_status ""
195
+ log_status "Pre-run hook [$idx/$hook_count]: $hook_name"
196
+
197
+ if execute_hook_commands "$hook_name"; then
198
+ log_success "Pre-run hook [$idx/$hook_count] '$hook_name' completed"
199
+ else
200
+ log_error "Pre-run hook [$idx/$hook_count] '$hook_name' had errors"
201
+ # Continue with next hook even if one fails
202
+ fi
203
+ done
204
+
205
+ log_status ""
206
+ log_status "=========================================="
207
+ log_status "Pre-run hooks phase complete"
208
+ log_status "=========================================="
209
+ }
210
+
211
+ # Execute all pre-run commands
212
+ execute_pre_run_commands() {
213
+ local cmd_count=${#PRE_RUN_CMDS[@]}
214
+
215
+ if [[ $cmd_count -eq 0 ]]; then
216
+ return 0
217
+ fi
218
+
219
+ log_status ""
220
+ log_status "=========================================="
221
+ log_status "Executing $cmd_count pre-run command(s)"
222
+ log_status "=========================================="
223
+
224
+ local idx=0
225
+ for cmd in "${PRE_RUN_CMDS[@]}"; do
226
+ idx=$((idx + 1))
227
+ log_status ""
228
+ log_status "Pre-run [$idx/$cmd_count]: $cmd"
229
+ log_status "------------------------------------------"
230
+
231
+ # Execute the command
232
+ if eval "$cmd"; then
233
+ log_success "Pre-run [$idx/$cmd_count] completed successfully"
234
+ else
235
+ local exit_code=$?
236
+ log_error "Pre-run [$idx/$cmd_count] failed with exit code $exit_code"
237
+ log_error "Command was: $cmd"
238
+ # Continue with next pre-run command even if one fails
239
+ # This allows partial execution like Slack sync failing but still running juno-code
240
+ fi
241
+ done
242
+
243
+ log_status ""
244
+ log_status "=========================================="
245
+ log_status "Pre-run phase complete"
246
+ log_status "=========================================="
247
+ }
248
+
249
+ # Logging functions
250
+ log_info() {
251
+ # Only print if JUNO_VERBOSE is set to true
252
+ if [ "${JUNO_VERBOSE:-false}" = "true" ]; then
253
+ echo -e "${BLUE}[RUN_UNTIL]${NC} $1" >&2
254
+ fi
255
+ }
256
+
257
+ log_success() {
258
+ # Only print if JUNO_VERBOSE is set to true
259
+ if [ "${JUNO_VERBOSE:-false}" = "true" ]; then
260
+ echo -e "${GREEN}[RUN_UNTIL]${NC} $1" >&2
261
+ fi
262
+ }
263
+
264
+ log_warning() {
265
+ # Only print if JUNO_VERBOSE is set to true
266
+ if [ "${JUNO_VERBOSE:-false}" = "true" ]; then
267
+ echo -e "${YELLOW}[RUN_UNTIL]${NC} $1" >&2
268
+ fi
269
+ }
270
+
271
+ log_error() {
272
+ # Always print errors regardless of JUNO_VERBOSE
273
+ echo -e "${RED}[RUN_UNTIL]${NC} $1" >&2
274
+ }
275
+
276
+ log_status() {
277
+ # Always print status updates so user knows what's happening
278
+ echo -e "${CYAN}[RUN_UNTIL]${NC} $1" >&2
279
+ }
280
+
281
+ # Get the directory where this script is located
282
+ SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
283
+
284
+ # Navigate to project root (parent of scripts directory)
285
+ PROJECT_ROOT="$( cd "$SCRIPT_DIR/../.." && pwd )"
286
+
287
+ # Change to project root
288
+ cd "$PROJECT_ROOT"
289
+
290
+ # Function to check if there are tasks remaining
291
+ has_remaining_tasks() {
292
+ log_info "Checking kanban for remaining tasks..."
293
+
294
+ # Check if kanban script exists
295
+ if [ ! -f "$KANBAN_SCRIPT" ]; then
296
+ log_error "Kanban script not found: $KANBAN_SCRIPT"
297
+ log_error "Please run 'juno-code init' to initialize the project"
298
+ return 1
299
+ fi
300
+
301
+ # Make sure the script is executable
302
+ chmod +x "$KANBAN_SCRIPT"
303
+
304
+ # Run kanban list and check for "No results found"
305
+ # We capture both stdout and stderr to handle various output formats
306
+ local kanban_output
307
+ if kanban_output=$("$KANBAN_SCRIPT" list --status backlog todo in_progress 2>&1); then
308
+ if echo "$kanban_output" | grep -q "No results found"; then
309
+ log_info "No remaining tasks found"
310
+ return 1 # No remaining tasks
311
+ else
312
+ log_info "Found remaining tasks"
313
+ if [ "${JUNO_DEBUG:-false}" = "true" ]; then
314
+ echo "[DEBUG] Kanban output:" >&2
315
+ echo "$kanban_output" >&2
316
+ fi
317
+ return 0 # Has remaining tasks
318
+ fi
319
+ else
320
+ # kanban.sh returned non-zero, check if it's because no results
321
+ if echo "$kanban_output" | grep -q "No results found"; then
322
+ log_info "No remaining tasks found (from error output)"
323
+ return 1
324
+ fi
325
+ log_error "Failed to check kanban status"
326
+ log_error "Output: $kanban_output"
327
+ return 1 # Treat errors as "no tasks" to prevent infinite loops
328
+ fi
329
+ }
330
+
331
+ # Main run loop
332
+ main() {
333
+ local iteration=0
334
+ local max_iterations="${JUNO_RUN_UNTIL_MAX_ITERATIONS:-0}" # 0 = unlimited
335
+
336
+ # Parse arguments first to extract --pre-run commands
337
+ parse_arguments "$@"
338
+
339
+ log_status "=== Run Until Completion ==="
340
+ if [[ ${#PRE_RUN_HOOKS[@]} -gt 0 ]]; then
341
+ log_status "Pre-run hooks: ${PRE_RUN_HOOKS[*]}"
342
+ fi
343
+ if [[ ${#PRE_RUN_CMDS[@]} -gt 0 ]]; then
344
+ log_status "Pre-run commands: ${#PRE_RUN_CMDS[@]}"
345
+ fi
346
+ log_status "Arguments to juno-code: ${JUNO_ARGS[*]:-<none>}"
347
+
348
+ if [ "$max_iterations" -gt 0 ]; then
349
+ log_status "Maximum iterations: $max_iterations"
350
+ else
351
+ log_status "Maximum iterations: unlimited"
352
+ fi
353
+
354
+ # Check if we have any arguments for juno-code
355
+ if [[ ${#JUNO_ARGS[@]} -eq 0 ]]; then
356
+ log_warning "No arguments provided. Running juno-code with no arguments."
357
+ fi
358
+
359
+ # Execute pre-run hooks and commands before entering the main loop
360
+ # Hooks run first, then explicit commands
361
+ execute_pre_run_hooks
362
+ execute_pre_run_commands
363
+
364
+ # Do-while loop pattern: Run juno-code at least once, then continue while tasks remain
365
+ # This ensures juno-code's internal task management systems get a chance to operate
366
+ # even if kanban.sh doesn't initially detect any tasks
367
+ while true; do
368
+ iteration=$((iteration + 1))
369
+
370
+ log_status ""
371
+ log_status "=========================================="
372
+ log_status "Iteration $iteration"
373
+ log_status "=========================================="
374
+
375
+ # Check max iterations limit BEFORE running (prevents exceeding limit)
376
+ if [ "$max_iterations" -gt 0 ] && [ "$iteration" -gt "$max_iterations" ]; then
377
+ log_warning ""
378
+ log_warning "=========================================="
379
+ log_warning "Maximum iterations ($max_iterations) reached. Exiting."
380
+ log_warning "=========================================="
381
+ exit 0
382
+ fi
383
+
384
+ log_status "Running juno-code with args: ${JUNO_ARGS[*]:-<none>}"
385
+ log_status "------------------------------------------"
386
+
387
+ # Run juno-code with parsed arguments (excluding --pre-run which was already processed)
388
+ # We run juno-code FIRST (do-while pattern), then check for remaining tasks
389
+ if juno-code "${JUNO_ARGS[@]}"; then
390
+ log_success "juno-code completed successfully"
391
+ else
392
+ local exit_code=$?
393
+ log_warning "juno-code exited with code $exit_code"
394
+ # Continue the loop even if juno-code fails - it might succeed next iteration
395
+ # Some failures are expected (e.g., partial task completion)
396
+ fi
397
+
398
+ log_status "------------------------------------------"
399
+ log_status "Iteration $iteration complete. Checking for more tasks..."
400
+
401
+ # Small delay to prevent rapid-fire execution and allow user to Ctrl+C if needed
402
+ sleep 1
403
+
404
+ # Check for remaining tasks AFTER running juno-code (do-while pattern)
405
+ # This ensures juno-code runs at least once, allowing its internal task
406
+ # management systems to check kanban for updates
407
+ if ! has_remaining_tasks; then
408
+ log_success ""
409
+ log_success "=========================================="
410
+ log_success "All tasks completed! Exiting after $iteration iteration(s)."
411
+ log_success "=========================================="
412
+ exit 0
413
+ fi
414
+ done
415
+ }
416
+
417
+ # Run main function with all arguments
418
+ main "$@"