commandmate 0.1.11 → 0.1.12

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.
Files changed (69) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +9 -9
  3. package/.next/app-path-routes-manifest.json +1 -1
  4. package/.next/build-manifest.json +2 -2
  5. package/.next/cache/.tsbuildinfo +1 -1
  6. package/.next/cache/config.json +3 -3
  7. package/.next/cache/webpack/client-production/0.pack +0 -0
  8. package/.next/cache/webpack/client-production/1.pack +0 -0
  9. package/.next/cache/webpack/client-production/2.pack +0 -0
  10. package/.next/cache/webpack/client-production/index.pack +0 -0
  11. package/.next/cache/webpack/client-production/index.pack.old +0 -0
  12. package/.next/cache/webpack/edge-server-production/0.pack +0 -0
  13. package/.next/cache/webpack/edge-server-production/index.pack +0 -0
  14. package/.next/cache/webpack/server-production/0.pack +0 -0
  15. package/.next/cache/webpack/server-production/index.pack +0 -0
  16. package/.next/next-server.js.nft.json +1 -1
  17. package/.next/prerender-manifest.json +1 -1
  18. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  19. package/.next/server/app/_not-found.html +1 -1
  20. package/.next/server/app/_not-found.rsc +1 -1
  21. package/.next/server/app/api/hooks/claude-done/route.js +6 -6
  22. package/.next/server/app/api/hooks/claude-done/route.js.nft.json +1 -1
  23. package/.next/server/app/api/repositories/route.js +1 -1
  24. package/.next/server/app/api/repositories/route.js.nft.json +1 -1
  25. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
  26. package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
  27. package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
  28. package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
  29. package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
  30. package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
  31. package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
  32. package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
  33. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
  34. package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
  35. package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
  36. package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
  37. package/.next/server/app/api/worktrees/[id]/route.js +1 -1
  38. package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
  39. package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
  40. package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
  41. package/.next/server/app/api/worktrees/[id]/start-polling/route.js +1 -1
  42. package/.next/server/app/api/worktrees/[id]/start-polling/route.js.nft.json +1 -1
  43. package/.next/server/app/api/worktrees/route.js +1 -1
  44. package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
  45. package/.next/server/app/index.html +1 -1
  46. package/.next/server/app/index.rsc +1 -1
  47. package/.next/server/app/page_client-reference-manifest.js +1 -1
  48. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  49. package/.next/server/app/worktrees/[id]/page.js +4 -4
  50. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  51. package/.next/server/app/worktrees/[id]/simple-terminal/page_client-reference-manifest.js +1 -1
  52. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  53. package/.next/server/app-paths-manifest.json +8 -8
  54. package/.next/server/chunks/2597.js +1 -0
  55. package/.next/server/chunks/2648.js +1 -0
  56. package/.next/server/middleware-manifest.json +5 -5
  57. package/.next/server/pages/404.html +1 -1
  58. package/.next/server/pages/500.html +1 -1
  59. package/.next/server/server-reference-manifest.json +1 -1
  60. package/.next/static/chunks/app/worktrees/[id]/page-58fcf2e63c056743.js +1 -0
  61. package/.next/trace +5 -5
  62. package/dist/server/src/lib/auto-yes-manager.js +6 -7
  63. package/dist/server/src/lib/claude-session.js +134 -28
  64. package/package.json +1 -1
  65. package/.next/server/chunks/1528.js +0 -1
  66. package/.next/server/chunks/7213.js +0 -1
  67. package/.next/static/chunks/app/worktrees/[id]/page-720605c2fb074444.js +0 -1
  68. /package/.next/static/{gRNW5YXY43KqCKbCdaJoJ → 564GHwluX5xIv9qpqLJV2}/_buildManifest.js +0 -0
  69. /package/.next/static/{gRNW5YXY43KqCKbCdaJoJ → 564GHwluX5xIv9qpqLJV2}/_ssgManifest.js +0 -0
@@ -43,13 +43,12 @@ exports.MAX_CONCURRENT_POLLERS = 50;
43
43
  const AUTO_YES_TIMEOUT_MS = 3600000;
44
44
  /** Worktree ID validation pattern (security: prevent command injection) */
45
45
  const WORKTREE_ID_PATTERN = /^[a-zA-Z0-9_-]+$/;
46
- // =============================================================================
47
- // In-memory State
48
- // =============================================================================
49
- /** In-memory storage for auto-yes states */
50
- const autoYesStates = new Map();
51
- /** In-memory storage for poller states (Issue #138) */
52
- const autoYesPollerStates = new Map();
46
+ /** In-memory storage for auto-yes states (globalThis for hot reload persistence) */
47
+ const autoYesStates = globalThis.__autoYesStates ??
48
+ (globalThis.__autoYesStates = new Map());
49
+ /** In-memory storage for poller states (globalThis for hot reload persistence) */
50
+ const autoYesPollerStates = globalThis.__autoYesPollerStates ??
51
+ (globalThis.__autoYesPollerStates = new Map());
53
52
  // =============================================================================
54
53
  // Utility Functions
55
54
  // =============================================================================
@@ -4,19 +4,84 @@
4
4
  * Manages Claude CLI sessions within tmux for each worktree
5
5
  */
6
6
  Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.CLAUDE_PROMPT_POLL_INTERVAL = exports.CLAUDE_PROMPT_WAIT_TIMEOUT = exports.CLAUDE_POST_PROMPT_DELAY = exports.CLAUDE_INIT_POLL_INTERVAL = exports.CLAUDE_INIT_TIMEOUT = void 0;
7
8
  exports.getSessionName = getSessionName;
8
9
  exports.isClaudeInstalled = isClaudeInstalled;
9
10
  exports.isClaudeRunning = isClaudeRunning;
10
11
  exports.getClaudeSessionState = getClaudeSessionState;
12
+ exports.waitForPrompt = waitForPrompt;
11
13
  exports.startClaudeSession = startClaudeSession;
12
14
  exports.sendMessageToClaude = sendMessageToClaude;
13
15
  exports.captureClaudeOutput = captureClaudeOutput;
14
16
  exports.stopClaudeSession = stopClaudeSession;
15
17
  exports.restartClaudeSession = restartClaudeSession;
16
18
  const tmux_1 = require("./tmux");
19
+ const cli_patterns_1 = require("./cli-patterns");
17
20
  const child_process_1 = require("child_process");
18
21
  const util_1 = require("util");
19
22
  const execAsync = (0, util_1.promisify)(child_process_1.exec);
23
+ // ----- Helper Functions -----
24
+ /**
25
+ * Extract error message from unknown error type
26
+ * Provides consistent error message extraction across the module (DRY)
27
+ *
28
+ * @param error - Unknown error object
29
+ * @returns Error message string
30
+ */
31
+ function getErrorMessage(error) {
32
+ return error instanceof Error ? error.message : String(error);
33
+ }
34
+ // ----- Timeout and Polling Constants (OCP-001) -----
35
+ // These constants are exported to allow configuration and testing.
36
+ // Changing these values affects Claude CLI session startup behavior.
37
+ /**
38
+ * Claude CLI initialization max wait time (milliseconds)
39
+ *
40
+ * This timeout allows sufficient time for Claude CLI to:
41
+ * - Load and initialize its internal state
42
+ * - Authenticate with Anthropic servers (if needed)
43
+ * - Display the interactive prompt
44
+ *
45
+ * 15 seconds provides headroom for slower networks or cold starts.
46
+ */
47
+ exports.CLAUDE_INIT_TIMEOUT = 15000;
48
+ /**
49
+ * Initialization polling interval (milliseconds)
50
+ *
51
+ * How frequently we check if Claude CLI has finished initializing.
52
+ * 300ms balances responsiveness with avoiding excessive polling overhead.
53
+ */
54
+ exports.CLAUDE_INIT_POLL_INTERVAL = 300;
55
+ /**
56
+ * Stability delay after prompt detection (milliseconds)
57
+ *
58
+ * This delay is necessary because Claude CLI renders its UI progressively:
59
+ * 1. The prompt character (> or U+276F) appears first
60
+ * 2. Additional UI elements (tips, suggestions) may render afterward
61
+ * 3. Sending input too quickly can interrupt this rendering process
62
+ *
63
+ * The 500ms value was empirically determined to provide sufficient buffer
64
+ * for Claude CLI to complete its initialization rendering while maintaining
65
+ * responsive user experience. (DOC-001)
66
+ *
67
+ * @see Issue #152 - First message not being sent after session start
68
+ */
69
+ exports.CLAUDE_POST_PROMPT_DELAY = 500;
70
+ /**
71
+ * Prompt wait timeout before message send (milliseconds)
72
+ *
73
+ * When sending a message, we first verify Claude is at a prompt state.
74
+ * This timeout limits how long we wait for Claude to return to prompt
75
+ * if it's still processing a previous request.
76
+ */
77
+ exports.CLAUDE_PROMPT_WAIT_TIMEOUT = 5000;
78
+ /**
79
+ * Prompt wait polling interval (milliseconds)
80
+ *
81
+ * How frequently we check for prompt state before sending messages.
82
+ * 200ms provides quick response while minimizing CPU usage.
83
+ */
84
+ exports.CLAUDE_PROMPT_POLL_INTERVAL = 200;
20
85
  /**
21
86
  * Cached Claude CLI path
22
87
  */
@@ -122,6 +187,35 @@ async function getClaudeSessionState(worktreeId) {
122
187
  lastActivity: new Date(),
123
188
  };
124
189
  }
190
+ /**
191
+ * Wait for session to be at prompt state
192
+ * Polls for prompt detection using CLAUDE_PROMPT_PATTERN (DRY-001)
193
+ *
194
+ * @param sessionName - tmux session name
195
+ * @param timeout - Timeout in milliseconds (default: CLAUDE_PROMPT_WAIT_TIMEOUT)
196
+ * @throws {Error} If prompt is not detected within timeout
197
+ *
198
+ * @example
199
+ * ```typescript
200
+ * await waitForPrompt('mcbd-claude-feature-foo');
201
+ * // Session is now ready to receive input
202
+ * ```
203
+ */
204
+ async function waitForPrompt(sessionName, timeout = exports.CLAUDE_PROMPT_WAIT_TIMEOUT) {
205
+ const startTime = Date.now();
206
+ const pollInterval = exports.CLAUDE_PROMPT_POLL_INTERVAL;
207
+ while (Date.now() - startTime < timeout) {
208
+ // Use -50 lines to capture more context including status bars
209
+ const output = await (0, tmux_1.capturePane)(sessionName, { startLine: -50 });
210
+ // DRY-001: Use CLAUDE_PROMPT_PATTERN from cli-patterns.ts
211
+ // Strip ANSI escape sequences before pattern matching (Issue #152)
212
+ if (cli_patterns_1.CLAUDE_PROMPT_PATTERN.test((0, cli_patterns_1.stripAnsi)(output))) {
213
+ return; // Prompt detected
214
+ }
215
+ await new Promise((resolve) => setTimeout(resolve, pollInterval));
216
+ }
217
+ throw new Error(`Prompt detection timeout (${timeout}ms)`);
218
+ }
125
219
  /**
126
220
  * Start a Claude CLI session in tmux
127
221
  *
@@ -162,18 +256,25 @@ async function startClaudeSession(options) {
162
256
  const claudePath = await getClaudePath();
163
257
  // Start Claude CLI in interactive mode using dynamically resolved path
164
258
  await (0, tmux_1.sendKeys)(sessionName, claudePath, true);
165
- // Wait for Claude to initialize with dynamic detection
166
- // Check for Claude prompt instead of fixed delay
167
- const maxWaitTime = 10000; // 10 seconds max
168
- const pollInterval = 500; // Check every 500ms
259
+ // Wait for Claude to initialize with dynamic detection (OCP-001)
260
+ // Use constants instead of hardcoded values
261
+ const maxWaitTime = exports.CLAUDE_INIT_TIMEOUT;
262
+ const pollInterval = exports.CLAUDE_INIT_POLL_INTERVAL;
169
263
  const startTime = Date.now();
264
+ let initialized = false;
170
265
  while (Date.now() - startTime < maxWaitTime) {
171
266
  await new Promise((resolve) => setTimeout(resolve, pollInterval));
172
267
  try {
173
268
  const output = await (0, tmux_1.capturePane)(sessionName, { startLine: -50 });
174
- // Claude is ready when we see the prompt (> ) or separator line
175
- if (/^>\s*$/m.test(output) || /^─{10,}$/m.test(output)) {
176
- console.log(`✓ Claude initialized in ${Date.now() - startTime}ms`);
269
+ // Claude is ready when we see the prompt or separator line (DRY-001, DRY-002)
270
+ // Use patterns from cli-patterns.ts for consistency
271
+ // Strip ANSI escape sequences before pattern matching (Issue #152)
272
+ const cleanOutput = (0, cli_patterns_1.stripAnsi)(output);
273
+ if (cli_patterns_1.CLAUDE_PROMPT_PATTERN.test(cleanOutput) || cli_patterns_1.CLAUDE_SEPARATOR_PATTERN.test(cleanOutput)) {
274
+ // Wait for stability after prompt detection (CONS-007, DOC-001)
275
+ await new Promise((resolve) => setTimeout(resolve, exports.CLAUDE_POST_PROMPT_DELAY));
276
+ console.log(`Claude initialized in ${Date.now() - startTime}ms`);
277
+ initialized = true;
177
278
  break;
178
279
  }
179
280
  }
@@ -181,11 +282,14 @@ async function startClaudeSession(options) {
181
282
  // Ignore capture errors during initialization
182
283
  }
183
284
  }
184
- console.log(`✓ Started Claude session: ${sessionName}`);
285
+ // Throw error on timeout instead of silently continuing (CONS-005, IMP-001)
286
+ if (!initialized) {
287
+ throw new Error(`Claude initialization timeout (${exports.CLAUDE_INIT_TIMEOUT}ms)`);
288
+ }
289
+ console.log(`Started Claude session: ${sessionName}`);
185
290
  }
186
291
  catch (error) {
187
- const errorMessage = error instanceof Error ? error.message : String(error);
188
- throw new Error(`Failed to start Claude session: ${errorMessage}`);
292
+ throw new Error(`Failed to start Claude session: ${getErrorMessage(error)}`);
189
293
  }
190
294
  }
191
295
  /**
@@ -207,21 +311,25 @@ async function sendMessageToClaude(worktreeId, message) {
207
311
  if (!exists) {
208
312
  throw new Error(`Claude session ${sessionName} does not exist. Start the session first.`);
209
313
  }
210
- try {
211
- // Send message to Claude (without Enter)
212
- await (0, tmux_1.sendKeys)(sessionName, message, false);
213
- // Wait a moment for the text to be typed
214
- await new Promise((resolve) => setTimeout(resolve, 100));
215
- // Send Enter key to submit (single Enter submits in Claude Code CLI)
216
- await execAsync(`tmux send-keys -t "${sessionName}" C-m`);
217
- // Wait a moment for the message to be processed
218
- await new Promise((resolve) => setTimeout(resolve, 200));
219
- console.log(`✓ Sent message to Claude session: ${sessionName}`);
220
- }
221
- catch (error) {
222
- const errorMessage = error instanceof Error ? error.message : String(error);
223
- throw new Error(`Failed to send message to Claude: ${errorMessage}`);
314
+ // Verify prompt state before sending (CONS-006, DRY-001)
315
+ // Use -50 lines to ensure we capture the prompt even with status bars
316
+ const output = await (0, tmux_1.capturePane)(sessionName, { startLine: -50 });
317
+ // Strip ANSI escape sequences before pattern matching (Issue #152)
318
+ if (!cli_patterns_1.CLAUDE_PROMPT_PATTERN.test((0, cli_patterns_1.stripAnsi)(output))) {
319
+ // Wait for prompt if not at prompt state
320
+ // Use longer timeout (10s) to handle slow responses
321
+ try {
322
+ await waitForPrompt(sessionName, 10000);
323
+ }
324
+ catch {
325
+ // Log warning but don't block - Claude might be in a special state
326
+ console.warn(`[sendMessageToClaude] Prompt not detected, sending anyway`);
327
+ }
224
328
  }
329
+ // Send message using sendKeys consistently (CONS-001)
330
+ await (0, tmux_1.sendKeys)(sessionName, message, false); // Message without Enter
331
+ await (0, tmux_1.sendKeys)(sessionName, '', true); // Enter key
332
+ console.log(`Sent message to Claude session: ${sessionName}`);
225
333
  }
226
334
  /**
227
335
  * Capture Claude session output
@@ -247,8 +355,7 @@ async function captureClaudeOutput(worktreeId, lines = 1000) {
247
355
  return await (0, tmux_1.capturePane)(sessionName, { startLine: -lines });
248
356
  }
249
357
  catch (error) {
250
- const errorMessage = error instanceof Error ? error.message : String(error);
251
- throw new Error(`Failed to capture Claude output: ${errorMessage}`);
358
+ throw new Error(`Failed to capture Claude output: ${getErrorMessage(error)}`);
252
359
  }
253
360
  }
254
361
  /**
@@ -282,8 +389,7 @@ async function stopClaudeSession(worktreeId) {
282
389
  return killed;
283
390
  }
284
391
  catch (error) {
285
- const errorMessage = error instanceof Error ? error.message : String(error);
286
- console.error(`Error stopping Claude session: ${errorMessage}`);
392
+ console.error(`Error stopping Claude session: ${getErrorMessage(error)}`);
287
393
  return false;
288
394
  }
289
395
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commandmate",
3
- "version": "0.1.11",
3
+ "version": "0.1.12",
4
4
  "description": "Git worktree management with Claude CLI and tmux sessions",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1 +0,0 @@
1
- "use strict";exports.id=1528,exports.ids=[1528],exports.modules={19377:(t,e,n)=>{n.d(e,{Wg:()=>c,bs:()=>function t(e){switch(e){case"claude":return{promptPattern:o,separatorPattern:a,thinkingPattern:s,skipPatterns:[/^─{10,}$/,/^[>❯]\s*$/,s,/^\s*[⎿⏋]\s+Tip:/,/^\s*Tip:/,/^\s*\?\s*for shortcuts/,/to interrupt\)/]};case"codex":return{promptPattern:p,separatorPattern:l,thinkingPattern:i,skipPatterns:[/^─.*─+$/,/^›\s*$/,/^›\s+(Implement|Find and fix|Type)/,i,/^\s*\d+%\s+context left/,/^\s*for shortcuts$/,/╭─+╮/,/╰─+╯/]};case"gemini":return{promptPattern:u,separatorPattern:/^gemini\s+--\s+/m,thinkingPattern:/(?!)/m,skipPatterns:[/^gemini\s+--\s+/,u,/^\s*$/]};default:return t("claude")}},vp:()=>g});let r=(0,n(43895).h)("cli-patterns"),s=RegExp(`[✻✽⏺\xb7∴✢✳✶⦿◉●○◌◎⊙⊚⠋⠙⠹⠸⠼⠴⠦⠧⠇⠏]\\s+.+…|to interrupt\\)`,"m"),i=/•\s*(Planning|Searching|Exploring|Running|Thinking|Working|Reading|Writing|Analyzing)/m,o=/^[>❯](\s*$|\s+\S)/m,a=/^─{10,}$/m,p=/^›\s+.+/m,l=/^─.*Worked for.*─+$/m,u=/^(%|\$|.*@.*[%$#])\s*$/m;function c(t,e){let n;let o=r.withContext({cliToolId:t});switch(o.debug("detectThinking:check",{contentLength:e.length}),t){case"claude":default:n=s.test(e);break;case"codex":n=i.test(e);break;case"gemini":n=!1}return o.debug("detectThinking:result",{isThinking:n}),n}let m=/\x1b\[[0-9;]*[a-zA-Z]|\x1b\][^\x07]*\x07|\[[0-9;]*m/g;function g(t){return t.replace(m,"")}},89194:(t,e,n)=>{n.d(e,{Lg:()=>o,NA:()=>a});var r=n(10927),s=n(97213);let i=(0,n(43895).h)("cli-session");async function o(t,e){let n=s.g.getInstance().getTool(e).getSessionName(t);return await (0,r.Hk)(n)}async function a(t,e,n=1e3){let o=i.withContext({worktreeId:t,cliToolId:e});o.debug("captureSessionOutput:start",{requestedLines:n});let a=s.g.getInstance().getTool(e),p=a.getSessionName(t);if(!await (0,r.Hk)(p))throw o.debug("captureSessionOutput:sessionNotFound",{sessionName:p}),Error(`${a.name} session ${p} does not exist`);try{let t=await (0,r.xq)(p,{startLine:-n}),e=t.split("\n").length;return o.debug("captureSessionOutput:success",{actualLines:e,lastFewLines:t.split("\n").slice(-3).join(" | ")}),t}catch(e){let t=e instanceof Error?e.message:String(e);throw o.error("captureSessionOutput:failed",{error:t}),Error(`Failed to capture ${a.name} output: ${t}`)}}},43895:(t,e,n)=>{n.d(e,{Y:()=>p,h:()=>l});var r=n(93346);let s=[{pattern:/Bearer\s+[A-Za-z0-9\-._~+/]+=*/gi,replacement:"Bearer [REDACTED]"},{pattern:/(password|passwd|pwd)[=:]\s*\S+/gi,replacement:"$1=[REDACTED]"},{pattern:/(token|secret|api_key|apikey|auth)[=:]\s*\S+/gi,replacement:"$1=[REDACTED]"},{pattern:/CM_AUTH_TOKEN=\S+/gi,replacement:"CM_AUTH_TOKEN=[REDACTED]"},{pattern:/MCBD_AUTH_TOKEN=\S+/gi,replacement:"MCBD_AUTH_TOKEN=[REDACTED]"},{pattern:/Authorization:\s*\S+/gi,replacement:"Authorization: [REDACTED]"},{pattern:/-----BEGIN\s+\w+\s+PRIVATE\s+KEY-----[\s\S]*?-----END\s+\w+\s+PRIVATE\s+KEY-----/g,replacement:"[SSH_KEY_REDACTED]"}],i=/password|secret|token|key|auth/i,o={debug:0,info:1,warn:2,error:3};function a(t,e,n,a,p){let l=(0,r.LI)().level;if(o[t]<o[l])return;let u=a?function t(e){if("string"==typeof e){let t=e;for(let{pattern:e,replacement:n}of s)t=t.replace(e,n);return t}if("object"==typeof e&&null!==e){if(Array.isArray(e))return e.map(t);let n={};for(let[r,s]of Object.entries(e))i.test(r)?n[r]="[REDACTED]":n[r]=t(s);return n}return e}(a):void 0,c=function(t){if("json"===(0,r.LI)().format)return JSON.stringify(t);let{timestamp:e,level:n,module:s,action:i,data:o,worktreeId:a,cliToolId:p,requestId:l}=t,u=[a,p].filter(Boolean),c=u.length>0?` [${u.join(":")}]`:"",m=l?` (${l.slice(0,8)})`:"",g=o?` ${JSON.stringify(o)}`:"";return`[${e}] [${n.toUpperCase()}] [${s}]${c}${m} ${i}${g}`}({level:t,module:e,action:n,timestamp:new Date().toISOString(),...p,...u&&{data:u}});switch(t){case"error":console.error(c);break;case"warn":console.warn(c);break;default:console.log(c)}}function p(){return"undefined"!=typeof crypto&&crypto.randomUUID?crypto.randomUUID():"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,t=>{let e=16*Math.random()|0;return("x"===t?e:3&e|8).toString(16)})}function l(t){let e=n=>({debug:(e,r)=>a("debug",t,e,r,n),info:(e,r)=>a("info",t,e,r,n),warn:(e,r)=>a("warn",t,e,r,n),error:(e,r)=>a("error",t,e,r,n),withContext:t=>e({...n,...t})});return e()}},63661:(t,e,n)=>{n.d(e,{F:()=>s,J:()=>o});let r=(0,n(43895).h)("prompt-detector");function s(t){r.debug("detectPrompt:start",{outputLength:t.length});let e=t.split("\n").slice(-10).join("\n"),n=function(t){let e=t.split("\n"),n=/^\s*([❯ ]\s*)?(\d+)\.\s*(.+)$/,r=[],s=-1,o=-1;for(let t=e.length-1;t>=0&&t>=e.length-50;t--){let i=e[t].trim(),a=e[t],p=i.match(n);if(p){let e=!!(p[1]&&p[1].includes("❯")),n=parseInt(p[2],10),s=p[3].trim();r.unshift({number:n,label:s,isDefault:e}),-1===o&&(o=t)}else if(r.length>0&&i&&!i.match(/^[-─]+$/)){let e=a.match(/^\s{2,}[^\d]/)&&!a.match(/^\s*\d+\./),n=i.length<5&&!i.endsWith("?");if(e||n)continue;s=t;break}}let a=r.some(t=>t.isDefault);if(r.length<2||!a)return{isPrompt:!1,cleanContent:t.trim()};let p="";if(s>=0){let t=[];for(let n=Math.max(0,s-5);n<=s;n++){let r=e[n].trim();r&&!r.match(/^[-─]+$/)&&t.push(r)}p=t.join(" ")}else p="Please select an option:";return{isPrompt:!0,promptData:{type:"multiple_choice",question:p.trim(),options:r.map(t=>{let e=i.some(e=>e.test(t.label));return{number:t.number,label:t.label,isDefault:t.isDefault,requiresTextInput:e}}),status:"pending"},cleanContent:p.trim()}}(t);if(n.isPrompt)return r.info("detectPrompt:multipleChoice",{isPrompt:!0,question:n.promptData?.question,optionsCount:n.promptData?.options?.length}),n;let s=e.match(/^(.+)\s+\(y\/n\)\s*$/m);if(s)return{isPrompt:!0,promptData:{type:"yes_no",question:s[1].trim(),options:["yes","no"],status:"pending"},cleanContent:s[1].trim()};let o=e.match(/^(.+)\s+\[y\/N\]\s*$/m);if(o)return{isPrompt:!0,promptData:{type:"yes_no",question:o[1].trim(),options:["yes","no"],status:"pending",defaultOption:"no"},cleanContent:o[1].trim()};let a=e.match(/^(.+)\s+\[Y\/n\]\s*$/m);if(a)return{isPrompt:!0,promptData:{type:"yes_no",question:a[1].trim(),options:["yes","no"],status:"pending",defaultOption:"yes"},cleanContent:a[1].trim()};let p=e.match(/^(.+)\s+\(yes\/no\)\s*$/m);if(p)return{isPrompt:!0,promptData:{type:"yes_no",question:p[1].trim(),options:["yes","no"],status:"pending"},cleanContent:p[1].trim()};let l=e.match(/^(.*?)Approve\?\s*$/m);if(l){let t=l[1].trim();return{isPrompt:!0,promptData:{type:"yes_no",question:t?`${t} Approve?`:"Approve?",options:["yes","no"],status:"pending"},cleanContent:t||"Approve?"}}return r.debug("detectPrompt:complete",{isPrompt:!1}),{isPrompt:!1,cleanContent:t.trim()}}let i=[/type\s+here/i,/tell\s+(me|claude)/i,/enter\s+/i,/custom/i,/differently/i];function o(t,e="yes_no"){let n=t.toLowerCase().trim();if("multiple_choice"===e){if(/^\d+$/.test(n))return n;throw Error(`Invalid answer for multiple choice: ${t}. Expected a number.`)}if("yes"===n||"y"===n)return"y";if("no"===n||"n"===n)return"n";throw Error(`Invalid answer: ${t}. Expected 'yes', 'no', 'y', or 'n'.`)}}};
@@ -1 +0,0 @@
1
- "use strict";exports.id=7213,exports.ids=[7213],exports.modules={62648:(t,e,s)=>{s.d(e,{Lm:()=>w,Uv:()=>c,YI:()=>m,_f:()=>h,xd:()=>d,ym:()=>u});var i=s(10927),o=s(61282);let n=(0,s(21764).promisify)(o.exec),a=null;async function r(){if(a)return a;if(process.env.CLAUDE_PATH)return a=process.env.CLAUDE_PATH;try{let{stdout:t}=await n("which claude",{timeout:5e3});return a=t.trim()}catch{for(let t of["/opt/homebrew/bin/claude","/usr/local/bin/claude","/usr/bin/claude"])try{return await n(`test -x "${t}"`,{timeout:1e3}),a=t}catch{}throw Error("Claude CLI not found. Set CLAUDE_PATH environment variable or install Claude CLI.")}}function l(t){return`mcbd-claude-${t}`}async function c(){try{return await n("which claude",{timeout:5e3}),!0}catch{return!1}}async function m(t){let e=l(t);return await (0,i.Hk)(e)}async function u(t){let{worktreeId:e,worktreePath:s}=t;if(!await c())throw Error("Claude CLI is not installed or not in PATH");let o=l(e);if(await (0,i.Hk)(o)){console.log(`Claude session ${o} already exists`);return}try{await (0,i.ed)({sessionName:o,workingDirectory:s,historyLimit:5e4});let t=await r();await (0,i.Is)(o,t,!0);let e=Date.now();for(;Date.now()-e<1e4;){await new Promise(t=>setTimeout(t,500));try{let t=await (0,i.xq)(o,{startLine:-50});if(/^>\s*$/m.test(t)||/^─{10,}$/m.test(t)){console.log(`✓ Claude initialized in ${Date.now()-e}ms`);break}}catch{}}console.log(`✓ Started Claude session: ${o}`)}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to start Claude session: ${t}`)}}async function d(t,e){let s=l(t);if(!await (0,i.Hk)(s))throw Error(`Claude session ${s} does not exist. Start the session first.`);try{await (0,i.Is)(s,e,!1),await new Promise(t=>setTimeout(t,100)),await n(`tmux send-keys -t "${s}" C-m`),await new Promise(t=>setTimeout(t,200)),console.log(`✓ Sent message to Claude session: ${s}`)}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to send message to Claude: ${t}`)}}async function w(t,e=1e3){let s=l(t);if(!await (0,i.Hk)(s))throw Error(`Claude session ${s} does not exist`);try{return await (0,i.xq)(s,{startLine:-e})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to capture Claude output: ${t}`)}}async function h(t){let e=l(t);try{await (0,i.Hk)(e)&&(await (0,i.Is)(e,"",!1),await n(`tmux send-keys -t "${e}" C-d`),await new Promise(t=>setTimeout(t,500)));let t=await (0,i.AJ)(e);return t&&console.log(`✓ Stopped Claude session: ${e}`),t}catch(e){let t=e instanceof Error?e.message:String(e);return console.error(`Error stopping Claude session: ${t}`),!1}}},97213:(t,e,s)=>{s.d(e,{g:()=>h});var i=s(61282),o=s(21764),n=s(10927);let a=(0,o.promisify)(i.exec);class r{async isInstalled(){try{return await a(`which ${this.command}`,{timeout:5e3}),!0}catch{return!1}}getSessionName(t){return`mcbd-${this.id}-${t}`}async interrupt(t){let e=this.getSessionName(t);await (0,n.ZV)(e,"Escape")}}var l=s(62648);class c extends r{async isInstalled(){return await (0,l.Uv)()}async isRunning(t){return await (0,l.YI)(t)}async startSession(t,e){await (0,l.ym)({worktreeId:t,worktreePath:e})}async sendMessage(t,e){await (0,l.xd)(t,e)}async killSession(t){await (0,l._f)(t)}constructor(...t){super(...t),this.id="claude",this.name="Claude Code",this.command="claude"}}let m=(0,o.promisify)(i.exec);class u extends r{async isRunning(t){let e=this.getSessionName(t);return await (0,n.Hk)(e)}async startSession(t,e){if(!await this.isInstalled())throw Error("Codex CLI is not installed or not in PATH");let s=this.getSessionName(t);if(await (0,n.Hk)(s)){console.log(`Codex session ${s} already exists`);return}try{await (0,n.ed)({sessionName:s,workingDirectory:e,historyLimit:5e4}),await new Promise(t=>setTimeout(t,100)),await (0,n.Is)(s,"codex",!0),await new Promise(t=>setTimeout(t,3e3)),await (0,n.Is)(s,"2",!0),await new Promise(t=>setTimeout(t,500)),console.log(`✓ Started Codex session: ${s}`)}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to start Codex session: ${t}`)}}async sendMessage(t,e){let s=this.getSessionName(t);if(!await (0,n.Hk)(s))throw Error(`Codex session ${s} does not exist. Start the session first.`);try{await (0,n.Is)(s,e,!1),await new Promise(t=>setTimeout(t,100)),await m(`tmux send-keys -t "${s}" C-m`),await new Promise(t=>setTimeout(t,200)),console.log(`✓ Sent message to Codex session: ${s}`)}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to send message to Codex: ${t}`)}}async killSession(t){let e=this.getSessionName(t);try{await (0,n.Hk)(e)&&(await m(`tmux send-keys -t "${e}" C-d`),await new Promise(t=>setTimeout(t,500))),await (0,n.AJ)(e)&&console.log(`✓ Stopped Codex session: ${e}`)}catch(e){let t=e instanceof Error?e.message:String(e);throw console.error(`Error stopping Codex session: ${t}`),e}}constructor(...t){super(...t),this.id="codex",this.name="Codex CLI",this.command="codex"}}let d=(0,o.promisify)(i.exec);class w extends r{async isRunning(t){let e=this.getSessionName(t);return await (0,n.Hk)(e)}async startSession(t,e){if(!await this.isInstalled())throw Error("Gemini CLI is not installed or not in PATH");let s=this.getSessionName(t);if(await (0,n.Hk)(s)){console.log(`Gemini session ${s} already exists`);return}try{await (0,n.ed)({sessionName:s,workingDirectory:e,historyLimit:5e4}),console.log(`✓ Started Gemini session: ${s}`)}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to start Gemini session: ${t}`)}}async sendMessage(t,e){let s=this.getSessionName(t);if(!await (0,n.Hk)(s))throw Error(`Gemini session ${s} does not exist. Start the session first.`);try{let t=e.replace(/'/g,"'\\''");await (0,n.Is)(s,`echo '${t}' | gemini`,!0),console.log(`✓ Sent message to Gemini session: ${s}`)}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to send message to Gemini: ${t}`)}}async killSession(t){let e=this.getSessionName(t);try{await (0,n.Hk)(e)&&(await d(`tmux send-keys -t "${e}" C-d`),await new Promise(t=>setTimeout(t,500))),await (0,n.AJ)(e)&&console.log(`✓ Stopped Gemini session: ${e}`)}catch(e){let t=e instanceof Error?e.message:String(e);throw console.error(`Error stopping Gemini session: ${t}`),e}}constructor(...t){super(...t),this.id="gemini",this.name="Gemini CLI",this.command="gemini"}}class h{constructor(){this.tools=new Map,this.tools.set("claude",new c),this.tools.set("codex",new u),this.tools.set("gemini",new w)}static getInstance(){return h.instance||(h.instance=new h),h.instance}getTool(t){let e=this.tools.get(t);if(!e)throw Error(`CLI tool '${t}' not found`);return e}getAllTools(){return Array.from(this.tools.values())}async getToolInfo(t){let e=this.getTool(t),s=await e.isInstalled();return{id:e.id,name:e.name,command:e.command,installed:s}}async getAllToolsInfo(){return Promise.all(this.getAllTools().map(async t=>{let e=await t.isInstalled();return{id:t.id,name:t.name,command:t.command,installed:e}}))}async getInstalledTools(){return(await this.getAllToolsInfo()).filter(t=>t.installed)}}},10927:(t,e,s)=>{s.d(e,{AJ:()=>c,Hk:()=>n,Is:()=>r,ZV:()=>m,ed:()=>a,xq:()=>l});var i=s(61282);let o=(0,s(21764).promisify)(i.exec);async function n(t){try{return await o(`tmux has-session -t "${t}"`,{timeout:5e3}),!0}catch{return!1}}async function a(t,e){let s,i,n;"string"==typeof t?(s=t,i=e,n=5e4):(s=t.sessionName,i=t.workingDirectory,n=t.historyLimit||5e4);try{await o(`tmux new-session -d -s "${s}" -c "${i}"`,{timeout:5e3}),await o(`tmux set-option -t "${s}" history-limit ${n}`,{timeout:5e3})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to create tmux session: ${t}`)}}async function r(t,e,s=!0){let i=e.replace(/'/g,"'\\''"),n=s?`tmux send-keys -t "${t}" '${i}' C-m`:`tmux send-keys -t "${t}" '${i}'`;try{await o(n,{timeout:5e3})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to send keys to tmux session: ${t}`)}}async function l(t,e){let s,i;"number"==typeof e?(s=-e,i="-"):e?(s=e.startLine??-1e4,i=e.endLine??"-"):(s=-1e3,i="-");try{let{stdout:e}=await o(`tmux capture-pane -t "${t}" -p -e -S ${s} -E ${i}`,{timeout:5e3,maxBuffer:10485760});return e}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to capture pane: ${t}`)}}async function c(t){try{return await o(`tmux kill-session -t "${t}"`,{timeout:5e3}),!0}catch(e){let t=e instanceof Error?e.message:String(e);if(t?.includes("no server running")||t?.includes("can't find session"))return!1;throw Error(`Failed to kill tmux session: ${t}`)}}async function m(t,e){try{await o(`tmux send-keys -t "${t}" ${e}`,{timeout:5e3})}catch(e){let t=e instanceof Error?e.message:String(e);throw Error(`Failed to send special key: ${t}`)}}}};