handoff-mcp-server 0.13.0 → 0.15.0

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
@@ -13,9 +13,9 @@ dependencies = [
13
13
 
14
14
  [[package]]
15
15
  name = "anyhow"
16
- version = "1.0.102"
16
+ version = "1.0.103"
17
17
  source = "registry+https://github.com/rust-lang/crates.io-index"
18
- checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
18
+ checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3"
19
19
 
20
20
  [[package]]
21
21
  name = "autocfg"
@@ -37,9 +37,9 @@ checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
37
37
 
38
38
  [[package]]
39
39
  name = "cc"
40
- version = "1.2.64"
40
+ version = "1.2.65"
41
41
  source = "registry+https://github.com/rust-lang/crates.io-index"
42
- checksum = "dad887fd958be91b5098c0248def011f4523ab786cd411be668777e55063501f"
42
+ checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96"
43
43
  dependencies = [
44
44
  "find-msvc-tools",
45
45
  "shlex",
@@ -99,12 +99,6 @@ version = "0.1.9"
99
99
  source = "registry+https://github.com/rust-lang/crates.io-index"
100
100
  checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
101
101
 
102
- [[package]]
103
- name = "foldhash"
104
- version = "0.1.5"
105
- source = "registry+https://github.com/rust-lang/crates.io-index"
106
- checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
107
-
108
102
  [[package]]
109
103
  name = "futures-core"
110
104
  version = "0.3.32"
@@ -131,20 +125,18 @@ dependencies = [
131
125
 
132
126
  [[package]]
133
127
  name = "getrandom"
134
- version = "0.4.2"
128
+ version = "0.4.3"
135
129
  source = "registry+https://github.com/rust-lang/crates.io-index"
136
- checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
130
+ checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
137
131
  dependencies = [
138
132
  "cfg-if",
139
133
  "libc",
140
134
  "r-efi",
141
- "wasip2",
142
- "wasip3",
143
135
  ]
144
136
 
145
137
  [[package]]
146
138
  name = "handoff-mcp"
147
- version = "0.13.0"
139
+ version = "0.15.0"
148
140
  dependencies = [
149
141
  "anyhow",
150
142
  "chrono",
@@ -157,27 +149,12 @@ dependencies = [
157
149
  "toml_edit",
158
150
  ]
159
151
 
160
- [[package]]
161
- name = "hashbrown"
162
- version = "0.15.5"
163
- source = "registry+https://github.com/rust-lang/crates.io-index"
164
- checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
165
- dependencies = [
166
- "foldhash",
167
- ]
168
-
169
152
  [[package]]
170
153
  name = "hashbrown"
171
154
  version = "0.17.1"
172
155
  source = "registry+https://github.com/rust-lang/crates.io-index"
173
156
  checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
174
157
 
175
- [[package]]
176
- name = "heck"
177
- version = "0.5.0"
178
- source = "registry+https://github.com/rust-lang/crates.io-index"
179
- checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
180
-
181
158
  [[package]]
182
159
  name = "iana-time-zone"
183
160
  version = "0.1.65"
@@ -202,12 +179,6 @@ dependencies = [
202
179
  "cc",
203
180
  ]
204
181
 
205
- [[package]]
206
- name = "id-arena"
207
- version = "2.3.0"
208
- source = "registry+https://github.com/rust-lang/crates.io-index"
209
- checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
210
-
211
182
  [[package]]
212
183
  name = "indexmap"
213
184
  version = "2.14.0"
@@ -215,9 +186,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
215
186
  checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
216
187
  dependencies = [
217
188
  "equivalent",
218
- "hashbrown 0.17.1",
219
- "serde",
220
- "serde_core",
189
+ "hashbrown",
221
190
  ]
222
191
 
223
192
  [[package]]
@@ -228,21 +197,15 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
228
197
 
229
198
  [[package]]
230
199
  name = "js-sys"
231
- version = "0.3.102"
200
+ version = "0.3.103"
232
201
  source = "registry+https://github.com/rust-lang/crates.io-index"
233
- checksum = "03d04c30968dffe80775bd4d7fb676131cd04a1fb46d2686dbffbaec2d9dfd31"
202
+ checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102"
234
203
  dependencies = [
235
204
  "cfg-if",
236
205
  "futures-util",
237
206
  "wasm-bindgen",
238
207
  ]
239
208
 
240
- [[package]]
241
- name = "leb128fmt"
242
- version = "0.1.0"
243
- source = "registry+https://github.com/rust-lang/crates.io-index"
244
- checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
245
-
246
209
  [[package]]
247
210
  name = "lexsim"
248
211
  version = "0.1.0"
@@ -267,9 +230,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
267
230
 
268
231
  [[package]]
269
232
  name = "log"
270
- version = "0.4.32"
233
+ version = "0.4.33"
271
234
  source = "registry+https://github.com/rust-lang/crates.io-index"
272
- checksum = "953f07c43838f8e6f9758cab68bf5bed85465e7587ebe0b823f1bcd81978ad3a"
235
+ checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad"
273
236
 
274
237
  [[package]]
275
238
  name = "memchr"
@@ -298,16 +261,6 @@ version = "0.2.17"
298
261
  source = "registry+https://github.com/rust-lang/crates.io-index"
299
262
  checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
300
263
 
301
- [[package]]
302
- name = "prettyplease"
303
- version = "0.2.37"
304
- source = "registry+https://github.com/rust-lang/crates.io-index"
305
- checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
306
- dependencies = [
307
- "proc-macro2",
308
- "syn",
309
- ]
310
-
311
264
  [[package]]
312
265
  name = "proc-macro2"
313
266
  version = "1.0.106"
@@ -319,9 +272,9 @@ dependencies = [
319
272
 
320
273
  [[package]]
321
274
  name = "quote"
322
- version = "1.0.45"
275
+ version = "1.0.46"
323
276
  source = "registry+https://github.com/rust-lang/crates.io-index"
324
- checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
277
+ checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368"
325
278
  dependencies = [
326
279
  "proc-macro2",
327
280
  ]
@@ -351,12 +304,6 @@ version = "1.0.22"
351
304
  source = "registry+https://github.com/rust-lang/crates.io-index"
352
305
  checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
353
306
 
354
- [[package]]
355
- name = "semver"
356
- version = "1.0.28"
357
- source = "registry+https://github.com/rust-lang/crates.io-index"
358
- checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
359
-
360
307
  [[package]]
361
308
  name = "serde"
362
309
  version = "1.0.228"
@@ -393,6 +340,7 @@ version = "1.0.150"
393
340
  source = "registry+https://github.com/rust-lang/crates.io-index"
394
341
  checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
395
342
  dependencies = [
343
+ "indexmap",
396
344
  "itoa",
397
345
  "memchr",
398
346
  "serde",
@@ -423,9 +371,9 @@ checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
423
371
 
424
372
  [[package]]
425
373
  name = "syn"
426
- version = "2.0.117"
374
+ version = "2.0.118"
427
375
  source = "registry+https://github.com/rust-lang/crates.io-index"
428
- checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
376
+ checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422"
429
377
  dependencies = [
430
378
  "proc-macro2",
431
379
  "quote",
@@ -542,35 +490,11 @@ version = "1.13.3"
542
490
  source = "registry+https://github.com/rust-lang/crates.io-index"
543
491
  checksum = "c6f5d3c3b1bf09027a88a6bc961fc00497d651009560b5463668dc81b0fa87a8"
544
492
 
545
- [[package]]
546
- name = "unicode-xid"
547
- version = "0.2.6"
548
- source = "registry+https://github.com/rust-lang/crates.io-index"
549
- checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
550
-
551
- [[package]]
552
- name = "wasip2"
553
- version = "1.0.4+wasi-0.2.12"
554
- source = "registry+https://github.com/rust-lang/crates.io-index"
555
- checksum = "b67efb37e106e55ce722a510d6b5f9c17f083e5fc79afc2badeb12cc313d9487"
556
- dependencies = [
557
- "wit-bindgen 0.57.1",
558
- ]
559
-
560
- [[package]]
561
- name = "wasip3"
562
- version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
563
- source = "registry+https://github.com/rust-lang/crates.io-index"
564
- checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
565
- dependencies = [
566
- "wit-bindgen 0.51.0",
567
- ]
568
-
569
493
  [[package]]
570
494
  name = "wasm-bindgen"
571
- version = "0.2.125"
495
+ version = "0.2.126"
572
496
  source = "registry+https://github.com/rust-lang/crates.io-index"
573
- checksum = "8ddb3f79143bced6de84270411622a2699cee572fc0875aeaf1e7867cf9fca1a"
497
+ checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4"
574
498
  dependencies = [
575
499
  "cfg-if",
576
500
  "once_cell",
@@ -581,9 +505,9 @@ dependencies = [
581
505
 
582
506
  [[package]]
583
507
  name = "wasm-bindgen-macro"
584
- version = "0.2.125"
508
+ version = "0.2.126"
585
509
  source = "registry+https://github.com/rust-lang/crates.io-index"
586
- checksum = "4e21a184b13fb19e157296e2c46056aec9092264fab83e4ba59e68c61b323c3d"
510
+ checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1"
587
511
  dependencies = [
588
512
  "quote",
589
513
  "wasm-bindgen-macro-support",
@@ -591,9 +515,9 @@ dependencies = [
591
515
 
592
516
  [[package]]
593
517
  name = "wasm-bindgen-macro-support"
594
- version = "0.2.125"
518
+ version = "0.2.126"
595
519
  source = "registry+https://github.com/rust-lang/crates.io-index"
596
- checksum = "fecefd9c35bd935a20fc3fc344b5f29138961e4f47fb03297d88f2587afb5ebd"
520
+ checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e"
597
521
  dependencies = [
598
522
  "bumpalo",
599
523
  "proc-macro2",
@@ -604,47 +528,13 @@ dependencies = [
604
528
 
605
529
  [[package]]
606
530
  name = "wasm-bindgen-shared"
607
- version = "0.2.125"
531
+ version = "0.2.126"
608
532
  source = "registry+https://github.com/rust-lang/crates.io-index"
609
- checksum = "23939e44bb9a5d7576fa2b563dc2e136628f1224e88a8deed09e04858b77871f"
533
+ checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24"
610
534
  dependencies = [
611
535
  "unicode-ident",
612
536
  ]
613
537
 
614
- [[package]]
615
- name = "wasm-encoder"
616
- version = "0.244.0"
617
- source = "registry+https://github.com/rust-lang/crates.io-index"
618
- checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
619
- dependencies = [
620
- "leb128fmt",
621
- "wasmparser",
622
- ]
623
-
624
- [[package]]
625
- name = "wasm-metadata"
626
- version = "0.244.0"
627
- source = "registry+https://github.com/rust-lang/crates.io-index"
628
- checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
629
- dependencies = [
630
- "anyhow",
631
- "indexmap",
632
- "wasm-encoder",
633
- "wasmparser",
634
- ]
635
-
636
- [[package]]
637
- name = "wasmparser"
638
- version = "0.244.0"
639
- source = "registry+https://github.com/rust-lang/crates.io-index"
640
- checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
641
- dependencies = [
642
- "bitflags",
643
- "hashbrown 0.15.5",
644
- "indexmap",
645
- "semver",
646
- ]
647
-
648
538
  [[package]]
649
539
  name = "windows-core"
650
540
  version = "0.62.2"
@@ -722,100 +612,6 @@ dependencies = [
722
612
  "memchr",
723
613
  ]
724
614
 
725
- [[package]]
726
- name = "wit-bindgen"
727
- version = "0.51.0"
728
- source = "registry+https://github.com/rust-lang/crates.io-index"
729
- checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
730
- dependencies = [
731
- "wit-bindgen-rust-macro",
732
- ]
733
-
734
- [[package]]
735
- name = "wit-bindgen"
736
- version = "0.57.1"
737
- source = "registry+https://github.com/rust-lang/crates.io-index"
738
- checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
739
-
740
- [[package]]
741
- name = "wit-bindgen-core"
742
- version = "0.51.0"
743
- source = "registry+https://github.com/rust-lang/crates.io-index"
744
- checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
745
- dependencies = [
746
- "anyhow",
747
- "heck",
748
- "wit-parser",
749
- ]
750
-
751
- [[package]]
752
- name = "wit-bindgen-rust"
753
- version = "0.51.0"
754
- source = "registry+https://github.com/rust-lang/crates.io-index"
755
- checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
756
- dependencies = [
757
- "anyhow",
758
- "heck",
759
- "indexmap",
760
- "prettyplease",
761
- "syn",
762
- "wasm-metadata",
763
- "wit-bindgen-core",
764
- "wit-component",
765
- ]
766
-
767
- [[package]]
768
- name = "wit-bindgen-rust-macro"
769
- version = "0.51.0"
770
- source = "registry+https://github.com/rust-lang/crates.io-index"
771
- checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
772
- dependencies = [
773
- "anyhow",
774
- "prettyplease",
775
- "proc-macro2",
776
- "quote",
777
- "syn",
778
- "wit-bindgen-core",
779
- "wit-bindgen-rust",
780
- ]
781
-
782
- [[package]]
783
- name = "wit-component"
784
- version = "0.244.0"
785
- source = "registry+https://github.com/rust-lang/crates.io-index"
786
- checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
787
- dependencies = [
788
- "anyhow",
789
- "bitflags",
790
- "indexmap",
791
- "log",
792
- "serde",
793
- "serde_derive",
794
- "serde_json",
795
- "wasm-encoder",
796
- "wasm-metadata",
797
- "wasmparser",
798
- "wit-parser",
799
- ]
800
-
801
- [[package]]
802
- name = "wit-parser"
803
- version = "0.244.0"
804
- source = "registry+https://github.com/rust-lang/crates.io-index"
805
- checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
806
- dependencies = [
807
- "anyhow",
808
- "id-arena",
809
- "indexmap",
810
- "log",
811
- "semver",
812
- "serde",
813
- "serde_derive",
814
- "serde_json",
815
- "unicode-xid",
816
- "wasmparser",
817
- ]
818
-
819
615
  [[package]]
820
616
  name = "zmij"
821
617
  version = "1.0.21"
package/Cargo.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [package]
2
2
  name = "handoff-mcp"
3
- version = "0.13.0"
3
+ version = "0.15.0"
4
4
  edition = "2021"
5
5
  description = "MCP server that gives AI coding agents persistent memory across sessions"
6
6
  license = "MIT"
@@ -14,7 +14,7 @@ exclude = ["lefthook.yml", "tmp/", "wiki/", "docs/", ".claude/", ".vscode/"]
14
14
 
15
15
  [dependencies]
16
16
  serde = { version = "1", features = ["derive"] }
17
- serde_json = "1"
17
+ serde_json = { version = "1", features = ["preserve_order"] }
18
18
  toml = "0.8"
19
19
  toml_edit = "0.22"
20
20
  chrono = { version = "0.4", features = ["serde"] }
package/README.md CHANGED
@@ -33,12 +33,23 @@ At session start, the agent calls `handoff_load_context` to pick up where things
33
33
 
34
34
  ## Installation
35
35
 
36
- ### npm (recommended)
36
+ ### cargo (recommended if you have Rust)
37
+
38
+ ```bash
39
+ cargo install handoff-mcp
40
+ ```
41
+
42
+ ### npm
37
43
 
38
44
  ```bash
39
45
  npm install -g handoff-mcp-server
40
46
  ```
41
47
 
48
+ Both install the same binary. `cargo install` fetches the crate from
49
+ [crates.io](https://crates.io/crates/handoff-mcp) and compiles it directly;
50
+ `npm install` downloads the source and runs `cargo build --release` via
51
+ postinstall, so either way you need a Rust toolchain.
52
+
42
53
  ### Build from source
43
54
 
44
55
  ```bash
@@ -73,6 +84,34 @@ The `-s user` flag registers it globally (available in all projects). Verify wit
73
84
  }
74
85
  ```
75
86
 
87
+ ### Enable automatic memory injection (optional)
88
+
89
+ If you want project memories to be automatically injected into your context
90
+ (instead of calling `handoff_memory_query` manually every time), run:
91
+
92
+ ```bash
93
+ handoff-mcp setup
94
+ ```
95
+
96
+ This installs Claude Code hooks into `~/.claude/settings.json` that
97
+ automatically call `handoff_memory_query` on every prompt and file edit, and run
98
+ `handoff_memory_cleanup` at session start. Restart Claude Code after running setup.
99
+
100
+ You can check the current status or remove the hooks:
101
+
102
+ ```bash
103
+ handoff-mcp setup --check # Show hook status
104
+ handoff-mcp setup --uninstall # Remove handoff hooks
105
+ ```
106
+
107
+ The hooks fire on every prompt and file edit, which adds a small overhead per
108
+ interaction. If you want to stop automatic injection, run
109
+ `handoff-mcp setup --uninstall` — the memory tools themselves remain available
110
+ for manual use, only the automatic hooks are removed.
111
+
112
+ See [Automatic injection via hooks](#automatic-injection-via-hooks) for the
113
+ manual configuration alternative.
114
+
76
115
  ## Quick Start
77
116
 
78
117
  1. **Initialize** a project:
@@ -159,14 +198,22 @@ rename) so a concurrent reader never sees a partially-written file.
159
198
  | `handoff_get_referral` | Fetch one incoming referral in full — details, suggested tasks, done_criteria, context |
160
199
  | `handoff_update_referral` | Update referral status (open → acknowledged → resolved) |
161
200
 
201
+ ### Timer Coordination
202
+
203
+ | Tool | Purpose |
204
+ |------|---------|
205
+ | `handoff_timer_start` | Start tracking time for a task — delegates to VSCode extension if alive, otherwise starts MCP fallback timer |
206
+ | `handoff_timer_stop` | Stop the timer and log elapsed hours to `actual_hours` — delegates to VSCode if alive |
207
+ | `handoff_timer_get_time` | Get current timer state (elapsed, authority, projected total) without stopping |
208
+
162
209
  ### Project Memory
163
210
 
164
211
  | Tool | Purpose |
165
212
  |------|---------|
166
- | `memory_save` | Save a durable project memory (lesson/rule/convention/gotcha); detects exact and near-duplicate memories and hands near-duplicates back for AI-driven merge |
167
- | `memory_query` | Return the memories most relevant to the current prompt/file (BM25 + scope-path boost); with a `session_id`, suppresses repeats already injected this session |
168
- | `memory_delete` | Delete a memory by id (full id or unique prefix) |
169
- | `memory_cleanup` | Housekeeping (for SessionStart): silently merge exact duplicates, return near-duplicate/stale recommendations, gc old injection sidecars |
213
+ | `handoff_memory_save` | Save a durable project memory (lesson/rule/convention/gotcha); detects exact and near-duplicate memories and hands near-duplicates back for AI-driven merge |
214
+ | `handoff_memory_query` | Return the memories most relevant to the current prompt/file (BM25 + scope-path boost); with a `session_id`, suppresses repeats already injected this session |
215
+ | `handoff_memory_delete` | Delete a memory by id (full id or unique prefix) |
216
+ | `handoff_memory_cleanup` | Housekeeping (for SessionStart): silently merge exact duplicates, return near-duplicate/stale recommendations, gc old injection sidecars |
170
217
 
171
218
  See [Project Memory](#project-memory-1) below for what it is and how to wire automatic injection.
172
219
 
@@ -240,6 +287,9 @@ done_task_limit = 10 # Max completed tasks to show
240
287
  auto_git_summary = true # Capture git state automatically
241
288
  require_estimate_hours = true # Require estimate_hours on leaf tasks (default true)
242
289
  ai_estimate_multiplier = 0.2 # Multiplier turning human estimates into AI-effort hours
290
+ timer_provider = "auto" # "auto" | "vscode" | "mcp" | "off"
291
+ timer_authority_ttl_secs = 30 # Heartbeat freshness TTL for authority.json
292
+ timer_idle_timeout_minutes = 10 # Idle pause threshold for MCP fallback timer
243
293
 
244
294
  [dashboard]
245
295
  scan_dirs = ["~/pro/"] # Directories to scan for dashboard
@@ -300,6 +350,11 @@ Sessions answer *"what was I doing last time?"*. **Memory** answers a longer-liv
300
350
  question: *"what should every session in this project always know?"* — durable
301
351
  lessons, rules, conventions, and gotchas that outlive any one session.
302
352
 
353
+ > **Note:** The memory tools (`handoff_memory_save`, `handoff_memory_query`, etc.) can always be
354
+ > called directly by the agent. For **automatic** injection — where relevant
355
+ > memories are surfaced on every prompt without the agent asking — you need to
356
+ > configure Claude Code hooks. See [Automatic injection via hooks](#automatic-injection-via-hooks).
357
+
303
358
  Memories live in `.handoff/memory/` (one JSON file per memory, plus per-session
304
359
  `injected/` sidecars). A built-in multilingual similarity engine (Japanese /
305
360
  English, dictionary-free) ranks relevance and detects duplicates, all in-memory
@@ -309,28 +364,28 @@ and sub-millisecond.
309
364
 
310
365
  The agent can call the memory tools at any time:
311
366
 
312
- - `memory_save` — record a memory. An exact duplicate is reported (not
367
+ - `handoff_memory_save` — record a memory. An exact duplicate is reported (not
313
368
  rewritten); a near-duplicate comes back as a `conflict` with both bodies so the
314
369
  agent can merge them (`merge_into=<id>`, `absorb_ids=[…]`) or save separately
315
370
  with `force=true`. **handoff-mcp never merges for you** — it surfaces both
316
371
  bodies and lets the agent decide.
317
- - `memory_query` — fetch the memories most relevant to some text and/or files.
318
- - `memory_delete` / `memory_cleanup` — prune and de-duplicate the store.
372
+ - `handoff_memory_query` — fetch the memories most relevant to some text and/or files.
373
+ - `handoff_memory_delete` / `handoff_memory_cleanup` — prune and de-duplicate the store.
319
374
 
320
375
  ### Automatic injection via hooks
321
376
 
322
377
  MCP is request/response — the server cannot push a memory into the agent's
323
378
  context on its own. **Claude Code hooks** close that gap: they fire regardless of
324
- what the agent intends, call `memory_query`, and inject the matching memories as
379
+ what the agent intends, call `handoff_memory_query`, and inject the matching memories as
325
380
  `additionalContext`. A per-session diff (keyed on the hook `session_id`) ensures
326
381
  the same memory is **not injected twice in one session** — and an *edited* memory
327
382
  (new content hash) is re-injected.
328
383
 
329
384
  | Event | Calls | Effect |
330
385
  |-------|-------|--------|
331
- | `UserPromptSubmit` | `memory_query` (prompt text) | Inject memories relevant to the prompt |
332
- | `PreToolUse` (`Edit\|Write\|MultiEdit`) | `memory_query` (file path) | Inject memories scoped to the file being edited |
333
- | `SessionStart` | `memory_cleanup` | Merge exact duplicates, gc old sidecars |
386
+ | `UserPromptSubmit` | `handoff_memory_query` (prompt text) | Inject memories relevant to the prompt |
387
+ | `PreToolUse` (`Edit\|Write\|MultiEdit`) | `handoff_memory_query` (file path) | Inject memories scoped to the file being edited |
388
+ | `SessionStart` | `handoff_memory_cleanup` | Merge exact duplicates, gc old sidecars |
334
389
 
335
390
  > **Wire hooks in your *user/global* settings, not in the repo.** Hooks are a
336
391
  > personal workflow choice; the handoff-mcp repo does not ship a `.claude/`
@@ -347,14 +402,14 @@ MCP tool from a hook directly, with no wrapper script. In
347
402
  "hooks": {
348
403
  "UserPromptSubmit": [
349
404
  { "hooks": [ {
350
- "type": "mcp_tool", "server": "handoff", "tool": "memory_query",
405
+ "type": "mcp_tool", "server": "handoff", "tool": "handoff_memory_query",
351
406
  "input": { "project_dir": "${cwd}",
352
407
  "session_id": "${session_id}", "text": "${prompt}" }
353
408
  } ] }
354
409
  ],
355
410
  "PreToolUse": [
356
411
  { "matcher": "Edit|Write|MultiEdit", "hooks": [ {
357
- "type": "mcp_tool", "server": "handoff", "tool": "memory_query",
412
+ "type": "mcp_tool", "server": "handoff", "tool": "handoff_memory_query",
358
413
  "input": { "project_dir": "${cwd}",
359
414
  "session_id": "${session_id}", "tool_name": "${tool_name}",
360
415
  "text": "${tool_input.file_path}",
@@ -363,7 +418,7 @@ MCP tool from a hook directly, with no wrapper script. In
363
418
  ],
364
419
  "SessionStart": [
365
420
  { "hooks": [ {
366
- "type": "mcp_tool", "server": "handoff", "tool": "memory_cleanup",
421
+ "type": "mcp_tool", "server": "handoff", "tool": "handoff_memory_cleanup",
367
422
  "input": { "project_dir": "${cwd}" }
368
423
  } ] }
369
424
  ]
@@ -413,11 +468,50 @@ All under `[settings]` in `.handoff/config.toml`, all with safe defaults
413
468
  |-----|---------|---------|
414
469
  | `memory_enabled` | `true` | Master switch. When `false`, all four memory tools return a benign empty result and write nothing |
415
470
  | `memory_dup_threshold` | `0.72` | Jaccard similarity at/above which a save is a near-duplicate conflict and cleanup groups a cluster |
416
- | `memory_query_min_score` | `0.5` | BM25 relevance floor for `memory_query` results |
471
+ | `memory_query_min_score` | `0.5` | BM25 relevance floor for `handoff_memory_query` results |
417
472
  | `memory_query_limit` | `5` | Max memories returned per query |
418
473
  | `memory_stale_days` | `60` | Days without a reference before a memory is flagged stale |
419
474
  | `memory_injected_gc_days` | `14` | Age at which per-session injection sidecars are garbage-collected |
420
475
 
476
+ ## CLI API
477
+
478
+ Since v0.15.0, every MCP tool is also callable directly from the shell:
479
+
480
+ ```bash
481
+ handoff-mcp <group> <action> [--key value ...]
482
+ ```
483
+
484
+ All output is JSON on stdout, suitable for scripting and programmatic use
485
+ (e.g. `child_process.execFile` from a VSCode extension).
486
+
487
+ **Examples:**
488
+
489
+ ```bash
490
+ # Memory operations
491
+ handoff-mcp memory save --text "Always use atomic_write" --kind lesson --tags safety,io
492
+ handoff-mcp memory query --text "atomic" --limit 5
493
+ handoff-mcp memory delete --id m-20260630-...
494
+
495
+ # Task management
496
+ handoff-mcp task list --status-filter todo
497
+ handoff-mcp task update --id t1 --title "New task" --status todo --estimate-hours 2
498
+ handoff-mcp task log-time --task-id t1 --hours 0.5
499
+
500
+ # Session and metrics
501
+ handoff-mcp session load
502
+ handoff-mcp metrics
503
+ handoff-mcp dashboard
504
+ ```
505
+
506
+ **Available groups:** `init`, `task`, `session`, `config`, `memory`,
507
+ `referral`, `assignee`, `milestone`, `calendar`, `labels`, `project`,
508
+ `metrics`, `capacity`, `schedule`, `dashboard`, `timer`.
509
+
510
+ Run `handoff-mcp --help` to see all groups, or `handoff-mcp <group> --help`
511
+ for actions within a group. See the
512
+ [CLI API Reference](https://github.com/alphaelements/handoff-mcp/wiki/CLI-API-Reference)
513
+ on the wiki for the full command list.
514
+
421
515
  ## MCP Resources
422
516
 
423
517
  | URI | Description |
@@ -445,9 +539,13 @@ This project uses handoff-mcp for session continuity.
445
539
  - **Decisions**: Record decisions with confidence levels as they are made,
446
540
  not just at session end. Use `confirmed` for verified facts, `estimated`
447
541
  for reasonable assumptions, `unverified` for unknowns.
448
- - **Project memory**: Use `memory_save` to record durable lessons, rules,
542
+ - **Timer**: Use `handoff_timer_start` / `handoff_timer_stop` to track task time.
543
+ When the VSCode extension is running, the timer delegates to it automatically.
544
+ When the extension is absent, MCP runs a fallback timer and logs hours on stop.
545
+ Use `handoff_timer_get_time` to check elapsed time without stopping.
546
+ - **Project memory**: Use `handoff_memory_save` to record durable lessons, rules,
449
547
  conventions, and gotchas that every future session should know. Use
450
- `memory_query` to retrieve relevant memories. Near-duplicate memories are
548
+ `handoff_memory_query` to retrieve relevant memories. Near-duplicate memories are
451
549
  surfaced as conflicts for you to merge or force-save — never merged silently.
452
550
  ```
453
551
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "handoff-mcp-server",
3
- "version": "0.13.0",
3
+ "version": "0.15.0",
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>",