commandmate 0.3.3 → 0.3.4
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/.next/BUILD_ID +1 -1
- package/.next/app-build-manifest.json +5 -5
- package/.next/app-path-routes-manifest.json +1 -1
- package/.next/build-manifest.json +2 -2
- package/.next/cache/.tsbuildinfo +1 -1
- package/.next/cache/config.json +3 -3
- package/.next/cache/webpack/client-production/0.pack +0 -0
- package/.next/cache/webpack/client-production/1.pack +0 -0
- package/.next/cache/webpack/client-production/2.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack +0 -0
- package/.next/cache/webpack/client-production/index.pack.old +0 -0
- package/.next/cache/webpack/edge-server-production/0.pack +0 -0
- package/.next/cache/webpack/edge-server-production/index.pack +0 -0
- package/.next/cache/webpack/server-production/0.pack +0 -0
- package/.next/cache/webpack/server-production/index.pack +0 -0
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +1 -1
- package/.next/required-server-files.json +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/api/app/update-check/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app/proxy/[...path]/route.js +1 -1
- package/.next/server/app/worktrees/[id]/files/[...path]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/page.js +4 -4
- package/.next/server/app/worktrees/[id]/page_client-reference-manifest.js +1 -1
- package/.next/server/app/worktrees/[id]/terminal/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +8 -8
- package/.next/server/chunks/2314.js +1 -1
- package/.next/server/chunks/539.js +1 -1
- package/.next/server/chunks/6228.js +1 -1
- package/.next/server/chunks/7425.js +41 -35
- package/.next/server/chunks/9446.js +1 -1
- package/.next/server/middleware-manifest.json +5 -5
- package/.next/server/pages/500.html +1 -1
- package/.next/server/pages-manifest.json +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/static/chunks/{8091-274bc0716106e7fc.js → 8091-c0e955616dd86f82.js} +1 -1
- package/.next/static/chunks/app/worktrees/[id]/page-9c0c64488c17db3c.js +1 -0
- package/.next/static/css/fa3df0e6f437f2ba.css +3 -0
- package/.next/trace +5 -5
- package/dist/server/src/lib/cli-tools/types.js +35 -1
- package/dist/server/src/lib/cli-tools/vibe-local.js +12 -3
- package/dist/server/src/lib/db-migrations.js +17 -1
- package/dist/server/src/lib/db.js +21 -2
- package/dist/server/src/lib/prompt-detector.js +23 -4
- package/dist/server/src/lib/response-poller.js +28 -12
- package/package.json +4 -4
- package/.next/static/chunks/app/worktrees/[id]/page-78580947c201d698.js +0 -1
- package/.next/static/css/e85de230ef5ddc40.css +0 -3
- /package/.next/static/{O7EDFfAYQNe_HRbORxQAC → BiyH3zkbySg7ZWTeZuXqj}/_buildManifest.js +0 -0
- /package/.next/static/{O7EDFfAYQNe_HRbORxQAC → BiyH3zkbySg7ZWTeZuXqj}/_ssgManifest.js +0 -0
- /package/.next/static/chunks/app/{page-060057e02b841125.js → page-9e523a8f415bc707.js} +0 -0
|
@@ -3,10 +3,11 @@
|
|
|
3
3
|
* Type definitions and interfaces for CLI tools
|
|
4
4
|
*/
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.OLLAMA_MODEL_PATTERN = exports.CLI_TOOL_DISPLAY_NAMES = exports.CLI_TOOL_IDS = void 0;
|
|
6
|
+
exports.OLLAMA_MODEL_PATTERN = exports.VIBE_LOCAL_CONTEXT_WINDOW_MAX = exports.VIBE_LOCAL_CONTEXT_WINDOW_MIN = exports.CLI_TOOL_DISPLAY_NAMES = exports.CLI_TOOL_IDS = void 0;
|
|
7
7
|
exports.isCliToolType = isCliToolType;
|
|
8
8
|
exports.getCliToolDisplayName = getCliToolDisplayName;
|
|
9
9
|
exports.getCliToolDisplayNameSafe = getCliToolDisplayNameSafe;
|
|
10
|
+
exports.isValidVibeLocalContextWindow = isValidVibeLocalContextWindow;
|
|
10
11
|
/**
|
|
11
12
|
* CLI Tool IDs constant array
|
|
12
13
|
* T2.1: Single source of truth for CLI tool IDs
|
|
@@ -64,6 +65,39 @@ function getCliToolDisplayNameSafe(cliToolId, fallback = 'Assistant') {
|
|
|
64
65
|
return getCliToolDisplayName(cliToolId);
|
|
65
66
|
return fallback;
|
|
66
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* Minimum context window size for vibe-local.
|
|
70
|
+
* [S1-007] Lower bound rationale: Ollama's actual minimum context window is
|
|
71
|
+
* typically 2048+, but 128 is set as a permissive lower bound to accommodate
|
|
72
|
+
* custom models or future models with smaller contexts. Users are recommended
|
|
73
|
+
* to use practical values (e.g., 2048+).
|
|
74
|
+
* [S1-004] vibe-local specific constant. If more vibe-local constants are added,
|
|
75
|
+
* consider extracting to src/lib/cli-tools/vibe-local-config.ts.
|
|
76
|
+
* [SEC-002] Used to prevent unreasonable values in CLI arguments.
|
|
77
|
+
*/
|
|
78
|
+
exports.VIBE_LOCAL_CONTEXT_WINDOW_MIN = 128;
|
|
79
|
+
/**
|
|
80
|
+
* Maximum context window size for vibe-local (2M tokens).
|
|
81
|
+
* Shared between API validation and defense-in-depth (DRY principle).
|
|
82
|
+
* [S1-004] vibe-local specific constant. If more vibe-local constants are added,
|
|
83
|
+
* consider extracting to src/lib/cli-tools/vibe-local-config.ts.
|
|
84
|
+
* [SEC-002] Used to prevent unreasonable values in CLI arguments.
|
|
85
|
+
*/
|
|
86
|
+
exports.VIBE_LOCAL_CONTEXT_WINDOW_MAX = 2097152;
|
|
87
|
+
/**
|
|
88
|
+
* Validate vibe-local context window value.
|
|
89
|
+
* Shared between API layer and CLI layer (defense-in-depth).
|
|
90
|
+
* [S1-001] DRY: Single source of truth for context window validation.
|
|
91
|
+
*
|
|
92
|
+
* @param value - Value to validate (accepts unknown for type guard usage)
|
|
93
|
+
* @returns True if value is a valid context window size (integer between MIN and MAX)
|
|
94
|
+
*/
|
|
95
|
+
function isValidVibeLocalContextWindow(value) {
|
|
96
|
+
return (typeof value === 'number' &&
|
|
97
|
+
Number.isInteger(value) &&
|
|
98
|
+
value >= exports.VIBE_LOCAL_CONTEXT_WINDOW_MIN &&
|
|
99
|
+
value <= exports.VIBE_LOCAL_CONTEXT_WINDOW_MAX);
|
|
100
|
+
}
|
|
67
101
|
/**
|
|
68
102
|
* Ollama model name validation pattern.
|
|
69
103
|
* Allows: alphanumeric start, followed by alphanumeric, dots, underscores, colons, slashes, hyphens.
|
|
@@ -69,18 +69,27 @@ class VibeLocalTool extends base_1.BaseCLITool {
|
|
|
69
69
|
});
|
|
70
70
|
// Wait a moment for the session to be created
|
|
71
71
|
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
72
|
-
// Read Ollama model
|
|
72
|
+
// Read Ollama model and context window preferences from DB
|
|
73
73
|
// [SEC-001] Re-validate model name at point of use (defense-in-depth)
|
|
74
|
+
// [S1-005] DB direct access follows existing vibeLocalModel pattern;
|
|
75
|
+
// future DIP refactoring should pass these as startSession() arguments.
|
|
74
76
|
let vibeLocalCommand = 'vibe-local -y';
|
|
75
77
|
try {
|
|
76
78
|
const db = (0, db_instance_1.getDbInstance)();
|
|
77
79
|
const wt = (0, db_1.getWorktreeById)(db, worktreeId);
|
|
78
80
|
if (wt?.vibeLocalModel && types_1.OLLAMA_MODEL_PATTERN.test(wt.vibeLocalModel)) {
|
|
79
|
-
vibeLocalCommand
|
|
81
|
+
vibeLocalCommand += ` -m ${wt.vibeLocalModel}`;
|
|
82
|
+
}
|
|
83
|
+
// [C2-008] contextWindow from the same wt object (no additional DB call)
|
|
84
|
+
const ctxWindow = wt?.vibeLocalContextWindow;
|
|
85
|
+
// [SEC-002] Defense-in-depth: re-validate at point of use
|
|
86
|
+
// [S4-001] Number() cast for additional safety in template literal
|
|
87
|
+
if ((0, types_1.isValidVibeLocalContextWindow)(ctxWindow)) {
|
|
88
|
+
vibeLocalCommand += ` --context-window ${Number(ctxWindow)}`;
|
|
80
89
|
}
|
|
81
90
|
}
|
|
82
91
|
catch {
|
|
83
|
-
// DB read failure is non-fatal; use
|
|
92
|
+
// DB read failure is non-fatal; use defaults
|
|
84
93
|
}
|
|
85
94
|
// Start vibe-local in interactive mode with auto-approve (-y)
|
|
86
95
|
// -y flag skips the permission confirmation prompt
|
|
@@ -19,7 +19,7 @@ const db_1 = require("./db");
|
|
|
19
19
|
* Current schema version
|
|
20
20
|
* Increment this when adding new migrations
|
|
21
21
|
*/
|
|
22
|
-
exports.CURRENT_SCHEMA_VERSION =
|
|
22
|
+
exports.CURRENT_SCHEMA_VERSION = 20;
|
|
23
23
|
/**
|
|
24
24
|
* Migration registry
|
|
25
25
|
* All migrations should be added to this array in order
|
|
@@ -860,6 +860,22 @@ const migrations = [
|
|
|
860
860
|
// vibe_local_model is a nullable TEXT column; harmless if unused
|
|
861
861
|
console.log('No rollback for vibe_local_model column (SQLite limitation)');
|
|
862
862
|
}
|
|
863
|
+
},
|
|
864
|
+
{
|
|
865
|
+
version: 20,
|
|
866
|
+
name: 'add-vibe-local-context-window-column',
|
|
867
|
+
up: (db) => {
|
|
868
|
+
// Issue #374: Add vibe_local_context_window column for Ollama context window size
|
|
869
|
+
// NULL means use the default (vibe-local CLI decides)
|
|
870
|
+
db.exec(`
|
|
871
|
+
ALTER TABLE worktrees ADD COLUMN vibe_local_context_window INTEGER DEFAULT NULL;
|
|
872
|
+
`);
|
|
873
|
+
console.log('✓ Added vibe_local_context_window column to worktrees table');
|
|
874
|
+
},
|
|
875
|
+
down: () => {
|
|
876
|
+
// vibe_local_context_window is a nullable INTEGER column; harmless if unused
|
|
877
|
+
console.log('No rollback for vibe_local_context_window column (SQLite limitation)');
|
|
878
|
+
}
|
|
863
879
|
}
|
|
864
880
|
];
|
|
865
881
|
/**
|
|
@@ -34,6 +34,7 @@ exports.updateStatus = updateStatus;
|
|
|
34
34
|
exports.updateCliToolId = updateCliToolId;
|
|
35
35
|
exports.updateSelectedAgents = updateSelectedAgents;
|
|
36
36
|
exports.updateVibeLocalModel = updateVibeLocalModel;
|
|
37
|
+
exports.updateVibeLocalContextWindow = updateVibeLocalContextWindow;
|
|
37
38
|
exports.getMemosByWorktreeId = getMemosByWorktreeId;
|
|
38
39
|
exports.getMemoById = getMemoById;
|
|
39
40
|
exports.createMemo = createMemo;
|
|
@@ -193,7 +194,7 @@ function getWorktrees(db, repositoryPath) {
|
|
|
193
194
|
w.id, w.name, w.path, w.repository_path, w.repository_name, w.description,
|
|
194
195
|
w.last_user_message, w.last_user_message_at, w.last_message_summary,
|
|
195
196
|
w.updated_at, w.favorite, w.status, w.link, w.cli_tool_id, w.last_viewed_at,
|
|
196
|
-
w.selected_agents, w.vibe_local_model,
|
|
197
|
+
w.selected_agents, w.vibe_local_model, w.vibe_local_context_window,
|
|
197
198
|
(SELECT MAX(timestamp) FROM chat_messages
|
|
198
199
|
WHERE worktree_id = w.id AND role = 'assistant') as last_assistant_message_at
|
|
199
200
|
FROM worktrees w
|
|
@@ -231,6 +232,7 @@ function getWorktrees(db, repositoryPath) {
|
|
|
231
232
|
cliToolId: row.cli_tool_id ?? 'claude',
|
|
232
233
|
selectedAgents: (0, selected_agents_validator_1.parseSelectedAgents)(row.selected_agents),
|
|
233
234
|
vibeLocalModel: row.vibe_local_model ?? null,
|
|
235
|
+
vibeLocalContextWindow: row.vibe_local_context_window ?? null,
|
|
234
236
|
};
|
|
235
237
|
});
|
|
236
238
|
}
|
|
@@ -265,7 +267,7 @@ function getWorktreeById(db, id) {
|
|
|
265
267
|
w.id, w.name, w.path, w.repository_path, w.repository_name, w.description,
|
|
266
268
|
w.last_user_message, w.last_user_message_at, w.last_message_summary,
|
|
267
269
|
w.updated_at, w.favorite, w.status, w.link, w.cli_tool_id, w.last_viewed_at,
|
|
268
|
-
w.selected_agents, w.vibe_local_model,
|
|
270
|
+
w.selected_agents, w.vibe_local_model, w.vibe_local_context_window,
|
|
269
271
|
(SELECT MAX(timestamp) FROM chat_messages
|
|
270
272
|
WHERE worktree_id = w.id AND role = 'assistant') as last_assistant_message_at
|
|
271
273
|
FROM worktrees w
|
|
@@ -294,6 +296,7 @@ function getWorktreeById(db, id) {
|
|
|
294
296
|
cliToolId: row.cli_tool_id ?? 'claude',
|
|
295
297
|
selectedAgents: (0, selected_agents_validator_1.parseSelectedAgents)(row.selected_agents),
|
|
296
298
|
vibeLocalModel: row.vibe_local_model ?? null,
|
|
299
|
+
vibeLocalContextWindow: row.vibe_local_context_window ?? null,
|
|
297
300
|
};
|
|
298
301
|
}
|
|
299
302
|
/**
|
|
@@ -728,6 +731,22 @@ function updateVibeLocalModel(db, id, model) {
|
|
|
728
731
|
`);
|
|
729
732
|
stmt.run(model, id);
|
|
730
733
|
}
|
|
734
|
+
/**
|
|
735
|
+
* Update vibe_local_context_window for a worktree
|
|
736
|
+
* Issue #374: Persists the user's Ollama context window size for vibe-local
|
|
737
|
+
*
|
|
738
|
+
* @param db - Database instance
|
|
739
|
+
* @param id - Worktree ID
|
|
740
|
+
* @param contextWindow - Context window size or null for default
|
|
741
|
+
*/
|
|
742
|
+
function updateVibeLocalContextWindow(db, id, contextWindow) {
|
|
743
|
+
const stmt = db.prepare(`
|
|
744
|
+
UPDATE worktrees
|
|
745
|
+
SET vibe_local_context_window = ?
|
|
746
|
+
WHERE id = ?
|
|
747
|
+
`);
|
|
748
|
+
stmt.run(contextWindow, id);
|
|
749
|
+
}
|
|
731
750
|
/**
|
|
732
751
|
* Map database row to WorktreeMemo model
|
|
733
752
|
*/
|
|
@@ -166,12 +166,12 @@ const TEXT_INPUT_PATTERNS = [
|
|
|
166
166
|
/differently/i,
|
|
167
167
|
];
|
|
168
168
|
/**
|
|
169
|
-
* Pattern for ❯ (U+276F) / ● (U+25CF) indicator lines used by CLI tools to mark the default selection.
|
|
170
|
-
* Claude CLI uses ❯, Gemini CLI uses
|
|
169
|
+
* Pattern for ❯ (U+276F) / ● (U+25CF) / › (U+203A) indicator lines used by CLI tools to mark the default selection.
|
|
170
|
+
* Claude CLI uses ❯, Gemini CLI uses ●, Codex CLI uses › (Issue #372).
|
|
171
171
|
* Used in Pass 1 (existence check) and Pass 2 (option collection) of the 2-pass detection.
|
|
172
172
|
* Anchored at both ends -- ReDoS safe (S4-001).
|
|
173
173
|
*/
|
|
174
|
-
const DEFAULT_OPTION_PATTERN = /^\s*[\u276F\u25CF]\s*(\d+)\.\s*(.+)$/;
|
|
174
|
+
const DEFAULT_OPTION_PATTERN = /^\s*[\u276F\u25CF\u203A]\s*(\d+)\.\s*(.+)$/;
|
|
175
175
|
/**
|
|
176
176
|
* Pattern for normal option lines (no ❯ indicator, just leading whitespace + number).
|
|
177
177
|
* Only applied in Pass 2 when ❯ indicator existence is confirmed by Pass 1.
|
|
@@ -207,6 +207,13 @@ const SEPARATOR_LINE_PATTERN = /^[-─]+$/;
|
|
|
207
207
|
* @see Issue #256: multiple_choice prompt detection improvement
|
|
208
208
|
*/
|
|
209
209
|
const QUESTION_SCAN_RANGE = 3;
|
|
210
|
+
/**
|
|
211
|
+
* Maximum consecutive continuation lines allowed between options and question.
|
|
212
|
+
* Issue #372: Codex TUI indents all output with 2 spaces, causing isContinuationLine()
|
|
213
|
+
* to match body text lines indefinitely. Without this limit, the scanner would traverse
|
|
214
|
+
* through the entire command output, picking up numbered lists as false options.
|
|
215
|
+
*/
|
|
216
|
+
const MAX_CONTINUATION_LINES = 5;
|
|
210
217
|
/**
|
|
211
218
|
* Creates a "no prompt detected" result.
|
|
212
219
|
* Centralizes the repeated pattern of returning isPrompt: false with trimmed content.
|
|
@@ -544,6 +551,7 @@ function detectMultipleChoicePrompt(output, options) {
|
|
|
544
551
|
// ==========================================================================
|
|
545
552
|
const collectedOptions = [];
|
|
546
553
|
let questionEndIndex = -1;
|
|
554
|
+
let continuationLineCount = 0;
|
|
547
555
|
for (let i = effectiveEnd - 1; i >= scanStart; i--) {
|
|
548
556
|
const line = lines[i].trim();
|
|
549
557
|
// Try DEFAULT_OPTION_PATTERN first (❯ indicator)
|
|
@@ -552,6 +560,7 @@ function detectMultipleChoicePrompt(output, options) {
|
|
|
552
560
|
const number = parseInt(defaultMatch[1], 10);
|
|
553
561
|
const label = defaultMatch[2].trim();
|
|
554
562
|
collectedOptions.unshift({ number, label, isDefault: true });
|
|
563
|
+
continuationLineCount = 0;
|
|
555
564
|
continue;
|
|
556
565
|
}
|
|
557
566
|
// Try NORMAL_OPTION_PATTERN (no ❯ indicator)
|
|
@@ -560,6 +569,7 @@ function detectMultipleChoicePrompt(output, options) {
|
|
|
560
569
|
const number = parseInt(normalMatch[1], 10);
|
|
561
570
|
const label = normalMatch[2].trim();
|
|
562
571
|
collectedOptions.unshift({ number, label, isDefault: false });
|
|
572
|
+
continuationLineCount = 0;
|
|
563
573
|
continue;
|
|
564
574
|
}
|
|
565
575
|
// [Issue #287 Bug3] User input prompt barrier:
|
|
@@ -568,7 +578,7 @@ function detectMultipleChoicePrompt(output, options) {
|
|
|
568
578
|
// user input prompt (e.g., "❯ 1", "❯ /command") or idle prompt ("❯").
|
|
569
579
|
// Anything above this line in the scrollback is historical conversation text,
|
|
570
580
|
// not an active prompt. Stop scanning to prevent false positives.
|
|
571
|
-
if (collectedOptions.length === 0 && (line.startsWith('\u276F') || line.startsWith('\u25CF'))) {
|
|
581
|
+
if (collectedOptions.length === 0 && (line.startsWith('\u276F') || line.startsWith('\u25CF') || line.startsWith('\u203A'))) {
|
|
572
582
|
return noPromptResult(output);
|
|
573
583
|
}
|
|
574
584
|
// Non-option line handling
|
|
@@ -591,6 +601,15 @@ function detectMultipleChoicePrompt(output, options) {
|
|
|
591
601
|
// or path/filename fragments from terminal width wrapping - Issue #181)
|
|
592
602
|
const rawLine = lines[i]; // Original line with indentation preserved
|
|
593
603
|
if (isContinuationLine(rawLine, line)) {
|
|
604
|
+
continuationLineCount++;
|
|
605
|
+
// Issue #372: Codex TUI indents all output with 2 spaces, causing
|
|
606
|
+
// every line to match isContinuationLine(). Limit the scan distance
|
|
607
|
+
// to prevent traversing into body text where numbered lists would be
|
|
608
|
+
// collected as false options.
|
|
609
|
+
if (continuationLineCount > MAX_CONTINUATION_LINES) {
|
|
610
|
+
questionEndIndex = i;
|
|
611
|
+
break;
|
|
612
|
+
}
|
|
594
613
|
// Skip continuation lines and continue scanning for more options
|
|
595
614
|
continue;
|
|
596
615
|
}
|
|
@@ -90,13 +90,15 @@ function incompleteResult(lineCount) {
|
|
|
90
90
|
* @param findRecentUserPromptIndex - Callback to locate the most recent user prompt
|
|
91
91
|
* @returns ExtractionResult with isComplete: true and ANSI-stripped response
|
|
92
92
|
*/
|
|
93
|
-
function buildPromptExtractionResult(lines, lastCapturedLine, totalLines, bufferReset, cliToolId, findRecentUserPromptIndex) {
|
|
93
|
+
function buildPromptExtractionResult(lines, lastCapturedLine, totalLines, bufferReset, cliToolId, findRecentUserPromptIndex, promptDetection) {
|
|
94
94
|
const startIndex = resolveExtractionStartIndex(lastCapturedLine, totalLines, bufferReset, cliToolId, findRecentUserPromptIndex);
|
|
95
95
|
const extractedLines = lines.slice(startIndex);
|
|
96
96
|
return {
|
|
97
97
|
response: (0, cli_patterns_1.stripAnsi)(extractedLines.join('\n')),
|
|
98
98
|
isComplete: true,
|
|
99
99
|
lineCount: totalLines,
|
|
100
|
+
promptDetection,
|
|
101
|
+
bufferReset,
|
|
100
102
|
};
|
|
101
103
|
}
|
|
102
104
|
/**
|
|
@@ -357,14 +359,20 @@ function extractResponse(output, lastCapturedLine, cliToolId) {
|
|
|
357
359
|
}
|
|
358
360
|
return -1;
|
|
359
361
|
};
|
|
360
|
-
// Early check for
|
|
361
|
-
// Permission prompts appear after normal responses and need special handling
|
|
362
|
-
|
|
362
|
+
// Early check for interactive prompts (before extraction logic)
|
|
363
|
+
// Permission prompts appear after normal responses and need special handling.
|
|
364
|
+
// Issue #372: Codex command confirmation prompts (› 1. Yes, proceed) match
|
|
365
|
+
// CODEX_PROMPT_PATTERN, causing isCodexOrGeminiComplete to fire prematurely.
|
|
366
|
+
// Early detection ensures prompt options are preserved in the extraction result.
|
|
367
|
+
if (cliToolId === 'claude' || cliToolId === 'codex') {
|
|
363
368
|
const fullOutput = lines.join('\n');
|
|
364
369
|
const promptDetection = detectPromptWithOptions(fullOutput, cliToolId);
|
|
365
370
|
if (promptDetection.isPrompt) {
|
|
366
|
-
// Prompt detection uses full buffer for accuracy, but return only lastCapturedLine onwards
|
|
367
|
-
|
|
371
|
+
// Prompt detection uses full buffer for accuracy, but return only lastCapturedLine onwards.
|
|
372
|
+
// Issue #372: Carry promptDetection through ExtractionResult so checkForResponse()
|
|
373
|
+
// can use it directly, avoiding a second detection on the (potentially truncated)
|
|
374
|
+
// extracted portion which may miss the › indicator line.
|
|
375
|
+
return buildPromptExtractionResult(lines, lastCapturedLine, totalLines, bufferReset, cliToolId, findRecentUserPromptIndex, promptDetection);
|
|
368
376
|
}
|
|
369
377
|
}
|
|
370
378
|
// Strip ANSI codes before pattern matching
|
|
@@ -466,6 +474,7 @@ function extractResponse(output, lastCapturedLine, cliToolId) {
|
|
|
466
474
|
response,
|
|
467
475
|
isComplete: true,
|
|
468
476
|
lineCount: endIndex, // Use endIndex instead of totalLines to track where we actually stopped
|
|
477
|
+
bufferReset,
|
|
469
478
|
};
|
|
470
479
|
}
|
|
471
480
|
// Check if this is an interactive prompt (yes/no or multiple choice)
|
|
@@ -475,7 +484,7 @@ function extractResponse(output, lastCapturedLine, cliToolId) {
|
|
|
475
484
|
if (promptDetection.isPrompt) {
|
|
476
485
|
// Prompt detection uses full buffer for accuracy, but return only lastCapturedLine onwards
|
|
477
486
|
// stripAnsi is applied inside buildPromptExtractionResult (Stage 4 MF-001: XSS risk mitigation)
|
|
478
|
-
return buildPromptExtractionResult(lines, lastCapturedLine, totalLines, bufferReset, cliToolId, findRecentUserPromptIndex);
|
|
487
|
+
return buildPromptExtractionResult(lines, lastCapturedLine, totalLines, bufferReset, cliToolId, findRecentUserPromptIndex, promptDetection);
|
|
479
488
|
}
|
|
480
489
|
// Not a prompt, but we may have a partial response in progress (even if Claude shows a spinner)
|
|
481
490
|
const responseLines = [];
|
|
@@ -525,6 +534,7 @@ async function checkForResponse(worktreeId, cliToolId) {
|
|
|
525
534
|
// Check if CLI tool session is running
|
|
526
535
|
const running = await (0, cli_session_1.isSessionRunning)(worktreeId, cliToolId);
|
|
527
536
|
if (!running) {
|
|
537
|
+
console.log(`[checkForResponse] Session not running for ${worktreeId} (${cliToolId}), stopping poller`);
|
|
528
538
|
stopPolling(worktreeId, cliToolId);
|
|
529
539
|
return false;
|
|
530
540
|
}
|
|
@@ -556,17 +566,23 @@ async function checkForResponse(worktreeId, cliToolId) {
|
|
|
556
566
|
}
|
|
557
567
|
// CRITICAL FIX: If lineCount == lastCapturedLine AND there's no in-progress message,
|
|
558
568
|
// this response has already been saved. Skip to prevent duplicates.
|
|
559
|
-
|
|
569
|
+
// Issue #372: Skip when buffer reset detected (TUI redraw may coincidentally match lineCount).
|
|
570
|
+
if (!result.bufferReset && result.lineCount === lastCapturedLine && !sessionState?.inProgressMessageId) {
|
|
560
571
|
return false;
|
|
561
572
|
}
|
|
562
573
|
// Additional duplicate prevention: check if savePendingAssistantResponse
|
|
563
|
-
// already saved this content by comparing line counts
|
|
564
|
-
|
|
574
|
+
// already saved this content by comparing line counts.
|
|
575
|
+
// Issue #372: Skip this check when buffer reset is detected (TUI redraw, screen clear).
|
|
576
|
+
// Codex TUI redraws cause totalLines to shrink, making lineCount < lastCapturedLine.
|
|
577
|
+
if (!result.bufferReset && result.lineCount <= lastCapturedLine) {
|
|
565
578
|
console.log(`[checkForResponse] Already saved up to line ${lastCapturedLine}, skipping (result: ${result.lineCount})`);
|
|
566
579
|
return false;
|
|
567
580
|
}
|
|
568
|
-
// Response is complete! Check if it's a prompt
|
|
569
|
-
|
|
581
|
+
// Response is complete! Check if it's a prompt.
|
|
582
|
+
// Issue #372: Prefer the prompt detection carried from extractResponse() early check,
|
|
583
|
+
// which uses the full tmux output for accuracy. The extracted portion (result.response)
|
|
584
|
+
// may be truncated and miss the › indicator line when lastCapturedLine falls just before it.
|
|
585
|
+
const promptDetection = result.promptDetection ?? detectPromptWithOptions(result.response, cliToolId);
|
|
570
586
|
if (promptDetection.isPrompt) {
|
|
571
587
|
// This is a prompt - save as prompt message
|
|
572
588
|
(0, db_1.clearInProgressMessageId)(db, worktreeId, cliToolId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "commandmate",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "Git worktree management with Claude CLI and tmux sessions",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"claude-code",
|
|
@@ -88,14 +88,14 @@
|
|
|
88
88
|
"@types/uuid": "^10.0.0",
|
|
89
89
|
"@types/ws": "^8.18.1",
|
|
90
90
|
"@vitejs/plugin-react": "^5.1.1",
|
|
91
|
-
"@vitest/coverage-v8": "^4.0.
|
|
92
|
-
"@vitest/ui": "^4.0.
|
|
91
|
+
"@vitest/coverage-v8": "^4.0.18",
|
|
92
|
+
"@vitest/ui": "^4.0.18",
|
|
93
93
|
"eslint": "^8.57.0",
|
|
94
94
|
"eslint-config-next": "^14.2.35",
|
|
95
95
|
"tailwindcss": "^3.4.18",
|
|
96
96
|
"tsc-alias": "~1.8.16",
|
|
97
97
|
"tsx": "^4.20.6",
|
|
98
98
|
"typescript": "^5.5.0",
|
|
99
|
-
"vitest": "^4.0.
|
|
99
|
+
"vitest": "^4.0.18"
|
|
100
100
|
}
|
|
101
101
|
}
|