commandmate 0.2.12 → 0.2.13

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 (36) hide show
  1. package/.next/BUILD_ID +1 -1
  2. package/.next/app-build-manifest.json +20 -20
  3. package/.next/app-path-routes-manifest.json +1 -1
  4. package/.next/build-manifest.json +5 -5
  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/index.pack +0 -0
  13. package/.next/cache/webpack/server-production/0.pack +0 -0
  14. package/.next/cache/webpack/server-production/index.pack +0 -0
  15. package/.next/next-server.js.nft.json +1 -1
  16. package/.next/prerender-manifest.json +1 -1
  17. package/.next/required-server-files.json +1 -1
  18. package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  19. package/.next/server/app/api/app/update-check/route.js +1 -1
  20. package/.next/server/app/page_client-reference-manifest.js +1 -1
  21. package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
  22. package/.next/server/app/worktrees/[id]/page.js +2 -2
  23. package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
  24. package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
  25. package/.next/server/app-paths-manifest.json +12 -12
  26. package/.next/server/chunks/7536.js +1 -1
  27. package/.next/server/middleware-build-manifest.js +1 -1
  28. package/.next/server/pages/500.html +1 -1
  29. package/.next/server/server-reference-manifest.json +1 -1
  30. package/.next/static/chunks/app/worktrees/[id]/{page-c99258f57461962c.js → page-d9a7913679eccfd9.js} +1 -1
  31. package/.next/static/chunks/{webpack-af8567a485ade35a.js → webpack-e6531fcf859d9451.js} +1 -1
  32. package/.next/trace +5 -5
  33. package/dist/server/src/lib/response-poller.js +92 -39
  34. package/package.json +1 -1
  35. /package/.next/static/{ym6mA6Dl9wX62h3AoYO45 → oUEq-Bd47xtkJcFDOI6rr}/_buildManifest.js +0 -0
  36. /package/.next/static/{ym6mA6Dl9wX62h3AoYO45 → oUEq-Bd47xtkJcFDOI6rr}/_ssgManifest.js +0 -0
@@ -17,6 +17,7 @@
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.cleanClaudeResponse = cleanClaudeResponse;
19
19
  exports.cleanGeminiResponse = cleanGeminiResponse;
20
+ exports.resolveExtractionStartIndex = resolveExtractionStartIndex;
20
21
  exports.startPolling = startPolling;
21
22
  exports.stopPolling = stopPolling;
22
23
  exports.stopAllPolling = stopAllPolling;
@@ -72,6 +73,31 @@ const GEMINI_LOADING_INDICATORS = [
72
73
  function incompleteResult(lineCount) {
73
74
  return { response: '', isComplete: false, lineCount };
74
75
  }
76
+ /**
77
+ * Build a complete ExtractionResult for a detected prompt.
78
+ *
79
+ * Shared between Claude early prompt detection (section 3-4, site 1) and
80
+ * fallback prompt detection (section 3-4, site 2) in extractResponse().
81
+ * Applies resolveExtractionStartIndex() to limit extraction to lastCapturedLine
82
+ * onwards, then strips ANSI codes for safe DB storage (Stage 4 MF-001).
83
+ *
84
+ * @param lines - The trimmed tmux buffer lines array
85
+ * @param lastCapturedLine - Number of lines previously captured
86
+ * @param totalLines - Total line count in the buffer
87
+ * @param bufferReset - External buffer reset flag
88
+ * @param cliToolId - CLI tool identifier
89
+ * @param findRecentUserPromptIndex - Callback to locate the most recent user prompt
90
+ * @returns ExtractionResult with isComplete: true and ANSI-stripped response
91
+ */
92
+ function buildPromptExtractionResult(lines, lastCapturedLine, totalLines, bufferReset, cliToolId, findRecentUserPromptIndex) {
93
+ const startIndex = resolveExtractionStartIndex(lastCapturedLine, totalLines, bufferReset, cliToolId, findRecentUserPromptIndex);
94
+ const extractedLines = lines.slice(startIndex);
95
+ return {
96
+ response: (0, cli_patterns_1.stripAnsi)(extractedLines.join('\n')),
97
+ isComplete: true,
98
+ lineCount: totalLines,
99
+ };
100
+ }
75
101
  /**
76
102
  * Active pollers map: "worktreeId:cliToolId" -> NodeJS.Timeout
77
103
  */
@@ -223,6 +249,64 @@ function cleanGeminiResponse(response) {
223
249
  }
224
250
  return cleanedLines.join('\n').trim();
225
251
  }
252
+ /**
253
+ * Determine the start index for response extraction based on buffer state.
254
+ * Shared between normal response extraction and prompt detection paths.
255
+ *
256
+ * Implements a 4-branch decision tree for startIndex determination:
257
+ * 1. bufferWasReset -> findRecentUserPromptIndex(40) + 1, or 0 if not found
258
+ * 2. cliToolId === 'codex' -> Math.max(0, lastCapturedLine)
259
+ * 3. lastCapturedLine >= totalLines - 5 (scroll boundary) ->
260
+ * findRecentUserPromptIndex(50) + 1, or totalLines - 40 if not found
261
+ * 4. Normal case -> Math.max(0, lastCapturedLine)
262
+ *
263
+ * `bufferWasReset` is computed internally from `lastCapturedLine`, `totalLines`,
264
+ * and `bufferReset`. Callers do NOT need to pre-compute `bufferWasReset`.
265
+ * (Design: MF-001 responsibility boundary)
266
+ *
267
+ * Design references:
268
+ * - Issue #326 design policy section 3-2 (4-branch startIndex table)
269
+ * - Stage 4 SF-001: Defensive validation (negative lastCapturedLine clamped to 0)
270
+ * - Stage 1 SF-001: findRecentUserPromptIndex as callback for SRP/testability
271
+ *
272
+ * @param lastCapturedLine - Number of lines previously captured from the tmux buffer.
273
+ * Negative values are defensively clamped to 0 (Stage 4 SF-001).
274
+ * @param totalLines - Total number of (non-empty-trailing) lines in the current tmux buffer.
275
+ * @param bufferReset - External flag indicating the buffer was reset (e.g., session restart).
276
+ * Combined with `lastCapturedLine >= totalLines` to derive internal `bufferWasReset`.
277
+ * @param cliToolId - CLI tool identifier. Affects branch 2 (Codex-specific path).
278
+ * Note: When called from the Claude early prompt detection path (section 3-4),
279
+ * cliToolId is always 'claude', making the Codex branch unreachable in that context.
280
+ * The parameter is retained for the function's generality across all call sites.
281
+ * @param findRecentUserPromptIndex - Callback that searches the tmux buffer backwards
282
+ * for the most recent user prompt line within a given window size.
283
+ * Returns the line index (>= 0) if found, or -1 if not found.
284
+ * @returns The 0-based line index from which response extraction should begin.
285
+ *
286
+ * @internal Exported for testing only
287
+ */
288
+ function resolveExtractionStartIndex(lastCapturedLine, totalLines, bufferReset, cliToolId, findRecentUserPromptIndex) {
289
+ // Defensive validation: clamp negative values to 0 (Stage 4 SF-001)
290
+ lastCapturedLine = Math.max(0, lastCapturedLine);
291
+ // Compute bufferWasReset internally (MF-001: responsibility boundary)
292
+ const bufferWasReset = lastCapturedLine >= totalLines || bufferReset;
293
+ // Branch 1: Buffer was reset - find the most recent user prompt as anchor
294
+ if (bufferWasReset) {
295
+ const foundUserPrompt = findRecentUserPromptIndex(40);
296
+ return foundUserPrompt >= 0 ? foundUserPrompt + 1 : 0;
297
+ }
298
+ // Branch 2: Codex uses lastCapturedLine directly (Codex-specific TUI behavior)
299
+ if (cliToolId === 'codex') {
300
+ return Math.max(0, lastCapturedLine);
301
+ }
302
+ // Branch 3: Near scroll boundary - buffer may have scrolled, search for user prompt
303
+ if (lastCapturedLine >= totalLines - 5) {
304
+ const foundUserPrompt = findRecentUserPromptIndex(50);
305
+ return foundUserPrompt >= 0 ? foundUserPrompt + 1 : Math.max(0, totalLines - 40);
306
+ }
307
+ // Branch 4: Normal case - start from lastCapturedLine
308
+ return Math.max(0, lastCapturedLine);
309
+ }
226
310
  /**
227
311
  * Extract CLI tool response from tmux output
228
312
  * Detects when a CLI tool has completed a response by looking for tool-specific patterns
@@ -278,13 +362,8 @@ function extractResponse(output, lastCapturedLine, cliToolId) {
278
362
  const fullOutput = lines.join('\n');
279
363
  const promptDetection = detectPromptWithOptions(fullOutput, cliToolId);
280
364
  if (promptDetection.isPrompt) {
281
- // Return the full output as a complete interactive prompt
282
- // Use the cleaned output without ANSI codes
283
- return {
284
- response: (0, cli_patterns_1.stripAnsi)(fullOutput),
285
- isComplete: true,
286
- lineCount: totalLines,
287
- };
365
+ // Prompt detection uses full buffer for accuracy, but return only lastCapturedLine onwards
366
+ return buildPromptExtractionResult(lines, lastCapturedLine, totalLines, bufferReset, cliToolId, findRecentUserPromptIndex);
288
367
  }
289
368
  }
290
369
  // Strip ANSI codes before pattern matching
@@ -302,32 +381,9 @@ function extractResponse(output, lastCapturedLine, cliToolId) {
302
381
  // CLI tool has completed response
303
382
  // Extract the response content from lastCapturedLine to the separator (not just last 20 lines)
304
383
  const responseLines = [];
305
- // Handle tmux buffer scrolling: if lastCapturedLine >= totalLines, the buffer has scrolled
306
- // In this case, we need to find the response in the current visible buffer
307
- let startIndex;
308
- // For all tools: check if buffer has been reset/cleared (startIndex would be >= totalLines)
309
- // This happens when a session is restarted or buffer is cleared
310
- const bufferWasReset = lastCapturedLine >= totalLines || bufferReset;
311
- if (bufferWasReset) {
312
- // Buffer was reset - find the most recent user prompt
313
- const foundUserPrompt = findRecentUserPromptIndex(40);
314
- startIndex = foundUserPrompt >= 0 ? foundUserPrompt + 1 : 0;
315
- }
316
- else if (cliToolId === 'codex') {
317
- // Normal case for Codex: use lastCapturedLine
318
- startIndex = Math.max(0, lastCapturedLine);
319
- }
320
- else if (lastCapturedLine >= totalLines - 5) {
321
- // Buffer may have scrolled - look for the start of the new response
322
- // Find the last user input prompt to identify where the response starts
323
- const foundUserPrompt = findRecentUserPromptIndex(50);
324
- // Start extraction from after the user prompt, or from a safe earlier point
325
- startIndex = foundUserPrompt >= 0 ? foundUserPrompt + 1 : Math.max(0, totalLines - 40);
326
- }
327
- else {
328
- // Normal case: start from lastCapturedLine
329
- startIndex = Math.max(0, lastCapturedLine);
330
- }
384
+ // Determine start index for response extraction using shared helper
385
+ // Handles buffer reset, Codex-specific logic, scroll boundary, and normal cases
386
+ const startIndex = resolveExtractionStartIndex(lastCapturedLine, totalLines, bufferReset, cliToolId, findRecentUserPromptIndex);
331
387
  let endIndex = totalLines; // Track where extraction actually ended
332
388
  for (let i = startIndex; i < totalLines; i++) {
333
389
  const line = lines[i];
@@ -415,12 +471,9 @@ function extractResponse(output, lastCapturedLine, cliToolId) {
415
471
  const fullOutput = lines.join('\n');
416
472
  const promptDetection = detectPromptWithOptions(fullOutput, cliToolId);
417
473
  if (promptDetection.isPrompt) {
418
- // This is an interactive prompt - consider it complete
419
- return {
420
- response: fullOutput,
421
- isComplete: true,
422
- lineCount: totalLines,
423
- };
474
+ // Prompt detection uses full buffer for accuracy, but return only lastCapturedLine onwards
475
+ // stripAnsi is applied inside buildPromptExtractionResult (Stage 4 MF-001: XSS risk mitigation)
476
+ return buildPromptExtractionResult(lines, lastCapturedLine, totalLines, bufferReset, cliToolId, findRecentUserPromptIndex);
424
477
  }
425
478
  // Not a prompt, but we may have a partial response in progress (even if Claude shows a spinner)
426
479
  const responseLines = [];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "commandmate",
3
- "version": "0.2.12",
3
+ "version": "0.2.13",
4
4
  "description": "Git worktree management with Claude CLI and tmux sessions",
5
5
  "keywords": [
6
6
  "claude-code",