@synkro-sh/cli 1.4.30 → 1.4.32

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/bootstrap.js CHANGED
@@ -3047,23 +3047,69 @@ var init_install = __esm({
3047
3047
  set -uo pipefail
3048
3048
 
3049
3049
  SESSION=${TMUX_SESSION_NAME}
3050
+ LOG="$HOME/.synkro/cc_sessions/run-claude.log"
3051
+
3052
+ log() { echo "[$(date '+%H:%M:%S')] $*" >> "$LOG"; echo "$*"; }
3053
+
3054
+ # Pre-flight checks
3055
+ if ! command -v claude >/dev/null 2>&1; then
3056
+ log "ERROR: claude CLI not found on PATH. Install Claude Code first."
3057
+ exit 1
3058
+ fi
3059
+
3060
+ if ! command -v tmux >/dev/null 2>&1; then
3061
+ log "ERROR: tmux not found on PATH."
3062
+ exit 1
3063
+ fi
3064
+
3065
+ # Check claude is authenticated
3066
+ if ! claude --version >/dev/null 2>&1; then
3067
+ log "ERROR: claude --version failed. Is Claude Code installed correctly?"
3068
+ exit 1
3069
+ fi
3070
+
3071
+ log "Starting local-CC session..."
3072
+ log "claude version: $(claude --version 2>&1 | head -1)"
3050
3073
 
3051
3074
  # Kill any previous session so restarts come up clean.
3052
3075
  tmux kill-session -t "$SESSION" 2>/dev/null || true
3053
3076
 
3054
3077
  # Start claude inside a detached tmux session so it has a real pty.
3055
- #
3056
- # --setting-sources project,local: skip ~/.claude/settings.json so the
3057
- # synkro PreToolUse/PostToolUse hooks installed there don't load. Without
3058
- # this, the grader's own tool calls would re-trigger synkro grading,
3059
- # causing recursion / deadlock with the same channel session.
3078
+ # Redirect stderr to the log so we can see why it dies.
3060
3079
  tmux new-session -d -s "$SESSION" \\
3061
- "claude --dangerously-load-development-channels server:synkro-local --dangerously-skip-permissions --setting-sources project,local --model claude-sonnet-4-6"
3080
+ "claude --dangerously-load-development-channels server:synkro-local --dangerously-skip-permissions --setting-sources project,local --model claude-sonnet-4-6 2>>$LOG; echo 'claude exited with code '$'?' >> $LOG"
3081
+
3082
+ # Claude's --dangerously-skip-permissions shows a one-time confirmation
3083
+ # prompt (accept option = '2'). Because --setting-sources skips user
3084
+ # settings, the acceptance is never persisted, so the prompt reappears
3085
+ # every launch. Auto-accept it by sending the right key sequence.
3086
+ sleep 3
3087
+ if tmux has-session -t "$SESSION" 2>/dev/null; then
3088
+ # Send '2' to accept bypass-permissions, then Enter for any follow-up prompts
3089
+ tmux send-keys -t "$SESSION" '2' 2>/dev/null || true
3090
+ sleep 1
3091
+ tmux send-keys -t "$SESSION" Enter 2>/dev/null || true
3092
+ sleep 1
3093
+ # Additional Enter for workspace trust / MCP consent prompts
3094
+ tmux send-keys -t "$SESSION" Enter 2>/dev/null || true
3095
+ log "Sent auto-accept keys to claude session."
3096
+ fi
3097
+
3098
+ sleep 2
3099
+ if ! tmux has-session -t "$SESSION" 2>/dev/null; then
3100
+ log "ERROR: tmux session died immediately. Check $LOG for details."
3101
+ log "Try running claude manually to verify auth: claude --print 'say ok'"
3102
+ exit 1
3103
+ fi
3104
+
3105
+ log "tmux session started successfully."
3062
3106
 
3063
3107
  # Block on the tmux session so pueue's task lifetime tracks claude's.
3064
3108
  while tmux has-session -t "$SESSION" 2>/dev/null; do
3065
3109
  sleep 5
3066
3110
  done
3111
+
3112
+ log "tmux session ended."
3067
3113
  `;
3068
3114
  MCP_SERVER_NAME = "synkro-local";
3069
3115
  PLUGIN_PACKAGE_JSON = JSON.stringify(
@@ -3202,14 +3248,15 @@ function probePort(host, port, timeoutMs = 500) {
3202
3248
  sock.setTimeout(timeoutMs, () => done(false));
3203
3249
  });
3204
3250
  }
3205
- function tmuxKickEnter() {
3251
+ function tmuxDismissPrompts() {
3252
+ spawnSync2("tmux", ["send-keys", "-t", TMUX_SESSION, "2"], { encoding: "utf-8" });
3206
3253
  spawnSync2("tmux", ["send-keys", "-t", TMUX_SESSION, "Enter"], { encoding: "utf-8" });
3207
3254
  }
3208
3255
  async function waitForChannelReady(port, timeoutMs = 6e4, host = "127.0.0.1") {
3209
3256
  const deadline = Date.now() + timeoutMs;
3210
3257
  while (Date.now() < deadline) {
3211
3258
  if (await probePort(host, port)) return true;
3212
- tmuxKickEnter();
3259
+ tmuxDismissPrompts();
3213
3260
  await new Promise((r) => setTimeout(r, 1e3));
3214
3261
  }
3215
3262
  return probePort(host, port);
@@ -3686,7 +3733,7 @@ function writeConfigEnv(opts) {
3686
3733
  `SYNKRO_CREDENTIALS_PATH=${shellQuoteSingle(credsPath)}`,
3687
3734
  `SYNKRO_TIER=${shellQuoteSingle(safeTier)}`,
3688
3735
  `SYNKRO_INFERENCE=${shellQuoteSingle(safeInference)}`,
3689
- `SYNKRO_VERSION=${shellQuoteSingle("1.4.30")}`
3736
+ `SYNKRO_VERSION=${shellQuoteSingle("1.4.32")}`
3690
3737
  ];
3691
3738
  if (safeSynkroBin) lines.push(`SYNKRO_CLI_BIN=${shellQuoteSingle(safeSynkroBin)}`);
3692
3739
  if (safeUserId) lines.push(`SYNKRO_USER_ID=${shellQuoteSingle(safeUserId)}`);
@@ -4089,7 +4136,7 @@ async function installCommand(opts = {}) {
4089
4136
  console.log(`Installed local-CC channel plugin at ${r.pluginPath}`);
4090
4137
  const t = ensureRunning();
4091
4138
  console.log(`Local-CC pueue task: id=${t.id} status=${t.status}`);
4092
- console.log("Waiting for channel...");
4139
+ console.log("Waiting for channel (up to 60s)...");
4093
4140
  const ready = await waitForChannelReady(CHANNEL_PORT, 6e4, CHANNEL_HOST);
4094
4141
  if (ready) {
4095
4142
  console.log(` channel ready at ${CHANNEL_HOST}:${CHANNEL_PORT}`);
@@ -4101,7 +4148,35 @@ async function installCommand(opts = {}) {
4101
4148
  console.log(" warmup skipped (non-fatal)\n");
4102
4149
  }
4103
4150
  } else {
4104
- console.warn(` \u26A0 channel did not come up within 60s \u2014 check \`synkro local-cc logs\`
4151
+ console.warn(` \u26A0 Channel did not come up within 60s.`);
4152
+ try {
4153
+ const { spawnSync: sp } = await import("child_process");
4154
+ const tmuxCheck = sp("tmux", ["has-session", "-t", "synkro-local-cc"], { encoding: "utf-8" });
4155
+ console.warn(` tmux session: ${tmuxCheck.status === 0 ? "running" : "not running"}`);
4156
+ const pueueTask = findTask();
4157
+ console.warn(` pueue task: ${pueueTask ? `id=${pueueTask.id} status=${pueueTask.status}` : "not found"}`);
4158
+ const bunCheck = sp("bun", ["--version"], { encoding: "utf-8" });
4159
+ console.warn(` bun: ${bunCheck.status === 0 ? bunCheck.stdout.trim() : "not found"}`);
4160
+ const claudeCheck = sp("claude", ["--version"], { encoding: "utf-8" });
4161
+ console.warn(` claude: ${claudeCheck.status === 0 ? claudeCheck.stdout.trim().split("\n")[0] : "not found"}`);
4162
+ if (pueueTask) {
4163
+ const logs = tailLogs(15);
4164
+ if (logs && logs !== "(no output)") {
4165
+ console.warn(` pueue logs (last 15 lines):`);
4166
+ for (const line of logs.split("\n").slice(0, 15)) {
4167
+ console.warn(` ${line}`);
4168
+ }
4169
+ }
4170
+ }
4171
+ const logPath = join11(homedir10(), ".synkro", "cc_sessions", "run-claude.log");
4172
+ if (existsSync11(logPath)) {
4173
+ const logContent = readFileSync10(logPath, "utf-8").trim().split("\n").slice(-10);
4174
+ console.warn(` run-claude.log:`);
4175
+ for (const line of logContent) console.warn(` ${line}`);
4176
+ }
4177
+ } catch {
4178
+ }
4179
+ console.warn(` Run \`synkro local-cc status\` and \`synkro local-cc logs --tmux\` to debug.
4105
4180
  `);
4106
4181
  }
4107
4182
  } catch (err) {