Skip to content

Commit ecf1f82

Browse files
committed
Track all claude session ids and fall back if not found
1 parent af814a0 commit ecf1f82

File tree

8 files changed

+71
-2
lines changed

8 files changed

+71
-2
lines changed

apps/desktop/src/lib/codegen/types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ export interface ClaudeSession {
7171
id: string;
7272
/** The most recent session ID. If a session is stopped and resumed, Claude will copy over the past context into a new session. This value is unique. */
7373
currentId: string;
74+
/** All session IDs that have been used for this session, including the current one. */
75+
sessionIds: string[];
7476
/** The timestamp when the first session was created. */
7577
createdAt: string;
7678
/** The timestamp when the session was last updated. */

crates/but-claude/src/bridge.rs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020
//! more complex with more unknowns.
2121
2222
use crate::{
23-
ClaudeMessage, ClaudeMessageContent, UserInput,
23+
ClaudeMessage, ClaudeMessageContent, Transcript, UserInput,
2424
claude_config::{fmt_claude_mcp, fmt_claude_settings},
2525
db,
2626
rules::{create_claude_assignment_rule, list_claude_assignment_rules},
@@ -32,6 +32,7 @@ use gitbutler_command_context::CommandContext;
3232
use serde_json::json;
3333
use std::{
3434
collections::HashMap,
35+
fs,
3536
io::{BufRead, BufReader, PipeReader, Read as _},
3637
process::ExitStatus,
3738
sync::Arc,
@@ -296,7 +297,19 @@ async fn spawn_command(
296297
if create_new {
297298
command.arg(format!("--session-id={}", session.id));
298299
} else {
299-
command.arg(format!("--resume={}", session.current_id));
300+
let mut session_ids = session.session_ids.clone();
301+
let mut current_id = session_ids.pop().unwrap_or(session.current_id);
302+
303+
while !session_ids.is_empty()
304+
&& !dbg!(fs::exists(Transcript::get_transcript_path(
305+
&project_path,
306+
current_id
307+
)?)?)
308+
{
309+
current_id = session_ids.pop().unwrap_or(session.current_id);
310+
}
311+
312+
command.arg(format!("--resume={}", current_id));
300313
}
301314
command.arg(message);
302315
Ok(command.spawn()?)

crates/but-claude/src/db.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ pub fn save_new_session_with_gui_flag(
1919
let session = ClaudeSession {
2020
id,
2121
current_id: id,
22+
session_ids: vec![id],
2223
created_at: now,
2324
updated_at: now,
2425
in_gui,
@@ -41,6 +42,30 @@ pub fn set_session_current_id(
4142
Ok(())
4243
}
4344

45+
/// Adds a session ID to the list of session IDs for a given session.
46+
pub fn add_session_id(
47+
ctx: &mut CommandContext,
48+
session_id: Uuid,
49+
new_session_id: Uuid,
50+
) -> anyhow::Result<()> {
51+
if let Some(mut session) = get_session_by_id(ctx, session_id)? {
52+
if !session.session_ids.contains(&new_session_id) {
53+
session.session_ids.push(new_session_id);
54+
session.current_id = new_session_id;
55+
56+
let json = serde_json::to_string(&session.session_ids)?;
57+
58+
ctx.db()?
59+
.claude_sessions()
60+
.update_session_ids(&session_id.to_string(), &json)?;
61+
ctx.db()?
62+
.claude_sessions()
63+
.update_current_id(&session_id.to_string(), &new_session_id.to_string())?;
64+
}
65+
}
66+
Ok(())
67+
}
68+
4469
/// Updates the current session ID for a given session in the database.
4570
pub fn set_session_in_gui(
4671
ctx: &mut CommandContext,
@@ -157,9 +182,11 @@ pub fn update_permission_request(
157182
impl TryFrom<but_db::ClaudeSession> for crate::ClaudeSession {
158183
type Error = anyhow::Error;
159184
fn try_from(value: but_db::ClaudeSession) -> Result<Self, Self::Error> {
185+
let session_ids: Vec<Uuid> = serde_json::from_str(&value.session_ids)?;
160186
Ok(crate::ClaudeSession {
161187
id: Uuid::parse_str(&value.id)?,
162188
current_id: Uuid::parse_str(&value.current_id)?,
189+
session_ids,
163190
created_at: value.created_at,
164191
updated_at: value.updated_at,
165192
in_gui: value.in_gui,
@@ -170,9 +197,11 @@ impl TryFrom<but_db::ClaudeSession> for crate::ClaudeSession {
170197
impl TryFrom<crate::ClaudeSession> for but_db::ClaudeSession {
171198
type Error = anyhow::Error;
172199
fn try_from(value: crate::ClaudeSession) -> Result<Self, Self::Error> {
200+
let session_ids = serde_json::to_string(&value.session_ids)?;
173201
Ok(but_db::ClaudeSession {
174202
id: value.id.to_string(),
175203
current_id: value.current_id.to_string(),
204+
session_ids,
176205
created_at: value.created_at,
177206
updated_at: value.updated_at,
178207
in_gui: value.in_gui,

crates/but-claude/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ pub struct ClaudeSession {
1919
id: Uuid,
2020
/// The most recent session ID. If a session is stopped and resumed, Claude will copy over the past context into a new session. This value is unique.
2121
current_id: Uuid,
22+
/// All session IDs that have been used for this session, including the current one.
23+
session_ids: Vec<Uuid>,
2224
/// The timestamp when the first session was created.
2325
created_at: chrono::NaiveDateTime,
2426
/// The timestamp when the session was last updated.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
-- Remove session_ids column from claude_sessions table
2+
ALTER TABLE claude_sessions DROP COLUMN session_ids;
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
-- Add session_ids column to claude_sessions table to track all session IDs used
2+
ALTER TABLE claude_sessions ADD COLUMN session_ids TEXT NOT NULL DEFAULT '[]';
3+
4+
-- Initialize existing sessions with their current_id in the session_ids array
5+
UPDATE claude_sessions SET session_ids = json_array(current_id);

crates/but-db/src/claude.rs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ use serde::{Deserialize, Serialize};
1616
pub struct ClaudeSession {
1717
pub id: String,
1818
pub current_id: String,
19+
pub session_ids: String,
1920
pub created_at: chrono::NaiveDateTime,
2021
pub updated_at: chrono::NaiveDateTime,
2122
pub in_gui: bool,
@@ -177,6 +178,20 @@ impl ClaudeSessionsHandle<'_> {
177178
Ok(())
178179
}
179180

181+
pub fn update_session_ids(
182+
&mut self,
183+
id: &str,
184+
session_ids: &str,
185+
) -> Result<(), diesel::result::Error> {
186+
diesel::update(claude_sessions.filter(crate::schema::claude_sessions::id.eq(id)))
187+
.set((
188+
crate::schema::claude_sessions::session_ids.eq(session_ids),
189+
crate::schema::claude_sessions::updated_at.eq(chrono::Local::now().naive_local()),
190+
))
191+
.execute(&mut self.db.conn)?;
192+
Ok(())
193+
}
194+
180195
pub fn update_in_gui(&mut self, id: &str, in_gui: bool) -> Result<(), diesel::result::Error> {
181196
diesel::update(claude_sessions.filter(crate::schema::claude_sessions::id.eq(id)))
182197
.set((

crates/but-db/src/schema.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ diesel::table! {
5959
claude_sessions (id) {
6060
id -> Text,
6161
current_id -> Text,
62+
session_ids -> Text,
6263
created_at -> Timestamp,
6364
updated_at -> Timestamp,
6465
in_gui -> Bool,

0 commit comments

Comments
 (0)