handoff-mcp-server 0.24.5 → 0.24.6

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/Cargo.lock CHANGED
@@ -145,7 +145,7 @@ dependencies = [
145
145
 
146
146
  [[package]]
147
147
  name = "handoff-mcp"
148
- version = "0.24.5"
148
+ version = "0.24.6"
149
149
  dependencies = [
150
150
  "anyhow",
151
151
  "chrono",
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "handoff-mcp"
3
- version = "0.24.5"
3
+ version = "0.24.6"
4
4
  edition = "2021"
5
5
  description = "MCP server that gives AI coding agents persistent memory across sessions"
6
6
  license = "MIT"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "handoff-mcp-server",
3
- "version": "0.24.5",
3
+ "version": "0.24.6",
4
4
  "description": "MCP server that gives AI coding agents persistent memory across sessions",
5
5
  "license": "MIT",
6
6
  "author": "AlphaElements <66808803+alphaelements@users.noreply.github.com>",
@@ -130,11 +130,16 @@ pub fn handle(arguments: &Value) -> Result<String> {
130
130
  if let Some(active_session) = target {
131
131
  let sid = active_session.id.clone().unwrap_or_default();
132
132
  if keep_active {
133
- let updated_path = update_active_session(&sessions_dir, &sid, &handoff_updates)?;
133
+ let updated_path =
134
+ update_active_session(&sessions_dir, &sid, &handoff_updates, Some(arguments))?;
134
135
  (0, updated_path, Some(sid))
135
136
  } else {
136
- let closed_path =
137
- update_and_close_active_session(&sessions_dir, &sid, &handoff_updates)?;
137
+ let closed_path = update_and_close_active_session(
138
+ &sessions_dir,
139
+ &sid,
140
+ &handoff_updates,
141
+ Some(arguments),
142
+ )?;
138
143
  (
139
144
  if closed_path.is_some() { 1 } else { 0 },
140
145
  closed_path,
@@ -153,11 +153,13 @@ pub fn handle(arguments: &Value) -> Result<String> {
153
153
  Ok(msg)
154
154
  }
155
155
 
156
- fn truncate(s: &str, max: usize) -> String {
157
- if s.len() <= max {
158
- s.to_string()
156
+ fn truncate(s: &str, max_chars: usize) -> String {
157
+ let mut chars = s.chars();
158
+ let truncated: String = chars.by_ref().take(max_chars).collect();
159
+ if chars.next().is_some() {
160
+ format!("{truncated}...")
159
161
  } else {
160
- format!("{}...", &s[..max])
162
+ truncated
161
163
  }
162
164
  }
163
165
 
@@ -384,18 +384,37 @@ pub fn read_latest_closed_session(sessions_dir: &Path) -> Result<Option<SessionD
384
384
  Ok(sessions.into_iter().last())
385
385
  }
386
386
 
387
- fn apply_session_updates(data: &mut SessionData, updates: &SessionData) {
387
+ fn apply_session_updates(
388
+ data: &mut SessionData,
389
+ updates: &SessionData,
390
+ provided_keys: Option<&Value>,
391
+ ) {
388
392
  data.summary = updates.summary.clone();
389
393
  data.ended_at = updates.ended_at.clone();
390
394
  data.branch = updates.branch.clone();
391
395
  data.commit = updates.commit.clone();
392
396
  data.dirty_files = updates.dirty_files.clone();
393
- data.decisions = updates.decisions.clone();
394
- data.blockers = updates.blockers.clone();
395
- data.checklist = updates.checklist.clone();
396
- data.handoff_notes = updates.handoff_notes.clone();
397
- data.references = updates.references.clone();
398
- data.context_pointers = updates.context_pointers.clone();
397
+
398
+ let was_provided = |key: &str| provided_keys.and_then(|v| v.get(key)).is_some();
399
+
400
+ if was_provided("decisions") {
401
+ data.decisions = updates.decisions.clone();
402
+ }
403
+ if was_provided("blockers") {
404
+ data.blockers = updates.blockers.clone();
405
+ }
406
+ if was_provided("checklist") {
407
+ data.checklist = updates.checklist.clone();
408
+ }
409
+ if was_provided("handoff_notes") {
410
+ data.handoff_notes = updates.handoff_notes.clone();
411
+ }
412
+ if was_provided("references") {
413
+ data.references = updates.references.clone();
414
+ }
415
+ if was_provided("context_pointers") {
416
+ data.context_pointers = updates.context_pointers.clone();
417
+ }
399
418
  if updates.environment.is_some() {
400
419
  data.environment = updates.environment.clone();
401
420
  }
@@ -405,7 +424,7 @@ fn apply_session_updates(data: &mut SessionData, updates: &SessionData) {
405
424
  if updates.label.is_some() {
406
425
  data.label = updates.label.clone();
407
426
  }
408
- if !updates.related_task_ids.is_empty() {
427
+ if was_provided("related_task_ids") && !updates.related_task_ids.is_empty() {
409
428
  data.related_task_ids = updates.related_task_ids.clone();
410
429
  }
411
430
  }
@@ -415,6 +434,7 @@ fn find_and_update_active_session(
415
434
  session_id: &str,
416
435
  updates: &SessionData,
417
436
  transition_to: Option<&str>,
437
+ provided_keys: Option<&Value>,
418
438
  ) -> Result<Option<PathBuf>> {
419
439
  let suffix = ".active.json";
420
440
 
@@ -464,7 +484,7 @@ fn find_and_update_active_session(
464
484
  let mut data: SessionData = serde_json::from_str(&content)
465
485
  .with_context(|| format!("Failed to parse session: {}", path.display()))?;
466
486
 
467
- apply_session_updates(&mut data, updates);
487
+ apply_session_updates(&mut data, updates, provided_keys);
468
488
 
469
489
  let updated_content =
470
490
  serde_json::to_string_pretty(&data).context("Failed to serialize session")?;
@@ -495,16 +515,24 @@ pub fn update_and_close_active_session(
495
515
  sessions_dir: &Path,
496
516
  session_id: &str,
497
517
  updates: &SessionData,
518
+ provided_keys: Option<&Value>,
498
519
  ) -> Result<Option<PathBuf>> {
499
- find_and_update_active_session(sessions_dir, session_id, updates, Some("closed"))
520
+ find_and_update_active_session(
521
+ sessions_dir,
522
+ session_id,
523
+ updates,
524
+ Some("closed"),
525
+ provided_keys,
526
+ )
500
527
  }
501
528
 
502
529
  pub fn update_active_session(
503
530
  sessions_dir: &Path,
504
531
  session_id: &str,
505
532
  updates: &SessionData,
533
+ provided_keys: Option<&Value>,
506
534
  ) -> Result<Option<PathBuf>> {
507
- find_and_update_active_session(sessions_dir, session_id, updates, None)
535
+ find_and_update_active_session(sessions_dir, session_id, updates, None, provided_keys)
508
536
  }
509
537
 
510
538
  pub fn read_session_by_id(sessions_dir: &Path, session_id: &str) -> Result<Option<SessionData>> {