juno-code 1.0.47 → 1.0.49
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/README.md +44 -8
- package/dist/bin/cli.d.mts +17 -0
- package/dist/bin/cli.d.ts +17 -0
- package/dist/bin/cli.js +5606 -17514
- package/dist/bin/cli.js.map +1 -1
- package/dist/bin/cli.mjs +5647 -17553
- package/dist/bin/cli.mjs.map +1 -1
- package/dist/bin/feedback-collector.d.mts +2 -0
- package/dist/bin/feedback-collector.d.ts +2 -0
- package/dist/bin/feedback-collector.js.map +1 -1
- package/dist/bin/feedback-collector.mjs.map +1 -1
- package/dist/index.d.mts +2107 -0
- package/dist/index.d.ts +2107 -0
- package/dist/index.js +3760 -14730
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +3760 -14537
- package/dist/index.mjs.map +1 -1
- package/dist/templates/extensions/pi/juno-skill-preprocessor.ts +239 -0
- package/dist/templates/scripts/__pycache__/github.cpython-313.pyc +0 -0
- package/dist/templates/scripts/__pycache__/parallel_runner.cpython-313.pyc +0 -0
- package/dist/templates/scripts/__pycache__/slack_respond.cpython-313.pyc +0 -0
- package/dist/templates/scripts/kanban.sh +18 -4
- package/dist/templates/scripts/parallel_runner.sh +2242 -0
- package/dist/templates/services/README.md +61 -1
- package/dist/templates/services/__pycache__/claude.cpython-313.pyc +0 -0
- package/dist/templates/services/__pycache__/codex.cpython-313.pyc +0 -0
- package/dist/templates/services/__pycache__/pi.cpython-313.pyc +0 -0
- package/dist/templates/services/claude.py +132 -33
- package/dist/templates/services/codex.py +179 -66
- package/dist/templates/services/gemini.py +117 -27
- package/dist/templates/services/pi.py +1753 -0
- package/dist/templates/skills/claude/plan-kanban-tasks/SKILL.md +14 -7
- package/dist/templates/skills/claude/ralph-loop/SKILL.md +18 -22
- package/dist/templates/skills/claude/ralph-loop/references/first_check.md +15 -14
- package/dist/templates/skills/claude/ralph-loop/references/implement.md +17 -17
- package/dist/templates/skills/claude/ralph-loop/scripts/kanban.sh +18 -4
- package/dist/templates/skills/claude/understand-project/SKILL.md +14 -7
- package/dist/templates/skills/codex/ralph-loop/SKILL.md +18 -22
- package/dist/templates/skills/codex/ralph-loop/references/first_check.md +15 -14
- package/dist/templates/skills/codex/ralph-loop/references/implement.md +17 -17
- package/dist/templates/skills/codex/ralph-loop/scripts/kanban.sh +18 -4
- package/dist/templates/skills/pi/.gitkeep +0 -0
- package/dist/templates/skills/pi/plan-kanban-tasks/SKILL.md +32 -0
- package/dist/templates/skills/pi/ralph-loop/SKILL.md +39 -0
- package/dist/templates/skills/pi/ralph-loop/references/first_check.md +21 -0
- package/dist/templates/skills/pi/ralph-loop/references/implement.md +99 -0
- package/dist/templates/skills/pi/understand-project/SKILL.md +46 -0
- package/package.json +20 -42
- package/dist/templates/scripts/__pycache__/attachment_downloader.cpython-38.pyc +0 -0
- package/dist/templates/scripts/__pycache__/github.cpython-38.pyc +0 -0
- package/dist/templates/scripts/__pycache__/slack_fetch.cpython-38.pyc +0 -0
- package/dist/templates/scripts/__pycache__/slack_state.cpython-38.pyc +0 -0
- package/dist/templates/services/__pycache__/claude.cpython-38.pyc +0 -0
- package/dist/templates/services/__pycache__/codex.cpython-38.pyc +0 -0
|
@@ -9,6 +9,8 @@ import json
|
|
|
9
9
|
import os
|
|
10
10
|
import subprocess
|
|
11
11
|
import sys
|
|
12
|
+
import threading
|
|
13
|
+
import time
|
|
12
14
|
from datetime import datetime
|
|
13
15
|
from typing import List, Optional, Tuple
|
|
14
16
|
|
|
@@ -351,9 +353,28 @@ Examples:
|
|
|
351
353
|
|
|
352
354
|
def run_gemini(self, cmd: List[str], verbose: bool = False) -> int:
|
|
353
355
|
"""Execute the Gemini CLI and normalize streaming output for shell-backend consumption."""
|
|
356
|
+
capture_path = os.environ.get("JUNO_SUBAGENT_CAPTURE_PATH")
|
|
354
357
|
if verbose:
|
|
355
|
-
|
|
356
|
-
|
|
358
|
+
# Truncate prompt in display to avoid confusing multi-line output
|
|
359
|
+
display_cmd = []
|
|
360
|
+
skip_next = False
|
|
361
|
+
for i, part in enumerate(cmd):
|
|
362
|
+
if skip_next:
|
|
363
|
+
skip_next = False
|
|
364
|
+
continue
|
|
365
|
+
if part == "-p" and i + 1 < len(cmd):
|
|
366
|
+
prompt_val = cmd[i + 1]
|
|
367
|
+
if len(prompt_val) > 80 or "\n" in prompt_val:
|
|
368
|
+
first_line = prompt_val.split("\n")[0][:60]
|
|
369
|
+
display_cmd.append(f'-p "{first_line}..." ({len(prompt_val)} chars)')
|
|
370
|
+
else:
|
|
371
|
+
display_cmd.append(f"-p {prompt_val}")
|
|
372
|
+
skip_next = True
|
|
373
|
+
else:
|
|
374
|
+
display_cmd.append(part)
|
|
375
|
+
if not capture_path:
|
|
376
|
+
print(f"Executing: {' '.join(display_cmd)}", file=sys.stderr)
|
|
377
|
+
print("-" * 80, file=sys.stderr)
|
|
357
378
|
|
|
358
379
|
try:
|
|
359
380
|
process = subprocess.Popen(
|
|
@@ -366,33 +387,92 @@ Examples:
|
|
|
366
387
|
cwd=self.project_path,
|
|
367
388
|
)
|
|
368
389
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
390
|
+
# Watchdog thread: handles two scenarios where the stdout loop blocks:
|
|
391
|
+
# 1. Process exits but its stdout pipe stays open (inherited FDs)
|
|
392
|
+
# 2. Process itself never exits (hung event loop)
|
|
393
|
+
# The watchdog waits for an "output done" signal from the main thread
|
|
394
|
+
# (set when the stdout loop finishes). Once signaled, it gives the
|
|
395
|
+
# process a grace period to exit, then terminates it and closes stdout.
|
|
396
|
+
wait_timeout = int(os.environ.get("GEMINI_WAIT_TIMEOUT", "30"))
|
|
397
|
+
output_done = threading.Event()
|
|
398
|
+
|
|
399
|
+
def _stdout_watchdog():
|
|
400
|
+
"""Terminate process and close stdout pipe if it hangs after output."""
|
|
401
|
+
# Wait until the main thread signals output is done,
|
|
402
|
+
# OR until the process exits on its own (poll every second).
|
|
403
|
+
while not output_done.is_set():
|
|
404
|
+
if process.poll() is not None:
|
|
405
|
+
break
|
|
406
|
+
output_done.wait(timeout=1)
|
|
407
|
+
|
|
408
|
+
if output_done.is_set() and process.poll() is None:
|
|
409
|
+
# Output is done but process hasn't exited — give it grace period.
|
|
410
|
+
try:
|
|
411
|
+
process.wait(timeout=wait_timeout)
|
|
412
|
+
except subprocess.TimeoutExpired:
|
|
413
|
+
print(
|
|
414
|
+
f"Warning: Gemini process did not exit within {wait_timeout}s after output. Terminating.",
|
|
415
|
+
file=sys.stderr
|
|
416
|
+
)
|
|
417
|
+
process.terminate()
|
|
418
|
+
try:
|
|
419
|
+
process.wait(timeout=5)
|
|
420
|
+
except subprocess.TimeoutExpired:
|
|
421
|
+
print("Warning: Gemini process did not respond to SIGTERM. Killing.", file=sys.stderr)
|
|
422
|
+
process.kill()
|
|
423
|
+
try:
|
|
424
|
+
process.wait(timeout=5)
|
|
425
|
+
except subprocess.TimeoutExpired:
|
|
426
|
+
pass
|
|
378
427
|
|
|
379
|
-
|
|
428
|
+
# Process has exited (naturally or forcefully).
|
|
429
|
+
# Grace period for remaining pipe data to flush, then close stdout.
|
|
430
|
+
time.sleep(2)
|
|
431
|
+
try:
|
|
432
|
+
if process.stdout and not process.stdout.closed:
|
|
433
|
+
process.stdout.close()
|
|
434
|
+
except Exception:
|
|
435
|
+
pass
|
|
380
436
|
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
try:
|
|
384
|
-
parsed = json.loads(part)
|
|
385
|
-
formatted = self._format_event_pretty(parsed)
|
|
386
|
-
print(formatted, flush=True)
|
|
387
|
-
except Exception:
|
|
388
|
-
print(part, flush=True)
|
|
389
|
-
continue
|
|
437
|
+
watchdog = threading.Thread(target=_stdout_watchdog, daemon=True)
|
|
438
|
+
watchdog.start()
|
|
390
439
|
|
|
391
|
-
|
|
392
|
-
if pending:
|
|
393
|
-
continue
|
|
440
|
+
pending = ""
|
|
394
441
|
|
|
395
|
-
|
|
442
|
+
if process.stdout:
|
|
443
|
+
try:
|
|
444
|
+
for raw_line in process.stdout:
|
|
445
|
+
combined = (pending + raw_line).replace("\x7f", "\n")
|
|
446
|
+
pending = ""
|
|
447
|
+
|
|
448
|
+
if not combined.strip():
|
|
449
|
+
continue
|
|
450
|
+
|
|
451
|
+
parts, pending = self._split_json_stream(combined)
|
|
452
|
+
|
|
453
|
+
if parts:
|
|
454
|
+
for part in parts:
|
|
455
|
+
try:
|
|
456
|
+
parsed = json.loads(part)
|
|
457
|
+
formatted = self._format_event_pretty(parsed)
|
|
458
|
+
print(formatted, flush=True)
|
|
459
|
+
except Exception:
|
|
460
|
+
print(part, flush=True)
|
|
461
|
+
continue
|
|
462
|
+
|
|
463
|
+
# Fallback for non-JSON lines or partial content
|
|
464
|
+
if pending:
|
|
465
|
+
continue
|
|
466
|
+
|
|
467
|
+
print(combined, end="" if combined.endswith("\n") else "\n", flush=True)
|
|
468
|
+
except ValueError:
|
|
469
|
+
# Watchdog closed stdout because the process exited but the
|
|
470
|
+
# pipe stayed open (inherited FDs from child processes).
|
|
471
|
+
# This is expected — all output has been consumed.
|
|
472
|
+
pass
|
|
473
|
+
|
|
474
|
+
# Signal watchdog that output is done (stdout loop has exited).
|
|
475
|
+
output_done.set()
|
|
396
476
|
|
|
397
477
|
if pending.strip():
|
|
398
478
|
try:
|
|
@@ -401,7 +481,13 @@ Examples:
|
|
|
401
481
|
except Exception:
|
|
402
482
|
print(pending, flush=True)
|
|
403
483
|
|
|
404
|
-
process.
|
|
484
|
+
# Ensure process has exited. The watchdog thread handles termination
|
|
485
|
+
# if the process hangs, so by this point it should already be dead.
|
|
486
|
+
# Use a short timeout as a safety net.
|
|
487
|
+
try:
|
|
488
|
+
process.wait(timeout=5)
|
|
489
|
+
except subprocess.TimeoutExpired:
|
|
490
|
+
pass # Watchdog thread will handle cleanup
|
|
405
491
|
|
|
406
492
|
if process.stderr and process.returncode != 0:
|
|
407
493
|
stderr_output = process.stderr.read()
|
|
@@ -414,7 +500,11 @@ Examples:
|
|
|
414
500
|
print("\nInterrupted by user", file=sys.stderr)
|
|
415
501
|
try:
|
|
416
502
|
process.terminate()
|
|
417
|
-
|
|
503
|
+
try:
|
|
504
|
+
process.wait(timeout=5)
|
|
505
|
+
except subprocess.TimeoutExpired:
|
|
506
|
+
process.kill()
|
|
507
|
+
process.wait(timeout=5)
|
|
418
508
|
except Exception:
|
|
419
509
|
pass
|
|
420
510
|
return 130
|