orchestrix-yuri 2.3.3 → 2.4.0

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.
@@ -20,7 +20,7 @@ const DEFAULTS = {
20
20
  engine: {
21
21
  skill: 'yuri',
22
22
  tmux_session: 'yuri-gateway',
23
- startup_timeout: 30000, // ms to wait for Claude Code to initialize
23
+ startup_timeout: 60000, // ms to wait for Claude Code to initialize
24
24
  poll_interval: 2000, // ms between capture-pane polls
25
25
  stable_count: 3, // consecutive stable polls before declaring done
26
26
  max_retries: 3, // session restart retries before error
@@ -143,24 +143,28 @@ function tmuxSafe(cmd) {
143
143
 
144
144
  // ── Claude Code TUI Indicators ─────────────────────────────────────────────────
145
145
  //
146
- // Claude Code uses three circle symbols as primary state indicators:
146
+ // Claude Code TUI state indicators vary by version and statusline config:
147
147
  //
148
- // (U+25CB) IDLE — Claude is waiting for user input
149
- // (U+25CF) PROCESSING Claude is actively generating a response
150
- // ◐ (U+25D0) APPROVAL Claude is waiting for permission approval
148
+ // Idle indicators (any of these = ready for input):
149
+ // (U+25CB) — circle idle indicator (shown with certain statusline configs)
150
+ //prompt cursor (always shown when idle, most reliable)
151
151
  //
152
- // During processing, a Braille spinner animates: ⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏
153
- // with cycling verbs like "Baking...", "Computing...", "Thinking..."
152
+ // Processing indicators:
153
+ // ● (U+25CF) filled circle, active generation
154
+ // Braille spinner: ⠋ ⠙ ⠹ ⠸ ⠼ ⠴ ⠦ ⠧ ⠇ ⠏
154
155
  //
155
- // Completion message format (past-tense verb + duration):
156
- // "Baked for 31s", "Worked for 2m 45s", "Cooked for 1m 6s"
157
- // Pattern: /[A-Z][a-z]*ed for \d+/
156
+ // Status line elements (NOT state indicators):
157
+ // ◐ — effort level indicator (e.g. " medium · /effort"), NOT approval prompt
158
+ //
159
+ // Completion message (past-tense verb + duration):
160
+ // "Baked for 31s", "Worked for 2m 45s"
161
+ // Pattern: /[A-Z][a-z]*ed for \d+/
158
162
  //
159
163
  // ────────────────────────────────────────────────────────────────────────────────
160
164
 
161
165
  const BRAILLE_SPINNER = /[⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]/;
162
166
  const COMPLETION_RE = /[A-Z][a-z]*ed for \d+/;
163
- const IDLE_RE = /○/;
167
+ const IDLE_RE = /[○❯]/;
164
168
  const PROCESSING_RE = /●/;
165
169
 
166
170
  /**
@@ -202,10 +206,6 @@ function paneTail(name, n) {
202
206
  /**
203
207
  * Detect Claude Code's current state from pane output.
204
208
  *
205
- * Note: We launch with --dangerously-skip-permissions, so approval prompts (◐)
206
- * should never appear. The detection is kept for robustness but no auto-approve
207
- * action is taken — sending blind 'y' keystrokes is dangerous.
208
- *
209
209
  * @returns {'idle'|'processing'|'complete'|'unknown'}
210
210
  */
211
211
  function detectState(name) {
@@ -325,32 +325,43 @@ async function createSession(engineConfig) {
325
325
 
326
326
  const binary = getClaudeBinary();
327
327
  const projectRoot = resolveProjectRoot() || os.homedir();
328
+ log.tmux(`Binary: ${binary}`);
329
+ log.tmux(`Project root: ${projectRoot}`);
328
330
 
329
331
  // Ensure CLAUDE.md has channel mode instructions (survives compact)
330
332
  ensureClaudeMd(projectRoot);
331
333
 
332
- // Kill existing stale session
334
+ // Only kill existing session if it exists — don't kill on retry
335
+ // so the user can `tmux attach -t yuri-gateway` to debug
333
336
  if (hasSession(sessionName)) {
337
+ log.tmux(`Killing existing session "${sessionName}"`);
334
338
  tmuxSafe(`kill-session -t ${sessionName}`);
335
339
  }
336
340
 
337
341
  // Create session with generous scrollback
338
342
  tmux(`new-session -d -s ${sessionName} -n claude -c "${projectRoot}"`);
339
343
  tmux(`set-option -t ${sessionName} history-limit ${HISTORY_LIMIT}`);
344
+ log.tmux(`Session "${sessionName}" created, launching Claude Code...`);
340
345
 
341
346
  // Set auto-compact threshold to 80% (default is 95%)
342
- // This gives comfortable buffer before context pressure
343
347
  const compactPct = engineConfig.autocompact_pct || 80;
344
348
  tmux(`send-keys -t ${sessionName}:0 'export CLAUDE_AUTOCOMPACT_PCT_OVERRIDE=${compactPct}' Enter`);
345
349
 
346
350
  // Launch Claude Code in interactive mode
347
351
  tmux(`send-keys -t ${sessionName}:0 '"${binary}" --dangerously-skip-permissions' Enter`);
348
352
 
349
- // Wait for Claude Code to initialize (detect idle indicator)
350
- const startupTimeout = engineConfig.startup_timeout || 30000;
353
+ // Wait for Claude Code to initialize (detect idle indicator)
354
+ // Default 60s — Claude Code needs time to load CLAUDE.md, connect MCP servers, etc.
355
+ const startupTimeout = engineConfig.startup_timeout || 60000;
356
+ log.tmux(`Waiting for Claude Code to become idle (timeout: ${startupTimeout / 1000}s)...`);
351
357
  const started = await waitForIdle(sessionName, startupTimeout);
352
358
  if (!started) {
353
- throw new Error(`Claude Code did not become idle within ${startupTimeout}ms`);
359
+ // Don't kill session on failure let user debug with tmux attach
360
+ const tail = paneTail(sessionName, 10);
361
+ log.error(`Claude Code did not become idle within ${startupTimeout / 1000}s`);
362
+ log.error(`Last pane output:\n${tail}`);
363
+ log.info(`Debug: tmux attach -t ${sessionName}`);
364
+ throw new Error(`Claude Code did not become idle within ${startupTimeout / 1000}s`);
354
365
  }
355
366
 
356
367
  // Send L1 context as the initial system message.
@@ -358,6 +369,7 @@ async function createSession(engineConfig) {
358
369
  // so we only inject L1 global memory here to prime the session.
359
370
  const l1 = loadL1Context();
360
371
  if (l1) {
372
+ log.tmux('Injecting L1 context...');
361
373
  await injectMessage(sessionName, l1);
362
374
  await waitForIdle(sessionName, 120000); // allow up to 2min for L1 processing
363
375
  }
@@ -545,8 +557,14 @@ async function ensureSession(engineConfig) {
545
557
  return;
546
558
  } catch (err) {
547
559
  log.warn(`Session init attempt ${attempt}/${maxRetries} failed: ${err.message}`);
548
- if (attempt === maxRetries) throw err;
549
- // Brief pause before retry
560
+ if (attempt === maxRetries) {
561
+ log.error('All init attempts failed. Check Claude Code installation and tmux.');
562
+ log.info(`Debug: tmux attach -t ${engineConfig.tmux_session || DEFAULT_SESSION}`);
563
+ throw err;
564
+ }
565
+ // Kill session before retry so createSession starts fresh
566
+ const sn = engineConfig.tmux_session || DEFAULT_SESSION;
567
+ if (hasSession(sn)) tmuxSafe(`kill-session -t ${sn}`);
550
568
  await new Promise((r) => setTimeout(r, 3000));
551
569
  }
552
570
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orchestrix-yuri",
3
- "version": "2.3.3",
3
+ "version": "2.4.0",
4
4
  "description": "Yuri — Meta-Orchestrator for Orchestrix. Drive your entire project lifecycle with natural language.",
5
5
  "main": "lib/installer.js",
6
6
  "bin": {