create-merlin-brain 5.3.2 → 5.3.3

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/bin/install.cjs CHANGED
@@ -1733,8 +1733,9 @@ ${colors.cyan}Universal Task Optimization (NEW in 5.3.0):${colors.reset}
1733
1733
  • ${colors.bright}/merlin:polish${colors.reset} - Animation polish via animation-expert
1734
1734
  • ${colors.bright}/merlin:redesign${colors.reset} - Full redesign via ui-builder
1735
1735
 
1736
- ${colors.cyan}Duo permissions (NEW in 5.3.2):${colors.reset}
1737
- Auto-allowlist codex/duo Bash commands when Codex is installed
1736
+ ${colors.cyan}Duo reliability (NEW in 5.3.3):${colors.reset}
1737
+ Portable codex timeout wrapper (no more "gtimeout: not found" on macOS)
1738
+ • codex-as.sh now accepts --timeout for long parallel planning runs
1738
1739
 
1739
1740
  ${colors.cyan}Merlin works with or without Sights:${colors.reset}
1740
1741
  • ${colors.green}With Sights${colors.reset}: Instant context, cross-session memory
@@ -256,9 +256,12 @@ When duo is OFF, Merlin runs a pre-route hook at the start of every routing deci
256
256
 
257
257
  `duo-installed.sh` only checks PATH presence. If Codex is on PATH but fails at runtime:
258
258
 
259
- 1. Wrap all Codex invocations with a 60s timeout:
260
- - With coreutils: `gtimeout 60s codex …`
261
- - Without coreutils: `perl -e 'alarm 60; exec @ARGV' codex …`
259
+ 1. Wrap all Codex invocations with a timeout using the portable wrapper:
260
+ - `~/.claude/scripts/with-timeout.sh <seconds> codex exec …`
261
+ (Auto-detects gtimeout / timeout / perl alarm DO NOT compose raw `gtimeout`
262
+ or `timeout` calls; they are not always installed.)
263
+ - For long-running parallel planning runs, prefer `codex-as.sh --timeout 1800 <agent> "<task>"`
264
+ - For 60-second probes, the existing `duo-codex-call.sh` already enforces this via MERLIN_CODEX_TIMEOUT_SEC (default 60s)
262
265
  2. On Codex error or timeout: log to `~/.claude/merlin-state/duo-decisions.log` with severity `codex_runtime_failure`. Fall back to Claude for that step:
263
266
  - Parallel branch: drop the codex result; arbiter receives one input
264
267
  - Sequential branch: Claude takes the author role
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env bash
2
2
  # codex-as.sh — invoke Codex as a Merlin specialist agent
3
- # Usage: codex-as.sh <agent-name> <task-text> [--model <model-name>]
3
+ # Usage: codex-as.sh <agent-name> <task-text> [--model <model-name>] [--timeout <seconds>]
4
4
 
5
5
  set -euo pipefail
6
6
 
@@ -10,6 +10,7 @@ command -v codex >/dev/null 2>&1 || exit 0
10
10
  AGENT_NAME=""
11
11
  TASK_TEXT=""
12
12
  MODEL_FLAG=""
13
+ TIMEOUT_SEC="${MERLIN_CODEX_TIMEOUT_SEC:-1800}"
13
14
 
14
15
  # Parse arguments
15
16
  while [[ $# -gt 0 ]]; do
@@ -23,6 +24,15 @@ while [[ $# -gt 0 ]]; do
23
24
  exit 1
24
25
  fi
25
26
  ;;
27
+ --timeout)
28
+ if [[ -n "${2:-}" && "$2" =~ ^[0-9]+$ ]]; then
29
+ TIMEOUT_SEC="$2"
30
+ shift 2
31
+ else
32
+ echo "Error: --timeout requires a non-negative integer" >&2
33
+ exit 1
34
+ fi
35
+ ;;
26
36
  *)
27
37
  if [[ -z "$AGENT_NAME" ]]; then
28
38
  AGENT_NAME="$1"
@@ -73,5 +83,13 @@ ${TASK_TEXT}"
73
83
  # (The legacy --write flag was removed from `codex exec`; -s workspace-write is the
74
84
  # current equivalent. Use --dangerously-bypass-approvals-and-sandbox only if you
75
85
  # explicitly want to skip all prompts — workspace-write is the safer default.)
76
- # shellcheck disable=SC2086
77
- exec codex exec -s workspace-write --cd "$PWD" $MODEL_FLAG "$FULL_PROMPT"
86
+ # Wrap with timeout using the portable with-timeout.sh wrapper.
87
+ WITH_TIMEOUT="$(dirname "${BASH_SOURCE[0]}")/with-timeout.sh"
88
+ if [[ -x "$WITH_TIMEOUT" ]]; then
89
+ # shellcheck disable=SC2086
90
+ exec "$WITH_TIMEOUT" "$TIMEOUT_SEC" codex exec -s workspace-write --cd "$PWD" $MODEL_FLAG "$FULL_PROMPT"
91
+ else
92
+ # Fallback if with-timeout.sh missing (shouldn't happen post-install)
93
+ # shellcheck disable=SC2086
94
+ exec codex exec -s workspace-write --cd "$PWD" $MODEL_FLAG "$FULL_PROMPT"
95
+ fi
@@ -3,10 +3,14 @@
3
3
  # Usage: duo-codex-call.sh <codex-command> [args...]
4
4
  # Exit 0: success (stdout/stderr forwarded)
5
5
  # Exit 75 (TEMPFAIL): codex failed or timed out — caller should fall back to Claude
6
- # Always exits — never hangs beyond 60s
6
+ # Always exits — never hangs beyond MERLIN_CODEX_TIMEOUT_SEC (default 60s)
7
+ #
8
+ # Environment variables:
9
+ # MERLIN_CODEX_TIMEOUT_SEC — timeout in seconds (default: 60)
7
10
 
8
11
  set -euo pipefail
9
12
 
13
+ TIMEOUT_SEC="${MERLIN_CODEX_TIMEOUT_SEC:-60}"
10
14
  FAILURES_FILE="${HOME}/.claude/merlin-state/.duo-codex-failures"
11
15
  DECISIONS_LOG="${HOME}/.claude/merlin-state/duo-decisions.log"
12
16
  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
@@ -47,21 +51,15 @@ _auto_disable_duo() {
47
51
  _write_counter 0
48
52
  }
49
53
 
50
- # --- Build timeout command ---
51
- _TIMEOUT=""
52
- if command -v gtimeout >/dev/null 2>&1; then
53
- _TIMEOUT="gtimeout 60"
54
- elif timeout --version >/dev/null 2>&1; then
55
- _TIMEOUT="timeout 60"
56
- fi
57
-
58
- # --- Execute (capture exit code without triggering set -e) ---
54
+ # --- Execute with timeout (capture exit code without triggering set -e) ---
55
+ WITH_TIMEOUT="${SCRIPT_DIR}/with-timeout.sh"
59
56
  EXIT_CODE=0
60
- if [[ -n "$_TIMEOUT" ]]; then
61
- $_TIMEOUT "$@" || EXIT_CODE=$?
57
+
58
+ if [[ -x "$WITH_TIMEOUT" ]]; then
59
+ "$WITH_TIMEOUT" "$TIMEOUT_SEC" "$@" || EXIT_CODE=$?
62
60
  else
63
- # perl alarm fallback for macOS without coreutils
64
- perl -e 'alarm 60; exec @ARGV or exit 127' -- "$@" || EXIT_CODE=$?
61
+ # Fallback if with-timeout.sh missing (shouldn't happen post-install)
62
+ perl -e 'alarm shift; exec @ARGV or exit 127' "$TIMEOUT_SEC" -- "$@" || EXIT_CODE=$?
65
63
  fi
66
64
 
67
65
  if [[ $EXIT_CODE -eq 0 ]]; then
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env bash
2
+ # with-timeout.sh — portable timeout wrapper (gtimeout → timeout → perl alarm)
3
+ # Usage: with-timeout.sh <seconds> <command> [args...]
4
+ # Exit 124 on timeout (GNU convention). Otherwise the wrapped command's exit code.
5
+
6
+ set -euo pipefail
7
+
8
+ if [[ $# -lt 2 ]]; then
9
+ echo "Usage: with-timeout.sh <seconds> <command> [args...]" >&2
10
+ exit 2
11
+ fi
12
+
13
+ SECS="$1"
14
+ shift
15
+
16
+ if ! [[ "$SECS" =~ ^[0-9]+$ ]]; then
17
+ echo "with-timeout.sh: timeout must be a non-negative integer (got: $SECS)" >&2
18
+ exit 2
19
+ fi
20
+
21
+ if command -v gtimeout >/dev/null 2>&1; then
22
+ exec gtimeout --preserve-status --signal=TERM "${SECS}s" "$@"
23
+ elif command -v timeout >/dev/null 2>&1; then
24
+ exec timeout --preserve-status --signal=TERM "${SECS}s" "$@"
25
+ else
26
+ # Perl fork+alarm fallback (always available on macOS+Linux).
27
+ # Must fork — exec replaces the perl process and discards the SIGALRM handler,
28
+ # so the alarm would never reach our exit-124 code path.
29
+ exec perl -e '
30
+ my $secs = shift;
31
+ my $pid = fork();
32
+ die "with-timeout.sh: fork failed: $!\n" unless defined $pid;
33
+ if ($pid == 0) {
34
+ exec { $ARGV[0] } @ARGV
35
+ or do { print STDERR "with-timeout.sh: exec failed: $!\n"; exit 127 };
36
+ }
37
+ $SIG{ALRM} = sub {
38
+ kill "TERM", $pid;
39
+ select(undef, undef, undef, 0.5);
40
+ kill "KILL", $pid if kill(0, $pid);
41
+ waitpid $pid, 0;
42
+ exit 124;
43
+ };
44
+ alarm $secs;
45
+ waitpid $pid, 0;
46
+ my $rc = $?;
47
+ if ($rc & 127) { exit 128 + ($rc & 127); }
48
+ exit($rc >> 8);
49
+ ' "$SECS" "$@"
50
+ fi
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-merlin-brain",
3
- "version": "5.3.2",
3
+ "version": "5.3.3",
4
4
  "description": "Merlin - The Ultimate AI Brain for Claude Code, Codex, and other AI CLIs. One install: workflows, agents, loop, and Sights MCP server.",
5
5
  "type": "module",
6
6
  "main": "./dist/server/index.js",