commandmate 0.3.2 → 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 +13 -13
- package/.next/app-path-routes-manifest.json +1 -1
- package/.next/build-manifest.json +4 -4
- 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/routes-manifest.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/ollama/models/route.js +1 -0
- package/.next/server/app/api/ollama/models/route.js.nft.json +1 -0
- package/.next/server/app/api/ollama/models.body +1 -0
- package/.next/server/app/api/ollama/models.meta +1 -0
- package/.next/server/app/api/repositories/route.js +3 -3
- package/.next/server/app/api/repositories/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/auto-yes/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/cli-tool/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/current-output/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/[logId]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/execution-logs/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/interrupt/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/kill-session/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/messages/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/prompt-response/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/respond/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/[scheduleId]/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/schedules/route.js +2 -2
- package/.next/server/app/api/worktrees/[id]/schedules/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/send/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/[id]/slash-commands/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/start-polling/route.js +1 -1
- package/.next/server/app/api/worktrees/[id]/start-polling/route.js.nft.json +1 -1
- package/.next/server/app/api/worktrees/route.js +1 -1
- package/.next/server/app/api/worktrees/route.js.nft.json +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/page.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 +11 -10
- package/.next/server/chunks/2314.js +1 -1
- package/.next/server/chunks/4559.js +1 -1
- package/.next/server/chunks/539.js +10 -10
- package/.next/server/chunks/5853.js +1 -1
- package/.next/server/chunks/6228.js +1 -1
- package/.next/server/chunks/7425.js +67 -41
- package/.next/server/chunks/7566.js +1 -1
- package/.next/server/chunks/8693.js +1 -1
- package/.next/server/chunks/9446.js +1 -0
- package/.next/server/functions-config-manifest.json +1 -1
- package/.next/server/middleware-build-manifest.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-c0e955616dd86f82.js +1 -0
- package/.next/static/chunks/app/page-9e523a8f415bc707.js +1 -0
- package/.next/static/chunks/app/worktrees/[id]/page-9c0c64488c17db3c.js +1 -0
- package/.next/static/chunks/{main-db79434ee4a6c931.js → main-2feda12a4d321111.js} +1 -1
- package/.next/static/css/{bd6065b03ddb3efd.css → fa3df0e6f437f2ba.css} +1 -1
- package/.next/trace +5 -5
- package/.next/types/app/api/ollama/models/route.ts +343 -0
- package/README.md +74 -76
- package/dist/server/src/config/schedule-config.js +7 -1
- package/dist/server/src/lib/auto-yes-manager.js +2 -2
- package/dist/server/src/lib/claude-executor.js +15 -4
- package/dist/server/src/lib/cli-patterns.js +73 -9
- package/dist/server/src/lib/cli-tools/gemini.js +81 -22
- package/dist/server/src/lib/cli-tools/manager.js +4 -2
- package/dist/server/src/lib/cli-tools/types.js +98 -2
- package/dist/server/src/lib/cli-tools/vibe-local.js +172 -0
- package/dist/server/src/lib/cmate-parser.js +25 -3
- package/dist/server/src/lib/db-migrations.js +66 -1
- package/dist/server/src/lib/db.js +70 -1
- package/dist/server/src/lib/prompt-detector.js +23 -3
- package/dist/server/src/lib/response-poller.js +50 -23
- package/dist/server/src/lib/schedule-manager.js +6 -2
- package/dist/server/src/lib/selected-agents-validator.js +99 -0
- package/dist/server/src/types/sidebar.js +9 -4
- package/package.json +4 -4
- package/.next/server/chunks/7536.js +0 -1
- package/.next/static/chunks/8091-925542bdfc843dce.js +0 -1
- package/.next/static/chunks/app/page-238b5a70d8c101e9.js +0 -1
- package/.next/static/chunks/app/worktrees/[id]/page-0c889ab3f30d5af7.js +0 -1
- /package/.next/static/{j8HFvzDZj7tHjAnhpXUno → BiyH3zkbySg7ZWTeZuXqj}/_buildManifest.js +0 -0
- /package/.next/static/{j8HFvzDZj7tHjAnhpXUno → BiyH3zkbySg7ZWTeZuXqj}/_ssgManifest.js +0 -0
|
@@ -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
|
|
@@ -811,6 +811,71 @@ const migrations = [
|
|
|
811
811
|
db.exec('DROP TABLE IF EXISTS scheduled_executions');
|
|
812
812
|
console.log('✓ Dropped scheduled_executions and execution_logs tables');
|
|
813
813
|
}
|
|
814
|
+
},
|
|
815
|
+
{
|
|
816
|
+
version: 18,
|
|
817
|
+
name: 'add-selected-agents-column',
|
|
818
|
+
up: (db) => {
|
|
819
|
+
// Issue #368: Add selected_agents column for agent selection persistence
|
|
820
|
+
// NOTE (R1-010): The literal values 'claude', 'codex' in the SQL CASE below
|
|
821
|
+
// are fixed at migration time and do NOT sync with TypeScript CLI_TOOL_IDS.
|
|
822
|
+
// Changes to CLI_TOOL_IDS will not retroactively affect already-migrated data.
|
|
823
|
+
// Migration tests cover all CLIToolType values to catch sync issues.
|
|
824
|
+
// Step 1: Add column
|
|
825
|
+
db.exec(`
|
|
826
|
+
ALTER TABLE worktrees ADD COLUMN selected_agents TEXT;
|
|
827
|
+
`);
|
|
828
|
+
// Step 2: Initialize existing data based on cli_tool_id
|
|
829
|
+
// - If cli_tool_id is 'claude' or 'codex' -> default ["claude","codex"]
|
|
830
|
+
// - Otherwise (e.g. 'gemini', 'vibe-local') -> [cli_tool_id, "claude"]
|
|
831
|
+
db.exec(`
|
|
832
|
+
UPDATE worktrees SET selected_agents =
|
|
833
|
+
CASE
|
|
834
|
+
WHEN cli_tool_id NOT IN ('claude', 'codex')
|
|
835
|
+
THEN json_array(cli_tool_id, 'claude')
|
|
836
|
+
ELSE '["claude","codex"]'
|
|
837
|
+
END;
|
|
838
|
+
`);
|
|
839
|
+
console.log('✓ Added selected_agents column to worktrees table');
|
|
840
|
+
console.log('✓ Initialized selected_agents based on cli_tool_id');
|
|
841
|
+
},
|
|
842
|
+
down: () => {
|
|
843
|
+
// selected_agents is a nullable TEXT column; dropping it requires table recreation
|
|
844
|
+
// which is disproportionate for a rollback. The column is harmless if unused.
|
|
845
|
+
console.log('No rollback for selected_agents column (SQLite limitation)');
|
|
846
|
+
}
|
|
847
|
+
},
|
|
848
|
+
{
|
|
849
|
+
version: 19,
|
|
850
|
+
name: 'add-vibe-local-model-column',
|
|
851
|
+
up: (db) => {
|
|
852
|
+
// Issue #368: Add vibe_local_model column for Ollama model selection
|
|
853
|
+
// NULL means use the default model (vibe-local decides)
|
|
854
|
+
db.exec(`
|
|
855
|
+
ALTER TABLE worktrees ADD COLUMN vibe_local_model TEXT DEFAULT NULL;
|
|
856
|
+
`);
|
|
857
|
+
console.log('✓ Added vibe_local_model column to worktrees table');
|
|
858
|
+
},
|
|
859
|
+
down: () => {
|
|
860
|
+
// vibe_local_model is a nullable TEXT column; harmless if unused
|
|
861
|
+
console.log('No rollback for vibe_local_model column (SQLite limitation)');
|
|
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
|
+
}
|
|
814
879
|
}
|
|
815
880
|
];
|
|
816
881
|
/**
|
|
@@ -32,6 +32,9 @@ exports.markPendingPromptsAsAnswered = markPendingPromptsAsAnswered;
|
|
|
32
32
|
exports.updateFavorite = updateFavorite;
|
|
33
33
|
exports.updateStatus = updateStatus;
|
|
34
34
|
exports.updateCliToolId = updateCliToolId;
|
|
35
|
+
exports.updateSelectedAgents = updateSelectedAgents;
|
|
36
|
+
exports.updateVibeLocalModel = updateVibeLocalModel;
|
|
37
|
+
exports.updateVibeLocalContextWindow = updateVibeLocalContextWindow;
|
|
35
38
|
exports.getMemosByWorktreeId = getMemosByWorktreeId;
|
|
36
39
|
exports.getMemoById = getMemoById;
|
|
37
40
|
exports.createMemo = createMemo;
|
|
@@ -44,6 +47,7 @@ exports.getWorktreeIdsByRepository = getWorktreeIdsByRepository;
|
|
|
44
47
|
exports.deleteRepositoryWorktrees = deleteRepositoryWorktrees;
|
|
45
48
|
exports.deleteWorktreesByIds = deleteWorktreesByIds;
|
|
46
49
|
const crypto_1 = require("crypto");
|
|
50
|
+
const selected_agents_validator_1 = require("../lib/selected-agents-validator");
|
|
47
51
|
function mapChatMessage(row) {
|
|
48
52
|
return {
|
|
49
53
|
id: row.id,
|
|
@@ -129,6 +133,16 @@ function initDatabase(db) {
|
|
|
129
133
|
* Get latest user message per CLI tool for multiple worktrees (batch query)
|
|
130
134
|
* Optimized to avoid N+1 query problem
|
|
131
135
|
*/
|
|
136
|
+
/**
|
|
137
|
+
* Get latest user message per CLI tool for multiple worktrees (batch query)
|
|
138
|
+
* Optimized to avoid N+1 query problem
|
|
139
|
+
*
|
|
140
|
+
* R4-001: SQL IN clause for cli_tool_id removed to eliminate SQLインジェクション risk.
|
|
141
|
+
* All cli_tool_id values are fetched; filtering happens at application layer.
|
|
142
|
+
* Tool count is at most 4-5, so the performance impact is negligible.
|
|
143
|
+
*
|
|
144
|
+
* R2-002: Return type changed to Partial<Record<CLIToolType, string>>
|
|
145
|
+
*/
|
|
132
146
|
function getLastMessagesByCliBatch(db, worktreeIds) {
|
|
133
147
|
if (worktreeIds.length === 0) {
|
|
134
148
|
return new Map();
|
|
@@ -149,7 +163,6 @@ function getLastMessagesByCliBatch(db, worktreeIds) {
|
|
|
149
163
|
FROM chat_messages
|
|
150
164
|
WHERE worktree_id IN (${placeholders})
|
|
151
165
|
AND role = 'user'
|
|
152
|
-
AND cli_tool_id IN ('claude', 'codex', 'gemini')
|
|
153
166
|
)
|
|
154
167
|
SELECT worktree_id, cli_tool_id, content
|
|
155
168
|
FROM ranked_messages
|
|
@@ -181,6 +194,7 @@ function getWorktrees(db, repositoryPath) {
|
|
|
181
194
|
w.id, w.name, w.path, w.repository_path, w.repository_name, w.description,
|
|
182
195
|
w.last_user_message, w.last_user_message_at, w.last_message_summary,
|
|
183
196
|
w.updated_at, w.favorite, w.status, w.link, w.cli_tool_id, w.last_viewed_at,
|
|
197
|
+
w.selected_agents, w.vibe_local_model, w.vibe_local_context_window,
|
|
184
198
|
(SELECT MAX(timestamp) FROM chat_messages
|
|
185
199
|
WHERE worktree_id = w.id AND role = 'assistant') as last_assistant_message_at
|
|
186
200
|
FROM worktrees w
|
|
@@ -216,6 +230,9 @@ function getWorktrees(db, repositoryPath) {
|
|
|
216
230
|
status: row.status || null,
|
|
217
231
|
link: row.link || undefined,
|
|
218
232
|
cliToolId: row.cli_tool_id ?? 'claude',
|
|
233
|
+
selectedAgents: (0, selected_agents_validator_1.parseSelectedAgents)(row.selected_agents),
|
|
234
|
+
vibeLocalModel: row.vibe_local_model ?? null,
|
|
235
|
+
vibeLocalContextWindow: row.vibe_local_context_window ?? null,
|
|
219
236
|
};
|
|
220
237
|
});
|
|
221
238
|
}
|
|
@@ -250,6 +267,7 @@ function getWorktreeById(db, id) {
|
|
|
250
267
|
w.id, w.name, w.path, w.repository_path, w.repository_name, w.description,
|
|
251
268
|
w.last_user_message, w.last_user_message_at, w.last_message_summary,
|
|
252
269
|
w.updated_at, w.favorite, w.status, w.link, w.cli_tool_id, w.last_viewed_at,
|
|
270
|
+
w.selected_agents, w.vibe_local_model, w.vibe_local_context_window,
|
|
253
271
|
(SELECT MAX(timestamp) FROM chat_messages
|
|
254
272
|
WHERE worktree_id = w.id AND role = 'assistant') as last_assistant_message_at
|
|
255
273
|
FROM worktrees w
|
|
@@ -276,6 +294,9 @@ function getWorktreeById(db, id) {
|
|
|
276
294
|
status: row.status || null,
|
|
277
295
|
link: row.link || undefined,
|
|
278
296
|
cliToolId: row.cli_tool_id ?? 'claude',
|
|
297
|
+
selectedAgents: (0, selected_agents_validator_1.parseSelectedAgents)(row.selected_agents),
|
|
298
|
+
vibeLocalModel: row.vibe_local_model ?? null,
|
|
299
|
+
vibeLocalContextWindow: row.vibe_local_context_window ?? null,
|
|
279
300
|
};
|
|
280
301
|
}
|
|
281
302
|
/**
|
|
@@ -678,6 +699,54 @@ function updateCliToolId(db, id, cliToolId) {
|
|
|
678
699
|
`);
|
|
679
700
|
stmt.run(cliToolId, id);
|
|
680
701
|
}
|
|
702
|
+
/**
|
|
703
|
+
* Update selected_agents for a worktree
|
|
704
|
+
* Issue #368: Persists the user's choice of 2 display agents
|
|
705
|
+
*
|
|
706
|
+
* @param db - Database instance
|
|
707
|
+
* @param id - Worktree ID
|
|
708
|
+
* @param selectedAgents - Tuple of 2 CLIToolType values
|
|
709
|
+
*/
|
|
710
|
+
function updateSelectedAgents(db, id, selectedAgents) {
|
|
711
|
+
const stmt = db.prepare(`
|
|
712
|
+
UPDATE worktrees
|
|
713
|
+
SET selected_agents = ?
|
|
714
|
+
WHERE id = ?
|
|
715
|
+
`);
|
|
716
|
+
stmt.run(JSON.stringify(selectedAgents), id);
|
|
717
|
+
}
|
|
718
|
+
/**
|
|
719
|
+
* Update vibe_local_model for a worktree
|
|
720
|
+
* Issue #368: Persists the user's Ollama model selection for vibe-local
|
|
721
|
+
*
|
|
722
|
+
* @param db - Database instance
|
|
723
|
+
* @param id - Worktree ID
|
|
724
|
+
* @param model - Model name or null for default
|
|
725
|
+
*/
|
|
726
|
+
function updateVibeLocalModel(db, id, model) {
|
|
727
|
+
const stmt = db.prepare(`
|
|
728
|
+
UPDATE worktrees
|
|
729
|
+
SET vibe_local_model = ?
|
|
730
|
+
WHERE id = ?
|
|
731
|
+
`);
|
|
732
|
+
stmt.run(model, id);
|
|
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
|
+
}
|
|
681
750
|
/**
|
|
682
751
|
* Map database row to WorktreeMemo model
|
|
683
752
|
*/
|
|
@@ -166,11 +166,12 @@ const TEXT_INPUT_PATTERNS = [
|
|
|
166
166
|
/differently/i,
|
|
167
167
|
];
|
|
168
168
|
/**
|
|
169
|
-
* Pattern for ❯ (U+276F) indicator lines used by
|
|
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).
|
|
170
171
|
* Used in Pass 1 (existence check) and Pass 2 (option collection) of the 2-pass detection.
|
|
171
172
|
* Anchored at both ends -- ReDoS safe (S4-001).
|
|
172
173
|
*/
|
|
173
|
-
const DEFAULT_OPTION_PATTERN = /^\s
|
|
174
|
+
const DEFAULT_OPTION_PATTERN = /^\s*[\u276F\u25CF\u203A]\s*(\d+)\.\s*(.+)$/;
|
|
174
175
|
/**
|
|
175
176
|
* Pattern for normal option lines (no ❯ indicator, just leading whitespace + number).
|
|
176
177
|
* Only applied in Pass 2 when ❯ indicator existence is confirmed by Pass 1.
|
|
@@ -206,6 +207,13 @@ const SEPARATOR_LINE_PATTERN = /^[-─]+$/;
|
|
|
206
207
|
* @see Issue #256: multiple_choice prompt detection improvement
|
|
207
208
|
*/
|
|
208
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;
|
|
209
217
|
/**
|
|
210
218
|
* Creates a "no prompt detected" result.
|
|
211
219
|
* Centralizes the repeated pattern of returning isPrompt: false with trimmed content.
|
|
@@ -543,6 +551,7 @@ function detectMultipleChoicePrompt(output, options) {
|
|
|
543
551
|
// ==========================================================================
|
|
544
552
|
const collectedOptions = [];
|
|
545
553
|
let questionEndIndex = -1;
|
|
554
|
+
let continuationLineCount = 0;
|
|
546
555
|
for (let i = effectiveEnd - 1; i >= scanStart; i--) {
|
|
547
556
|
const line = lines[i].trim();
|
|
548
557
|
// Try DEFAULT_OPTION_PATTERN first (❯ indicator)
|
|
@@ -551,6 +560,7 @@ function detectMultipleChoicePrompt(output, options) {
|
|
|
551
560
|
const number = parseInt(defaultMatch[1], 10);
|
|
552
561
|
const label = defaultMatch[2].trim();
|
|
553
562
|
collectedOptions.unshift({ number, label, isDefault: true });
|
|
563
|
+
continuationLineCount = 0;
|
|
554
564
|
continue;
|
|
555
565
|
}
|
|
556
566
|
// Try NORMAL_OPTION_PATTERN (no ❯ indicator)
|
|
@@ -559,6 +569,7 @@ function detectMultipleChoicePrompt(output, options) {
|
|
|
559
569
|
const number = parseInt(normalMatch[1], 10);
|
|
560
570
|
const label = normalMatch[2].trim();
|
|
561
571
|
collectedOptions.unshift({ number, label, isDefault: false });
|
|
572
|
+
continuationLineCount = 0;
|
|
562
573
|
continue;
|
|
563
574
|
}
|
|
564
575
|
// [Issue #287 Bug3] User input prompt barrier:
|
|
@@ -567,7 +578,7 @@ function detectMultipleChoicePrompt(output, options) {
|
|
|
567
578
|
// user input prompt (e.g., "❯ 1", "❯ /command") or idle prompt ("❯").
|
|
568
579
|
// Anything above this line in the scrollback is historical conversation text,
|
|
569
580
|
// not an active prompt. Stop scanning to prevent false positives.
|
|
570
|
-
if (collectedOptions.length === 0 && line.startsWith('\u276F')) {
|
|
581
|
+
if (collectedOptions.length === 0 && (line.startsWith('\u276F') || line.startsWith('\u25CF') || line.startsWith('\u203A'))) {
|
|
571
582
|
return noPromptResult(output);
|
|
572
583
|
}
|
|
573
584
|
// Non-option line handling
|
|
@@ -590,6 +601,15 @@ function detectMultipleChoicePrompt(output, options) {
|
|
|
590
601
|
// or path/filename fragments from terminal width wrapping - Issue #181)
|
|
591
602
|
const rawLine = lines[i]; // Original line with indentation preserved
|
|
592
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
|
+
}
|
|
593
613
|
// Skip continuation lines and continue scanning for more options
|
|
594
614
|
continue;
|
|
595
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
|
/**
|
|
@@ -131,7 +133,7 @@ function getPollerKey(worktreeId, cliToolId) {
|
|
|
131
133
|
*/
|
|
132
134
|
function detectPromptWithOptions(output, cliToolId) {
|
|
133
135
|
const promptOptions = (0, cli_patterns_1.buildDetectPromptOptions)(cliToolId);
|
|
134
|
-
return (0, prompt_detector_1.detectPrompt)((0, cli_patterns_1.stripAnsi)(output), promptOptions);
|
|
136
|
+
return (0, prompt_detector_1.detectPrompt)((0, cli_patterns_1.stripBoxDrawing)((0, cli_patterns_1.stripAnsi)(output)), promptOptions);
|
|
135
137
|
}
|
|
136
138
|
/**
|
|
137
139
|
* Clean up Claude response by removing shell setup commands, environment exports, ANSI codes, and banner
|
|
@@ -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
|
|
@@ -372,11 +380,12 @@ function extractResponse(output, lastCapturedLine, cliToolId) {
|
|
|
372
380
|
const hasPrompt = promptPattern.test(cleanOutputToCheck);
|
|
373
381
|
const hasSeparator = separatorPattern.test(cleanOutputToCheck);
|
|
374
382
|
const isThinking = thinkingPattern.test(cleanOutputToCheck);
|
|
375
|
-
// Codex/Gemini completion logic: prompt detected and not thinking (separator optional)
|
|
383
|
+
// Codex/Gemini/Vibe-Local completion logic: prompt detected and not thinking (separator optional)
|
|
376
384
|
// - Codex: Interactive TUI, detects › prompt
|
|
377
|
-
// - Gemini:
|
|
385
|
+
// - Gemini: Interactive REPL, detects > / ❯ prompt
|
|
386
|
+
// - Vibe-Local: Interactive REPL, detects > prompt
|
|
378
387
|
// Claude: require both prompt and separator
|
|
379
|
-
const isCodexOrGeminiComplete = (cliToolId === 'codex' || cliToolId === 'gemini') && hasPrompt && !isThinking;
|
|
388
|
+
const isCodexOrGeminiComplete = (cliToolId === 'codex' || cliToolId === 'gemini' || cliToolId === 'vibe-local') && hasPrompt && !isThinking;
|
|
380
389
|
const isClaudeComplete = cliToolId === 'claude' && hasPrompt && hasSeparator && !isThinking;
|
|
381
390
|
if (isCodexOrGeminiComplete || isClaudeComplete) {
|
|
382
391
|
// CLI tool has completed response
|
|
@@ -465,6 +474,7 @@ function extractResponse(output, lastCapturedLine, cliToolId) {
|
|
|
465
474
|
response,
|
|
466
475
|
isComplete: true,
|
|
467
476
|
lineCount: endIndex, // Use endIndex instead of totalLines to track where we actually stopped
|
|
477
|
+
bufferReset,
|
|
468
478
|
};
|
|
469
479
|
}
|
|
470
480
|
// Check if this is an interactive prompt (yes/no or multiple choice)
|
|
@@ -474,7 +484,7 @@ function extractResponse(output, lastCapturedLine, cliToolId) {
|
|
|
474
484
|
if (promptDetection.isPrompt) {
|
|
475
485
|
// Prompt detection uses full buffer for accuracy, but return only lastCapturedLine onwards
|
|
476
486
|
// stripAnsi is applied inside buildPromptExtractionResult (Stage 4 MF-001: XSS risk mitigation)
|
|
477
|
-
return buildPromptExtractionResult(lines, lastCapturedLine, totalLines, bufferReset, cliToolId, findRecentUserPromptIndex);
|
|
487
|
+
return buildPromptExtractionResult(lines, lastCapturedLine, totalLines, bufferReset, cliToolId, findRecentUserPromptIndex, promptDetection);
|
|
478
488
|
}
|
|
479
489
|
// Not a prompt, but we may have a partial response in progress (even if Claude shows a spinner)
|
|
480
490
|
const responseLines = [];
|
|
@@ -524,6 +534,7 @@ async function checkForResponse(worktreeId, cliToolId) {
|
|
|
524
534
|
// Check if CLI tool session is running
|
|
525
535
|
const running = await (0, cli_session_1.isSessionRunning)(worktreeId, cliToolId);
|
|
526
536
|
if (!running) {
|
|
537
|
+
console.log(`[checkForResponse] Session not running for ${worktreeId} (${cliToolId}), stopping poller`);
|
|
527
538
|
stopPolling(worktreeId, cliToolId);
|
|
528
539
|
return false;
|
|
529
540
|
}
|
|
@@ -555,17 +566,23 @@ async function checkForResponse(worktreeId, cliToolId) {
|
|
|
555
566
|
}
|
|
556
567
|
// CRITICAL FIX: If lineCount == lastCapturedLine AND there's no in-progress message,
|
|
557
568
|
// this response has already been saved. Skip to prevent duplicates.
|
|
558
|
-
|
|
569
|
+
// Issue #372: Skip when buffer reset detected (TUI redraw may coincidentally match lineCount).
|
|
570
|
+
if (!result.bufferReset && result.lineCount === lastCapturedLine && !sessionState?.inProgressMessageId) {
|
|
559
571
|
return false;
|
|
560
572
|
}
|
|
561
573
|
// Additional duplicate prevention: check if savePendingAssistantResponse
|
|
562
|
-
// already saved this content by comparing line counts
|
|
563
|
-
|
|
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) {
|
|
564
578
|
console.log(`[checkForResponse] Already saved up to line ${lastCapturedLine}, skipping (result: ${result.lineCount})`);
|
|
565
579
|
return false;
|
|
566
580
|
}
|
|
567
|
-
// Response is complete! Check if it's a prompt
|
|
568
|
-
|
|
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);
|
|
569
586
|
if (promptDetection.isPrompt) {
|
|
570
587
|
// This is a prompt - save as prompt message
|
|
571
588
|
(0, db_1.clearInProgressMessageId)(db, worktreeId, cliToolId);
|
|
@@ -667,10 +684,15 @@ function startPolling(worktreeId, cliToolId) {
|
|
|
667
684
|
stopPolling(worktreeId, cliToolId);
|
|
668
685
|
// Record start time
|
|
669
686
|
pollingStartTimes.set(pollerKey, Date.now());
|
|
670
|
-
// Start polling
|
|
671
|
-
|
|
672
|
-
|
|
687
|
+
// Start polling with setTimeout chain to prevent race conditions
|
|
688
|
+
scheduleNextResponsePoll(worktreeId, cliToolId);
|
|
689
|
+
}
|
|
690
|
+
/** Schedule next checkForResponse() after current one completes (setTimeout chain) */
|
|
691
|
+
function scheduleNextResponsePoll(worktreeId, cliToolId) {
|
|
692
|
+
const pollerKey = getPollerKey(worktreeId, cliToolId);
|
|
693
|
+
const timerId = setTimeout(async () => {
|
|
673
694
|
// Check if max duration exceeded
|
|
695
|
+
const startTime = pollingStartTimes.get(pollerKey);
|
|
674
696
|
if (startTime && Date.now() - startTime > MAX_POLLING_DURATION) {
|
|
675
697
|
stopPolling(worktreeId, cliToolId);
|
|
676
698
|
return;
|
|
@@ -682,8 +704,13 @@ function startPolling(worktreeId, cliToolId) {
|
|
|
682
704
|
catch (error) {
|
|
683
705
|
console.error(`[Poller] Error:`, error);
|
|
684
706
|
}
|
|
707
|
+
// Schedule next poll ONLY after current one completes
|
|
708
|
+
// Guard: only if poller is still active (not stopped during checkForResponse)
|
|
709
|
+
if (activePollers.has(pollerKey)) {
|
|
710
|
+
scheduleNextResponsePoll(worktreeId, cliToolId);
|
|
711
|
+
}
|
|
685
712
|
}, POLLING_INTERVAL);
|
|
686
|
-
activePollers.set(pollerKey,
|
|
713
|
+
activePollers.set(pollerKey, timerId);
|
|
687
714
|
}
|
|
688
715
|
/**
|
|
689
716
|
* Stop polling for a worktree and CLI tool combination
|
|
@@ -698,9 +725,9 @@ function startPolling(worktreeId, cliToolId) {
|
|
|
698
725
|
*/
|
|
699
726
|
function stopPolling(worktreeId, cliToolId) {
|
|
700
727
|
const pollerKey = getPollerKey(worktreeId, cliToolId);
|
|
701
|
-
const
|
|
702
|
-
if (
|
|
703
|
-
|
|
728
|
+
const timerId = activePollers.get(pollerKey);
|
|
729
|
+
if (timerId) {
|
|
730
|
+
clearTimeout(timerId);
|
|
704
731
|
activePollers.delete(pollerKey);
|
|
705
732
|
pollingStartTimes.delete(pollerKey);
|
|
706
733
|
}
|
|
@@ -220,12 +220,16 @@ async function executeSchedule(state) {
|
|
|
220
220
|
const logId = createExecutionLog(state.scheduleId, state.worktreeId, state.entry.message);
|
|
221
221
|
try {
|
|
222
222
|
const db = getLazyDbInstance();
|
|
223
|
-
const worktree = db.prepare('SELECT path FROM worktrees WHERE id = ?').get(state.worktreeId);
|
|
223
|
+
const worktree = db.prepare('SELECT path, vibe_local_model FROM worktrees WHERE id = ?').get(state.worktreeId);
|
|
224
224
|
if (!worktree) {
|
|
225
225
|
updateExecutionLog(logId, 'failed', 'Worktree not found', null);
|
|
226
226
|
return;
|
|
227
227
|
}
|
|
228
|
-
|
|
228
|
+
// Build options for vibe-local model
|
|
229
|
+
const options = state.entry.cliToolId === 'vibe-local' && worktree.vibe_local_model
|
|
230
|
+
? { model: worktree.vibe_local_model }
|
|
231
|
+
: undefined;
|
|
232
|
+
const result = await (0, claude_executor_1.executeClaudeCommand)(state.entry.message, worktree.path, state.entry.cliToolId, state.entry.permission, options);
|
|
229
233
|
updateExecutionLog(logId, result.status, result.output, result.exitCode);
|
|
230
234
|
updateScheduleLastExecuted(state.scheduleId);
|
|
231
235
|
console.log(`[schedule-manager] Executed ${state.entry.name}: ${result.status}`);
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Selected Agents Validator
|
|
4
|
+
* Issue #368: Validation and parsing for worktree selected_agents field
|
|
5
|
+
*
|
|
6
|
+
* Provides:
|
|
7
|
+
* - validateAgentsPair(): Core validation logic (R1-001)
|
|
8
|
+
* - parseSelectedAgents(): DB read with fallback + console.warn (R4-005 log sanitization)
|
|
9
|
+
* - validateSelectedAgentsInput(): API input validation
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.DEFAULT_SELECTED_AGENTS = void 0;
|
|
13
|
+
exports.validateAgentsPair = validateAgentsPair;
|
|
14
|
+
exports.parseSelectedAgents = parseSelectedAgents;
|
|
15
|
+
exports.validateSelectedAgentsInput = validateSelectedAgentsInput;
|
|
16
|
+
const types_1 = require("./cli-tools/types");
|
|
17
|
+
/**
|
|
18
|
+
* ANSI escape code pattern for log sanitization (R4-005)
|
|
19
|
+
* Duplicated from cli-patterns.ts to avoid importing server-side logger chain
|
|
20
|
+
*/
|
|
21
|
+
const ANSI_PATTERN = /\x1b\[[0-9;]*[a-zA-Z]|\x1b\][^\x07]*\x07|\[[0-9;]*m/g;
|
|
22
|
+
/**
|
|
23
|
+
* Strip ANSI escape codes from a string
|
|
24
|
+
*/
|
|
25
|
+
function stripAnsi(str) {
|
|
26
|
+
return str.replace(ANSI_PATTERN, '');
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Sanitize raw DB value for safe console.warn output (R4-005)
|
|
30
|
+
* Removes ANSI escapes, newlines, and truncates to 100 chars
|
|
31
|
+
*/
|
|
32
|
+
function sanitizeRawForLog(raw) {
|
|
33
|
+
return stripAnsi(raw).replace(/[\n\r]/g, ' ').substring(0, 100);
|
|
34
|
+
}
|
|
35
|
+
/** Default selected agents when DB value is missing or invalid */
|
|
36
|
+
exports.DEFAULT_SELECTED_AGENTS = ['claude', 'codex'];
|
|
37
|
+
/**
|
|
38
|
+
* Core validation function for a pair of CLI tool IDs (R1-001)
|
|
39
|
+
* Shared between parseSelectedAgents() and validateSelectedAgentsInput()
|
|
40
|
+
*
|
|
41
|
+
* @param input - Array of values to validate
|
|
42
|
+
* @returns Validation result with optional typed value or error message
|
|
43
|
+
*/
|
|
44
|
+
function validateAgentsPair(input) {
|
|
45
|
+
if (input.length !== 2) {
|
|
46
|
+
return { valid: false, error: 'Must be 2 elements' };
|
|
47
|
+
}
|
|
48
|
+
if (!input.every(id => typeof id === 'string' && types_1.CLI_TOOL_IDS.includes(id))) {
|
|
49
|
+
return { valid: false, error: 'Invalid CLI tool ID' };
|
|
50
|
+
}
|
|
51
|
+
if (input[0] === input[1]) {
|
|
52
|
+
return { valid: false, error: 'Duplicate tool IDs not allowed' };
|
|
53
|
+
}
|
|
54
|
+
return { valid: true, value: input };
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Parse selected_agents JSON from DB with safe fallback
|
|
58
|
+
* Returns default value for any invalid input (never throws)
|
|
59
|
+
*
|
|
60
|
+
* Logs a warning when fallback is triggered to help detect DB data issues.
|
|
61
|
+
* Log output is sanitized (R4-005): ANSI stripped, newlines removed, truncated.
|
|
62
|
+
*
|
|
63
|
+
* @param raw - Raw JSON string from DB (or null)
|
|
64
|
+
* @returns Validated tuple of 2 CLIToolType values
|
|
65
|
+
*/
|
|
66
|
+
function parseSelectedAgents(raw) {
|
|
67
|
+
if (!raw)
|
|
68
|
+
return exports.DEFAULT_SELECTED_AGENTS;
|
|
69
|
+
try {
|
|
70
|
+
const parsed = JSON.parse(raw);
|
|
71
|
+
if (!Array.isArray(parsed)) {
|
|
72
|
+
console.warn(`[selected-agents] Invalid format in DB, falling back to default: ${sanitizeRawForLog(raw)}`);
|
|
73
|
+
return exports.DEFAULT_SELECTED_AGENTS;
|
|
74
|
+
}
|
|
75
|
+
const result = validateAgentsPair(parsed);
|
|
76
|
+
if (!result.valid) {
|
|
77
|
+
console.warn(`[selected-agents] Invalid data in DB (${result.error}), falling back to default: ${sanitizeRawForLog(raw)}`);
|
|
78
|
+
return exports.DEFAULT_SELECTED_AGENTS;
|
|
79
|
+
}
|
|
80
|
+
return result.value;
|
|
81
|
+
}
|
|
82
|
+
catch {
|
|
83
|
+
console.warn(`[selected-agents] JSON parse error in DB, falling back to default: ${sanitizeRawForLog(raw)}`);
|
|
84
|
+
return exports.DEFAULT_SELECTED_AGENTS;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Validate selectedAgents input from API request body
|
|
89
|
+
* Returns structured error for API error responses (does not fallback)
|
|
90
|
+
*
|
|
91
|
+
* @param input - Raw input from request body (unknown type for safety)
|
|
92
|
+
* @returns Validation result with typed value or error string
|
|
93
|
+
*/
|
|
94
|
+
function validateSelectedAgentsInput(input) {
|
|
95
|
+
if (!Array.isArray(input) || input.length !== 2) {
|
|
96
|
+
return { valid: false, error: 'selected_agents must be an array of 2 elements' };
|
|
97
|
+
}
|
|
98
|
+
return validateAgentsPair(input);
|
|
99
|
+
}
|
|
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
8
8
|
exports.deriveCliStatus = deriveCliStatus;
|
|
9
9
|
exports.calculateHasUnread = calculateHasUnread;
|
|
10
10
|
exports.toBranchItem = toBranchItem;
|
|
11
|
+
const selected_agents_validator_1 = require("../lib/selected-agents-validator");
|
|
11
12
|
/**
|
|
12
13
|
* Derive BranchStatus from per-CLI tool session status flags.
|
|
13
14
|
* Shared by sidebar (toBranchItem) and WorktreeDetailRefactored tab dots.
|
|
@@ -58,6 +59,13 @@ function toBranchItem(worktree) {
|
|
|
58
59
|
const status = 'idle';
|
|
59
60
|
// Use new hasUnread logic based on lastAssistantMessageAt and lastViewedAt
|
|
60
61
|
const hasUnread = calculateHasUnread(worktree);
|
|
62
|
+
// Issue #368: Use selectedAgents to determine which tools to show status for
|
|
63
|
+
// Falls back to DEFAULT_SELECTED_AGENTS when selectedAgents is not set
|
|
64
|
+
const agents = worktree.selectedAgents ?? selected_agents_validator_1.DEFAULT_SELECTED_AGENTS;
|
|
65
|
+
const cliStatus = {};
|
|
66
|
+
for (const agent of agents) {
|
|
67
|
+
cliStatus[agent] = deriveCliStatus(worktree.sessionStatusByCli?.[agent]);
|
|
68
|
+
}
|
|
61
69
|
return {
|
|
62
70
|
id: worktree.id,
|
|
63
71
|
name: worktree.name,
|
|
@@ -66,9 +74,6 @@ function toBranchItem(worktree) {
|
|
|
66
74
|
hasUnread,
|
|
67
75
|
lastActivity: worktree.updatedAt,
|
|
68
76
|
description: worktree.description,
|
|
69
|
-
cliStatus
|
|
70
|
-
claude: deriveCliStatus(worktree.sessionStatusByCli?.claude),
|
|
71
|
-
codex: deriveCliStatus(worktree.sessionStatusByCli?.codex),
|
|
72
|
-
},
|
|
77
|
+
cliStatus,
|
|
73
78
|
};
|
|
74
79
|
}
|
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
|
}
|