Просмотр исходного кода

refactor(worktree): move project out of repository

Dax Raad 2 месяцев назад
Родитель
Сommit
31f94f205e

+ 0 - 1
packages/worktree/.gitignore

@@ -1 +0,0 @@
-/target/

+ 0 - 992
packages/worktree/Cargo.lock

@@ -1,992 +0,0 @@
-# This file is automatically @generated by Cargo.
-# It is not intended for manual editing.
-version = 4
-
-[[package]]
-name = "anstream"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "824a212faf96e9acacdbd09febd34438f8f711fb84e09a8916013cd7815ca28d"
-dependencies = [
- "anstyle",
- "anstyle-parse",
- "anstyle-query",
- "anstyle-wincon",
- "colorchoice",
- "is_terminal_polyfill",
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle"
-version = "1.0.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "940b3a0ca603d1eade50a4846a2afffd5ef57a9feac2c0e2ec2e14f9ead76000"
-
-[[package]]
-name = "anstyle-parse"
-version = "1.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "52ce7f38b242319f7cabaa6813055467063ecdc9d355bbb4ce0c68908cd8130e"
-dependencies = [
- "utf8parse",
-]
-
-[[package]]
-name = "anstyle-query"
-version = "1.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
-dependencies = [
- "windows-sys",
-]
-
-[[package]]
-name = "anstyle-wincon"
-version = "3.0.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
-dependencies = [
- "anstyle",
- "once_cell_polyfill",
- "windows-sys",
-]
-
-[[package]]
-name = "anyhow"
-version = "1.0.102"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c"
-
-[[package]]
-name = "bitflags"
-version = "2.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
-
-[[package]]
-name = "bumpalo"
-version = "3.20.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649"
-
-[[package]]
-name = "cc"
-version = "1.2.63"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "556e016178bb5662a08681bbe0f00f8e17631781a4dfc8c45e466e4b185ec27f"
-dependencies = [
- "find-msvc-tools",
- "shlex",
-]
-
-[[package]]
-name = "cfg-if"
-version = "1.0.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801"
-
-[[package]]
-name = "clap"
-version = "4.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51"
-dependencies = [
- "clap_builder",
- "clap_derive",
-]
-
-[[package]]
-name = "clap_builder"
-version = "4.6.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f"
-dependencies = [
- "anstream",
- "anstyle",
- "clap_lex",
- "strsim",
-]
-
-[[package]]
-name = "clap_derive"
-version = "4.6.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9"
-dependencies = [
- "heck",
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "clap_lex"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
-
-[[package]]
-name = "colorchoice"
-version = "1.0.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570"
-
-[[package]]
-name = "dirs"
-version = "6.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e"
-dependencies = [
- "dirs-sys",
-]
-
-[[package]]
-name = "dirs-sys"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab"
-dependencies = [
- "libc",
- "option-ext",
- "redox_users",
- "windows-sys",
-]
-
-[[package]]
-name = "equivalent"
-version = "1.0.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
-
-[[package]]
-name = "errno"
-version = "0.3.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
-dependencies = [
- "libc",
- "windows-sys",
-]
-
-[[package]]
-name = "fallible-iterator"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2acce4a10f12dc2fb14a218589d4f1f62ef011b2d0cc4b3cb1bba8e94da14649"
-
-[[package]]
-name = "fallible-streaming-iterator"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a"
-
-[[package]]
-name = "fastrand"
-version = "2.4.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6"
-
-[[package]]
-name = "filetime"
-version = "0.2.29"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759"
-dependencies = [
- "cfg-if",
- "libc",
-]
-
-[[package]]
-name = "find-msvc-tools"
-version = "0.1.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582"
-
-[[package]]
-name = "foldhash"
-version = "0.1.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2"
-
-[[package]]
-name = "futures-core"
-version = "0.3.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d"
-
-[[package]]
-name = "futures-task"
-version = "0.3.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
-
-[[package]]
-name = "futures-util"
-version = "0.3.32"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
-dependencies = [
- "futures-core",
- "futures-task",
- "pin-project-lite",
- "slab",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0"
-dependencies = [
- "cfg-if",
- "libc",
- "wasi",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.3.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
-dependencies = [
- "cfg-if",
- "libc",
- "r-efi 5.3.0",
- "wasip2",
-]
-
-[[package]]
-name = "getrandom"
-version = "0.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0de51e6874e94e7bf76d726fc5d13ba782deca734ff60d5bb2fb2607c7406555"
-dependencies = [
- "cfg-if",
- "libc",
- "r-efi 6.0.0",
- "wasip2",
- "wasip3",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.15.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1"
-dependencies = [
- "foldhash",
-]
-
-[[package]]
-name = "hashbrown"
-version = "0.17.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a"
-
-[[package]]
-name = "hashlink"
-version = "0.10.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1"
-dependencies = [
- "hashbrown 0.15.5",
-]
-
-[[package]]
-name = "heck"
-version = "0.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
-
-[[package]]
-name = "id-arena"
-version = "2.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954"
-
-[[package]]
-name = "indexmap"
-version = "2.14.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9"
-dependencies = [
- "equivalent",
- "hashbrown 0.17.1",
- "serde",
- "serde_core",
-]
-
-[[package]]
-name = "is_terminal_polyfill"
-version = "1.70.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
-
-[[package]]
-name = "itoa"
-version = "1.0.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
-
-[[package]]
-name = "js-sys"
-version = "0.3.99"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11"
-dependencies = [
- "cfg-if",
- "futures-util",
- "once_cell",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "leb128fmt"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2"
-
-[[package]]
-name = "libc"
-version = "0.2.186"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66"
-
-[[package]]
-name = "libredox"
-version = "0.1.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f02ab6bace2054fb888a3c16f990117b579d14a3088e472d63c6011fa185c9d3"
-dependencies = [
- "libc",
-]
-
-[[package]]
-name = "libsqlite3-sys"
-version = "0.33.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "947e6816f7825b2b45027c2c32e7085da9934defa535de4a6a46b10a4d5257fa"
-dependencies = [
- "cc",
- "pkg-config",
- "vcpkg",
-]
-
-[[package]]
-name = "linux-raw-sys"
-version = "0.12.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
-
-[[package]]
-name = "log"
-version = "0.4.30"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5"
-
-[[package]]
-name = "memchr"
-version = "2.8.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "6b947ae49db0d222b1dbc6b113ce7248a3fc3a6ca21b696717bfc000ba4484d8"
-
-[[package]]
-name = "once_cell"
-version = "1.21.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
-
-[[package]]
-name = "once_cell_polyfill"
-version = "1.70.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
-
-[[package]]
-name = "option-ext"
-version = "0.2.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
-
-[[package]]
-name = "pin-project-lite"
-version = "0.2.17"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
-
-[[package]]
-name = "pkg-config"
-version = "0.3.33"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
-
-[[package]]
-name = "ppv-lite86"
-version = "0.2.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
-dependencies = [
- "zerocopy",
-]
-
-[[package]]
-name = "prettyplease"
-version = "0.2.37"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b"
-dependencies = [
- "proc-macro2",
- "syn",
-]
-
-[[package]]
-name = "proc-macro2"
-version = "1.0.106"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "quote"
-version = "1.0.45"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924"
-dependencies = [
- "proc-macro2",
-]
-
-[[package]]
-name = "r-efi"
-version = "5.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
-
-[[package]]
-name = "r-efi"
-version = "6.0.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf"
-
-[[package]]
-name = "rand"
-version = "0.9.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea"
-dependencies = [
- "rand_chacha",
- "rand_core",
-]
-
-[[package]]
-name = "rand_chacha"
-version = "0.9.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
-dependencies = [
- "ppv-lite86",
- "rand_core",
-]
-
-[[package]]
-name = "rand_core"
-version = "0.9.5"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
-dependencies = [
- "getrandom 0.3.4",
-]
-
-[[package]]
-name = "redox_users"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac"
-dependencies = [
- "getrandom 0.2.17",
- "libredox",
- "thiserror",
-]
-
-[[package]]
-name = "rusqlite"
-version = "0.35.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a22715a5d6deef63c637207afbe68d0c72c3f8d0022d7cf9714c442d6157606b"
-dependencies = [
- "bitflags",
- "fallible-iterator",
- "fallible-streaming-iterator",
- "hashlink",
- "libsqlite3-sys",
- "smallvec",
-]
-
-[[package]]
-name = "rustix"
-version = "1.1.4"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
-dependencies = [
- "bitflags",
- "errno",
- "libc",
- "linux-raw-sys",
- "windows-sys",
-]
-
-[[package]]
-name = "rustversion"
-version = "1.0.22"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
-
-[[package]]
-name = "same-file"
-version = "1.0.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
-dependencies = [
- "winapi-util",
-]
-
-[[package]]
-name = "semver"
-version = "1.0.28"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
-
-[[package]]
-name = "serde"
-version = "1.0.228"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
-dependencies = [
- "serde_core",
-]
-
-[[package]]
-name = "serde_core"
-version = "1.0.228"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
-dependencies = [
- "serde_derive",
-]
-
-[[package]]
-name = "serde_derive"
-version = "1.0.228"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "serde_json"
-version = "1.0.150"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9"
-dependencies = [
- "itoa",
- "memchr",
- "serde",
- "serde_core",
- "zmij",
-]
-
-[[package]]
-name = "shlex"
-version = "2.0.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
-
-[[package]]
-name = "slab"
-version = "0.4.12"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
-
-[[package]]
-name = "smallvec"
-version = "1.15.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
-
-[[package]]
-name = "strsim"
-version = "0.11.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
-
-[[package]]
-name = "syn"
-version = "2.0.117"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99"
-dependencies = [
- "proc-macro2",
- "quote",
- "unicode-ident",
-]
-
-[[package]]
-name = "tempfile"
-version = "3.27.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
-dependencies = [
- "fastrand",
- "getrandom 0.4.2",
- "once_cell",
- "rustix",
- "windows-sys",
-]
-
-[[package]]
-name = "thiserror"
-version = "2.0.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4"
-dependencies = [
- "thiserror-impl",
-]
-
-[[package]]
-name = "thiserror-impl"
-version = "2.0.18"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "ulid"
-version = "1.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "470dbf6591da1b39d43c14523b2b469c86879a53e8b758c8e090a470fe7b1fbe"
-dependencies = [
- "rand",
- "web-time",
-]
-
-[[package]]
-name = "unicode-ident"
-version = "1.0.24"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
-
-[[package]]
-name = "unicode-xid"
-version = "0.2.6"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
-
-[[package]]
-name = "utf8parse"
-version = "0.2.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
-
-[[package]]
-name = "vcpkg"
-version = "0.2.15"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
-
-[[package]]
-name = "walkdir"
-version = "2.5.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
-dependencies = [
- "same-file",
- "winapi-util",
-]
-
-[[package]]
-name = "wasi"
-version = "0.11.1+wasi-snapshot-preview1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b"
-
-[[package]]
-name = "wasip2"
-version = "1.0.3+wasi-0.2.9"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6"
-dependencies = [
- "wit-bindgen 0.57.1",
-]
-
-[[package]]
-name = "wasip3"
-version = "0.4.0+wasi-0.3.0-rc-2026-01-06"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5"
-dependencies = [
- "wit-bindgen 0.51.0",
-]
-
-[[package]]
-name = "wasm-bindgen"
-version = "0.2.122"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409"
-dependencies = [
- "cfg-if",
- "once_cell",
- "rustversion",
- "wasm-bindgen-macro",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-macro"
-version = "0.2.122"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6"
-dependencies = [
- "quote",
- "wasm-bindgen-macro-support",
-]
-
-[[package]]
-name = "wasm-bindgen-macro-support"
-version = "0.2.122"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e"
-dependencies = [
- "bumpalo",
- "proc-macro2",
- "quote",
- "syn",
- "wasm-bindgen-shared",
-]
-
-[[package]]
-name = "wasm-bindgen-shared"
-version = "0.2.122"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437"
-dependencies = [
- "unicode-ident",
-]
-
-[[package]]
-name = "wasm-encoder"
-version = "0.244.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319"
-dependencies = [
- "leb128fmt",
- "wasmparser",
-]
-
-[[package]]
-name = "wasm-metadata"
-version = "0.244.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909"
-dependencies = [
- "anyhow",
- "indexmap",
- "wasm-encoder",
- "wasmparser",
-]
-
-[[package]]
-name = "wasmparser"
-version = "0.244.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe"
-dependencies = [
- "bitflags",
- "hashbrown 0.15.5",
- "indexmap",
- "semver",
-]
-
-[[package]]
-name = "web-time"
-version = "1.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb"
-dependencies = [
- "js-sys",
- "wasm-bindgen",
-]
-
-[[package]]
-name = "winapi-util"
-version = "0.1.11"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
-dependencies = [
- "windows-sys",
-]
-
-[[package]]
-name = "windows-link"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
-
-[[package]]
-name = "windows-sys"
-version = "0.61.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
-dependencies = [
- "windows-link",
-]
-
-[[package]]
-name = "wit-bindgen"
-version = "0.51.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5"
-dependencies = [
- "wit-bindgen-rust-macro",
-]
-
-[[package]]
-name = "wit-bindgen"
-version = "0.57.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
-
-[[package]]
-name = "wit-bindgen-core"
-version = "0.51.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc"
-dependencies = [
- "anyhow",
- "heck",
- "wit-parser",
-]
-
-[[package]]
-name = "wit-bindgen-rust"
-version = "0.51.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21"
-dependencies = [
- "anyhow",
- "heck",
- "indexmap",
- "prettyplease",
- "syn",
- "wasm-metadata",
- "wit-bindgen-core",
- "wit-component",
-]
-
-[[package]]
-name = "wit-bindgen-rust-macro"
-version = "0.51.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a"
-dependencies = [
- "anyhow",
- "prettyplease",
- "proc-macro2",
- "quote",
- "syn",
- "wit-bindgen-core",
- "wit-bindgen-rust",
-]
-
-[[package]]
-name = "wit-component"
-version = "0.244.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2"
-dependencies = [
- "anyhow",
- "bitflags",
- "indexmap",
- "log",
- "serde",
- "serde_derive",
- "serde_json",
- "wasm-encoder",
- "wasm-metadata",
- "wasmparser",
- "wit-parser",
-]
-
-[[package]]
-name = "wit-parser"
-version = "0.244.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736"
-dependencies = [
- "anyhow",
- "id-arena",
- "indexmap",
- "log",
- "semver",
- "serde",
- "serde_derive",
- "serde_json",
- "unicode-xid",
- "wasmparser",
-]
-
-[[package]]
-name = "worktree"
-version = "0.1.0"
-dependencies = [
- "dirs",
- "filetime",
- "libc",
- "rusqlite",
- "tempfile",
- "thiserror",
- "ulid",
- "walkdir",
-]
-
-[[package]]
-name = "worktree-cli"
-version = "0.1.0"
-dependencies = [
- "clap",
- "worktree",
-]
-
-[[package]]
-name = "zerocopy"
-version = "0.8.50"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3b065d4f0e55f82fae73202e189638116a87c55ab6b8e6c2721e13dd9d854ad1"
-dependencies = [
- "zerocopy-derive",
-]
-
-[[package]]
-name = "zerocopy-derive"
-version = "0.8.50"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b631b19d36a892ab55420c92dbc83ccd79274f25be714855d3074aa71cab639"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn",
-]
-
-[[package]]
-name = "zmij"
-version = "1.0.21"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa"

+ 0 - 19
packages/worktree/Cargo.toml

@@ -1,19 +0,0 @@
-[workspace]
-resolver = "2"
-members = ["crates/core", "crates/cli"]
-
-[workspace.package]
-edition = "2024"
-license = "MIT"
-version = "0.1.0"
-
-[workspace.dependencies]
-clap = { version = "4.5", features = ["derive"] }
-dirs = "6.0"
-filetime = "0.2"
-libc = "0.2"
-rusqlite = { version = "0.35", features = ["bundled"] }
-tempfile = "3.20"
-thiserror = "2.0"
-ulid = "1.2"
-walkdir = "2.5"

+ 0 - 13
packages/worktree/crates/cli/Cargo.toml

@@ -1,13 +0,0 @@
-[package]
-name = "worktree-cli"
-version.workspace = true
-edition.workspace = true
-license.workspace = true
-
-[[bin]]
-name = "worktree"
-path = "src/main.rs"
-
-[dependencies]
-clap.workspace = true
-worktree = { path = "../core" }

+ 0 - 79
packages/worktree/crates/cli/src/main.rs

@@ -1,79 +0,0 @@
-use clap::{Parser, Subcommand};
-use std::path::PathBuf;
-use worktree::{Create, Link, Manager};
-
-#[derive(Parser)]
-#[command(name = "worktree")]
-struct Cli {
-    #[arg(long, hide = true)]
-    database: Option<PathBuf>,
-    #[command(subcommand)]
-    command: Command,
-}
-
-#[derive(Subcommand)]
-enum Command {
-    Create {
-        from: Option<PathBuf>,
-        #[arg(long)]
-        name: Option<String>,
-        #[arg(long)]
-        into: Option<PathBuf>,
-    },
-    Remove {
-        at: PathBuf,
-    },
-    Link {
-        at: PathBuf,
-        #[arg(long)]
-        to: Option<PathBuf>,
-    },
-    Children {
-        of: PathBuf,
-    },
-    Ancestors {
-        of: PathBuf,
-    },
-}
-
-fn main() {
-    if let Err(error) = run() {
-        eprintln!("worktree: {error}");
-        std::process::exit(1);
-    }
-}
-
-fn run() -> worktree::Result<()> {
-    let cli = Cli::parse();
-    let mut manager = match cli.database {
-        Some(path) => Manager::open(path)?,
-        None => Manager::open_default()?,
-    };
-    match cli.command {
-        Command::Create { from, name, into } => {
-            println!(
-                "{}",
-                manager
-                    .create(Create {
-                        from: from.unwrap_or(std::env::current_dir()?),
-                        name,
-                        into,
-                    })?
-                    .display()
-            );
-        }
-        Command::Remove { at } => manager.remove(at)?,
-        Command::Link { at, to } => manager.link(Link { at, to })?,
-        Command::Children { of } => {
-            for path in manager.children(of)? {
-                println!("{}", path.display());
-            }
-        }
-        Command::Ancestors { of } => {
-            for path in manager.ancestors(of)? {
-                println!("{}", path.display());
-            }
-        }
-    }
-    Ok(())
-}

+ 0 - 17
packages/worktree/crates/core/Cargo.toml

@@ -1,17 +0,0 @@
-[package]
-name = "worktree"
-version.workspace = true
-edition.workspace = true
-license.workspace = true
-
-[dependencies]
-dirs.workspace = true
-filetime.workspace = true
-libc.workspace = true
-rusqlite.workspace = true
-thiserror.workspace = true
-ulid.workspace = true
-walkdir.workspace = true
-
-[dev-dependencies]
-tempfile.workspace = true

+ 0 - 195
packages/worktree/crates/core/src/copy.rs

@@ -1,195 +0,0 @@
-use crate::{Error, Result};
-#[cfg(target_os = "linux")]
-use filetime::{FileTime, set_file_times};
-use std::fs;
-#[cfg(target_os = "linux")]
-use std::fs::{File, OpenOptions};
-use std::path::Path;
-#[cfg(any(target_os = "linux", test))]
-use walkdir::WalkDir;
-
-pub(crate) trait CopyStrategy {
-    fn copy_directory(&self, from: &Path, to: &Path) -> Result<()>;
-}
-
-pub(crate) struct CowStrategy;
-
-impl CopyStrategy for CowStrategy {
-    fn copy_directory(&self, from: &Path, to: &Path) -> Result<()> {
-        #[cfg(target_os = "linux")]
-        return copy_directory_linux(from, to);
-
-        #[cfg(target_os = "macos")]
-        return copy_directory_macos(from, to);
-
-        #[cfg(not(any(target_os = "linux", target_os = "macos")))]
-        {
-            let _ = (from, to);
-            Err(Error::CowUnavailable(
-                "no copy-on-write strategy has been implemented for this platform".into(),
-            ))
-        }
-    }
-}
-
-#[cfg(target_os = "macos")]
-fn copy_directory_macos(from: &Path, to: &Path) -> Result<()> {
-    use std::ffi::CString;
-    use std::os::unix::ffi::OsStrExt;
-
-    let source = CString::new(from.as_os_str().as_bytes())
-        .map_err(|_| Error::Path(format!("path contains a null byte: {}", from.display())))?;
-    let destination = CString::new(to.as_os_str().as_bytes())
-        .map_err(|_| Error::Path(format!("path contains a null byte: {}", to.display())))?;
-    let result = unsafe { libc::clonefile(source.as_ptr(), destination.as_ptr(), 0) };
-    if result == 0 {
-        return Ok(());
-    }
-    Err(Error::CowUnavailable(format!(
-        "failed to clone {}: {}",
-        from.display(),
-        std::io::Error::last_os_error()
-    )))
-}
-
-#[cfg(target_os = "linux")]
-fn copy_directory_linux(from: &Path, to: &Path) -> Result<()> {
-    fs::create_dir(to)?;
-    fs::set_permissions(to, fs::metadata(from)?.permissions())?;
-
-    let entries = WalkDir::new(from)
-        .min_depth(1)
-        .follow_links(false)
-        .into_iter()
-        .collect::<std::result::Result<Vec<_>, _>>()?;
-
-    for entry in &entries {
-        let relative = entry
-            .path()
-            .strip_prefix(from)
-            .map_err(|error| Error::Path(error.to_string()))?;
-        let destination = to.join(relative);
-        let metadata = fs::symlink_metadata(entry.path())?;
-        if metadata.is_dir() {
-            fs::create_dir(&destination)?;
-            fs::set_permissions(&destination, metadata.permissions())?;
-            continue;
-        }
-        if metadata.is_symlink() {
-            copy_symlink(entry.path(), &destination)?;
-            continue;
-        }
-        if !metadata.is_file() {
-            return Err(Error::UnsupportedEntry(entry.path().to_path_buf()));
-        }
-        reflink_file(entry.path(), &destination)?;
-        fs::set_permissions(&destination, metadata.permissions())?;
-        set_file_times(
-            &destination,
-            FileTime::from_last_access_time(&metadata),
-            FileTime::from_last_modification_time(&metadata),
-        )?;
-    }
-
-    for entry in entries
-        .iter()
-        .rev()
-        .filter(|entry| entry.file_type().is_dir())
-    {
-        let destination = to.join(
-            entry
-                .path()
-                .strip_prefix(from)
-                .map_err(|error| Error::Path(error.to_string()))?,
-        );
-        let metadata = fs::metadata(entry.path())?;
-        set_file_times(
-            &destination,
-            FileTime::from_last_access_time(&metadata),
-            FileTime::from_last_modification_time(&metadata),
-        )?;
-    }
-
-    let metadata = fs::metadata(from)?;
-    set_file_times(
-        to,
-        FileTime::from_last_access_time(&metadata),
-        FileTime::from_last_modification_time(&metadata),
-    )?;
-    Ok(())
-}
-
-#[cfg(target_os = "linux")]
-fn reflink_file(from: &Path, to: &Path) -> Result<()> {
-    use std::os::fd::AsRawFd;
-
-    const FICLONE: libc::c_ulong = 0x4004_9409;
-    let source = File::open(from)?;
-    let destination = OpenOptions::new().write(true).create_new(true).open(to)?;
-    let result = unsafe { libc::ioctl(destination.as_raw_fd(), FICLONE, source.as_raw_fd()) };
-    if result == 0 {
-        return Ok(());
-    }
-    let error = std::io::Error::last_os_error();
-    Err(Error::CowUnavailable(format!(
-        "failed to reflink {}: {}",
-        from.display(),
-        error
-    )))
-}
-
-#[cfg(unix)]
-fn copy_symlink(from: &Path, to: &Path) -> Result<()> {
-    std::os::unix::fs::symlink(fs::read_link(from)?, to)?;
-    Ok(())
-}
-
-#[cfg(windows)]
-fn copy_symlink(from: &Path, to: &Path) -> Result<()> {
-    let target = fs::read_link(from)?;
-    if fs::metadata(from)?.is_dir() {
-        std::os::windows::fs::symlink_dir(target, to)?;
-        return Ok(());
-    }
-    std::os::windows::fs::symlink_file(target, to)?;
-    Ok(())
-}
-
-#[cfg(test)]
-pub(crate) struct TestStrategy;
-
-#[cfg(test)]
-impl CopyStrategy for TestStrategy {
-    fn copy_directory(&self, from: &Path, to: &Path) -> Result<()> {
-        fs::create_dir(to)?;
-        for entry in WalkDir::new(from).min_depth(1).follow_links(false) {
-            let entry = entry?;
-            let destination = to.join(
-                entry
-                    .path()
-                    .strip_prefix(from)
-                    .map_err(|error| Error::Path(error.to_string()))?,
-            );
-            if entry.file_type().is_dir() {
-                fs::create_dir(&destination)?;
-                continue;
-            }
-            if entry.file_type().is_symlink() {
-                copy_symlink(entry.path(), &destination)?;
-                continue;
-            }
-            fs::copy(entry.path(), destination)?;
-        }
-        Ok(())
-    }
-}
-
-#[cfg(test)]
-pub(crate) struct FailureStrategy;
-
-#[cfg(test)]
-impl CopyStrategy for FailureStrategy {
-    fn copy_directory(&self, _from: &Path, _to: &Path) -> Result<()> {
-        Err(Error::CowUnavailable("test failure".into()))
-    }
-}

+ 0 - 75
packages/worktree/crates/core/src/git.rs

@@ -1,75 +0,0 @@
-use crate::{Error, Result};
-use std::fs;
-use std::path::Path;
-use std::process::Command;
-
-pub(crate) fn check_source(path: &Path) -> Result<bool> {
-    let git = path.join(".git");
-    if !git.exists() {
-        return Ok(false);
-    }
-    if !git.is_dir() {
-        return Err(Error::UnsafeGit(
-            "linked Git worktree sources are not supported".into(),
-        ));
-    }
-
-    for state in [
-        "MERGE_HEAD",
-        "CHERRY_PICK_HEAD",
-        "REVERT_HEAD",
-        "BISECT_LOG",
-        "rebase-merge",
-        "rebase-apply",
-        "index.lock",
-        "HEAD.lock",
-    ] {
-        if git.join(state).exists() {
-            return Err(Error::UnsafeGit(format!("Git state in progress: {state}")));
-        }
-    }
-    Ok(true)
-}
-
-pub(crate) fn hide_marker(path: &Path) -> Result<()> {
-    let info = path.join(".git").join("info");
-    fs::create_dir_all(&info)?;
-    let exclude = info.join("exclude");
-    let existing = if exclude.exists() {
-        fs::read_to_string(&exclude)?
-    } else {
-        String::new()
-    };
-    if existing.lines().any(|line| line.trim() == "/.worktree") {
-        return Ok(());
-    }
-    let separator = if existing.is_empty() || existing.ends_with('\n') {
-        ""
-    } else {
-        "\n"
-    };
-    fs::write(exclude, format!("{existing}{separator}/.worktree\n"))?;
-    Ok(())
-}
-
-pub(crate) fn detach_destination(path: &Path) -> Result<()> {
-    let head = Command::new("git")
-        .arg("-C")
-        .arg(path)
-        .args(["rev-parse", "--verify", "HEAD^{commit}"])
-        .output()?;
-    if !head.status.success() {
-        return Ok(());
-    }
-    let output = Command::new("git")
-        .arg("-C")
-        .arg(path)
-        .args(["switch", "--detach", "--quiet", "HEAD"])
-        .output()?;
-    if output.status.success() {
-        return Ok(());
-    }
-    Err(Error::UnsafeGit(
-        String::from_utf8_lossy(&output.stderr).trim().to_owned(),
-    ))
-}

+ 0 - 712
packages/worktree/crates/core/src/lib.rs

@@ -1,712 +0,0 @@
-mod copy;
-mod git;
-
-use copy::{CopyStrategy, CowStrategy};
-use rusqlite::{Connection, OptionalExtension, params};
-use std::fs;
-use std::path::{Path, PathBuf};
-use thiserror::Error;
-use ulid::Ulid;
-
-pub type Result<T> = std::result::Result<T, Error>;
-
-#[derive(Debug, Error)]
-pub enum Error {
-    #[error("{0}")]
-    Io(#[from] std::io::Error),
-    #[error("{0}")]
-    Database(#[from] rusqlite::Error),
-    #[error("{0}")]
-    Walk(#[from] walkdir::Error),
-    #[error("invalid path: {0}")]
-    Path(String),
-    #[error("copy-on-write cloning unavailable: {0}")]
-    CowUnavailable(String),
-    #[error("unsupported filesystem entry: {0}")]
-    UnsupportedEntry(PathBuf),
-    #[error("unsafe Git source: {0}")]
-    UnsafeGit(String),
-    #[error("worktree is not managed: {0}")]
-    NotManaged(PathBuf),
-    #[error("worktree marker does not match the registry at: {0}")]
-    MarkerMismatch(PathBuf),
-    #[error("worktree marker belongs to an unknown registry entry at: {0}")]
-    UnknownMarker(PathBuf),
-    #[error("worktree already exists: {0}")]
-    AlreadyExists(PathBuf),
-    #[error("cannot remove the original registered workspace: {0}")]
-    CannotRemoveRoot(PathBuf),
-    #[error("cannot reparent the original registered workspace: {0}")]
-    CannotLinkRoot(PathBuf),
-    #[error("cannot remove subtree while a recorded worktree path is missing: {0}")]
-    MissingWorktree(PathBuf),
-    #[error("cannot link a worktree to itself or its descendant")]
-    Cycle,
-    #[error("cannot copy a workspace into itself: {0}")]
-    InsideSource(PathBuf),
-}
-
-pub struct Create {
-    pub from: PathBuf,
-    pub name: Option<String>,
-    pub into: Option<PathBuf>,
-}
-
-pub struct Link {
-    pub at: PathBuf,
-    pub to: Option<PathBuf>,
-}
-
-#[derive(Clone)]
-struct Record {
-    id: String,
-    parent_id: Option<String>,
-    path: PathBuf,
-}
-
-pub struct Manager {
-    database: Connection,
-    copier: Box<dyn CopyStrategy>,
-}
-
-impl Manager {
-    pub fn open_default() -> Result<Self> {
-        let path = default_database_path()?;
-        if let Some(parent) = path.parent() {
-            fs::create_dir_all(parent)?;
-        }
-        Self::open(path)
-    }
-
-    pub fn open(path: impl AsRef<Path>) -> Result<Self> {
-        Self::with_copier(path, Box::new(CowStrategy))
-    }
-
-    fn with_copier(path: impl AsRef<Path>, copier: Box<dyn CopyStrategy>) -> Result<Self> {
-        let database = Connection::open(path)?;
-        database.execute_batch(
-            "PRAGMA foreign_keys = ON;
-             CREATE TABLE IF NOT EXISTS worktree (
-               id TEXT PRIMARY KEY,
-               parent_id TEXT REFERENCES worktree(id) ON DELETE CASCADE,
-               path TEXT NOT NULL UNIQUE,
-               created_at INTEGER NOT NULL
-             );
-             CREATE INDEX IF NOT EXISTS worktree_parent_id_idx ON worktree(parent_id);",
-        )?;
-        Ok(Self { database, copier })
-    }
-
-    pub fn create(&mut self, input: Create) -> Result<PathBuf> {
-        let from = existing_directory(&input.from)?;
-        let git = git::check_source(&from)?;
-        let (source, register_source) = self.source(&from)?;
-        let root = self.root(&source)?;
-        let id = Ulid::new().to_string();
-        let destination_parent = match input.into {
-            Some(path) => absolute_path(&path)?,
-            None => default_storage(&root.path)?,
-        };
-        let name = destination_name(input.name, &id)?;
-        if destination_parent.join(&name).starts_with(&from) {
-            return Err(Error::InsideSource(destination_parent.join(name)));
-        }
-        fs::create_dir_all(&destination_parent)?;
-        let destination_parent = fs::canonicalize(destination_parent)?;
-        let destination = destination_parent.join(name);
-        if destination.starts_with(&from) {
-            return Err(Error::InsideSource(destination));
-        }
-        if destination.exists() {
-            return Err(Error::AlreadyExists(destination));
-        }
-
-        if let Err(error) = self.copier.copy_directory(&from, &destination) {
-            let _ = fs::remove_dir_all(&destination);
-            return Err(error);
-        }
-
-        let result = (|| {
-            write_marker(&destination, &id)?;
-            if git {
-                git::hide_marker(&destination)?;
-                git::detach_destination(&destination)?;
-            }
-            if register_source {
-                write_marker(&from, &source.id)?;
-                self.database.execute(
-                    "INSERT INTO worktree (id, parent_id, path, created_at) VALUES (?1, NULL, ?2, ?3)",
-                    params![source.id, path_text(&from)?, timestamp()],
-                )?;
-            }
-            if git {
-                git::hide_marker(&from)?;
-            }
-            self.database.execute(
-                "INSERT INTO worktree (id, parent_id, path, created_at) VALUES (?1, ?2, ?3, ?4)",
-                params![id, source.id, path_text(&destination)?, timestamp()],
-            )?;
-            Ok(destination.clone())
-        })();
-        if result.is_err() {
-            let _ = fs::remove_dir_all(&destination);
-        }
-        result
-    }
-
-    pub fn remove(&mut self, at: impl AsRef<Path>) -> Result<()> {
-        let at = existing_directory(at.as_ref())?;
-        let record = self.record_at(&at)?;
-        if record.parent_id.is_none() {
-            return Err(Error::CannotRemoveRoot(at));
-        }
-        verify_marker(&record)?;
-        let mut statement = self.database.prepare(
-            "WITH RECURSIVE subtree(id, path, depth) AS (
-               SELECT id, path, 0 FROM worktree WHERE id = ?1
-               UNION ALL
-               SELECT worktree.id, worktree.path, subtree.depth + 1
-               FROM worktree JOIN subtree ON worktree.parent_id = subtree.id
-             ) SELECT id, path, depth FROM subtree ORDER BY depth DESC",
-        )?;
-        let rows = statement
-            .query_map([&record.id], |row| {
-                Ok((
-                    row.get::<_, String>(0)?,
-                    PathBuf::from(row.get::<_, String>(1)?),
-                    row.get::<_, i64>(2)?,
-                ))
-            })?
-            .collect::<std::result::Result<Vec<_>, _>>()?;
-        drop(statement);
-        for (id, path, _) in &rows {
-            if !path.exists() {
-                return Err(Error::MissingWorktree(path.clone()));
-            }
-            verify_marker(&Record {
-                id: id.clone(),
-                parent_id: None,
-                path: path.clone(),
-            })?;
-        }
-        for (id, path, _) in &rows {
-            fs::remove_dir_all(path)?;
-            self.database
-                .execute("DELETE FROM worktree WHERE id = ?1", [id])?;
-        }
-        Ok(())
-    }
-
-    pub fn link(&mut self, input: Link) -> Result<()> {
-        let at = existing_directory(&input.at)?;
-        let record = match read_marker(&at)? {
-            Some(id) => {
-                let record = self
-                    .record_id(&id)?
-                    .ok_or_else(|| Error::UnknownMarker(at.clone()))?;
-                if record.path != at {
-                    if record.path.exists() {
-                        return Err(Error::MarkerMismatch(at));
-                    }
-                    self.database.execute(
-                        "UPDATE worktree SET path = ?1 WHERE id = ?2",
-                        params![path_text(&at)?, record.id],
-                    )?;
-                }
-                Record {
-                    path: at.clone(),
-                    ..record
-                }
-            }
-            None => {
-                let record = self.record_at(&at)?;
-                write_marker(&at, &record.id)?;
-                record
-            }
-        };
-        if at.join(".git").is_dir() {
-            git::hide_marker(&at)?;
-        }
-        let Some(to) = input.to else {
-            return Ok(());
-        };
-        if record.parent_id.is_none() {
-            return Err(Error::CannotLinkRoot(at));
-        }
-        let parent = self.record_at(&existing_directory(&to)?)?;
-        if parent.id == record.id || self.is_descendant(&parent.id, &record.id)? {
-            return Err(Error::Cycle);
-        }
-        self.database.execute(
-            "UPDATE worktree SET parent_id = ?1 WHERE id = ?2",
-            params![parent.id, record.id],
-        )?;
-        Ok(())
-    }
-
-    pub fn children(&self, of: impl AsRef<Path>) -> Result<Vec<PathBuf>> {
-        let record = self.record_at(&existing_directory(of.as_ref())?)?;
-        let mut statement = self
-            .database
-            .prepare("SELECT path FROM worktree WHERE parent_id = ?1 ORDER BY created_at, id")?;
-        Ok(statement
-            .query_map([record.id], |row| {
-                Ok(PathBuf::from(row.get::<_, String>(0)?))
-            })?
-            .collect::<std::result::Result<Vec<_>, _>>()?)
-    }
-
-    pub fn ancestors(&self, of: impl AsRef<Path>) -> Result<Vec<PathBuf>> {
-        let record = self.record_at(&existing_directory(of.as_ref())?)?;
-        let mut paths = Vec::new();
-        let mut parent_id = record.parent_id;
-        while let Some(id) = parent_id {
-            let parent = self
-                .record_id(&id)?
-                .ok_or_else(|| Error::NotManaged(record.path.clone()))?;
-            paths.push(parent.path);
-            parent_id = parent.parent_id;
-        }
-        Ok(paths)
-    }
-
-    fn source(&self, path: &Path) -> Result<(Record, bool)> {
-        if let Some(id) = read_marker(path)? {
-            let record = self
-                .record_id(&id)?
-                .ok_or_else(|| Error::UnknownMarker(path.to_path_buf()))?;
-            if record.path != path {
-                return Err(Error::MarkerMismatch(path.to_path_buf()));
-            }
-            return Ok((record, false));
-        }
-        if self.record_at_optional(path)?.is_some() {
-            return Err(Error::MarkerMismatch(path.to_path_buf()));
-        }
-        let id = Ulid::new().to_string();
-        Ok((
-            Record {
-                id,
-                parent_id: None,
-                path: path.to_path_buf(),
-            },
-            true,
-        ))
-    }
-
-    fn root(&self, record: &Record) -> Result<Record> {
-        let mut current = record.clone();
-        while let Some(id) = current.parent_id.clone() {
-            current = self
-                .record_id(&id)?
-                .ok_or_else(|| Error::NotManaged(record.path.clone()))?;
-        }
-        Ok(current)
-    }
-
-    fn record_at(&self, path: &Path) -> Result<Record> {
-        self.record_at_optional(path)?
-            .ok_or_else(|| Error::NotManaged(path.to_path_buf()))
-    }
-
-    fn record_at_optional(&self, path: &Path) -> Result<Option<Record>> {
-        self.database
-            .query_row(
-                "SELECT id, parent_id, path FROM worktree WHERE path = ?1",
-                [path_text(path)?],
-                |row| {
-                    Ok(Record {
-                        id: row.get(0)?,
-                        parent_id: row.get(1)?,
-                        path: PathBuf::from(row.get::<_, String>(2)?),
-                    })
-                },
-            )
-            .optional()
-            .map_err(Error::from)
-    }
-
-    fn record_id(&self, id: &str) -> Result<Option<Record>> {
-        self.database
-            .query_row(
-                "SELECT id, parent_id, path FROM worktree WHERE id = ?1",
-                [id],
-                |row| {
-                    Ok(Record {
-                        id: row.get(0)?,
-                        parent_id: row.get(1)?,
-                        path: PathBuf::from(row.get::<_, String>(2)?),
-                    })
-                },
-            )
-            .optional()
-            .map_err(Error::from)
-    }
-
-    fn is_descendant(&self, candidate: &str, of: &str) -> Result<bool> {
-        Ok(self.database.query_row(
-            "WITH RECURSIVE descendants(id) AS (
-               SELECT id FROM worktree WHERE parent_id = ?1
-               UNION ALL
-               SELECT worktree.id FROM worktree JOIN descendants ON worktree.parent_id = descendants.id
-             ) SELECT EXISTS(SELECT 1 FROM descendants WHERE id = ?2)",
-            params![of, candidate],
-            |row| row.get(0),
-        )?)
-    }
-}
-
-fn default_database_path() -> Result<PathBuf> {
-    let base = dirs::data_local_dir()
-        .ok_or_else(|| Error::Path("user data directory is unavailable".into()))?;
-    Ok(base.join("worktree").join("worktree.sqlite"))
-}
-
-fn existing_directory(path: &Path) -> Result<PathBuf> {
-    let path = fs::canonicalize(path)?;
-    if !path.is_dir() {
-        return Err(Error::Path(format!("not a directory: {}", path.display())));
-    }
-    Ok(path)
-}
-
-fn absolute_path(path: &Path) -> Result<PathBuf> {
-    if path.is_absolute() {
-        return Ok(path.to_path_buf());
-    }
-    Ok(std::env::current_dir()?.join(path))
-}
-
-fn default_storage(root: &Path) -> Result<PathBuf> {
-    let parent = root
-        .parent()
-        .ok_or_else(|| Error::Path(format!("workspace has no parent: {}", root.display())))?;
-    let name = root
-        .file_name()
-        .ok_or_else(|| Error::Path(format!("workspace has no name: {}", root.display())))?;
-    Ok(parent.join(".worktrees").join(name))
-}
-
-fn destination_name(name: Option<String>, id: &str) -> Result<String> {
-    let name = name.unwrap_or_else(|| id.to_owned());
-    if name.is_empty() || name == "." || name == ".." || Path::new(&name).components().count() != 1
-    {
-        return Err(Error::Path(format!("invalid worktree name: {name}")));
-    }
-    Ok(name)
-}
-
-fn marker(path: &Path) -> PathBuf {
-    path.join(".worktree")
-}
-
-fn write_marker(path: &Path, id: &str) -> Result<()> {
-    fs::write(marker(path), format!("{id}\n"))?;
-    Ok(())
-}
-
-fn read_marker(path: &Path) -> Result<Option<String>> {
-    let marker = marker(path);
-    if !marker.exists() {
-        return Ok(None);
-    }
-    Ok(Some(fs::read_to_string(marker)?.trim().to_owned()))
-}
-
-fn verify_marker(record: &Record) -> Result<()> {
-    if read_marker(&record.path)?.as_deref() == Some(&record.id) {
-        return Ok(());
-    }
-    Err(Error::MarkerMismatch(record.path.clone()))
-}
-
-fn path_text(path: &Path) -> Result<String> {
-    path.to_str()
-        .map(ToOwned::to_owned)
-        .ok_or_else(|| Error::Path(format!("path is not valid UTF-8: {}", path.display())))
-}
-
-fn timestamp() -> i64 {
-    std::time::SystemTime::now()
-        .duration_since(std::time::UNIX_EPOCH)
-        .unwrap_or_default()
-        .as_millis() as i64
-}
-
-#[cfg(test)]
-mod tests {
-    use super::*;
-    use crate::copy::{FailureStrategy, TestStrategy};
-    use std::process::Command;
-    use tempfile::TempDir;
-
-    fn manager(temp: &TempDir) -> Manager {
-        Manager::with_copier(temp.path().join("registry.sqlite"), Box::new(TestStrategy)).unwrap()
-    }
-
-    fn source(temp: &TempDir) -> PathBuf {
-        let source = temp.path().join("app");
-        fs::create_dir(&source).unwrap();
-        fs::write(source.join("file.txt"), "hello").unwrap();
-        source
-    }
-
-    #[test]
-    fn create_tracks_parentage_and_default_storage() {
-        let temp = TempDir::new().unwrap();
-        let source = source(&temp);
-        let mut manager = manager(&temp);
-        let first = manager
-            .create(Create {
-                from: source.clone(),
-                name: Some("first".into()),
-                into: None,
-            })
-            .unwrap();
-        let second = manager
-            .create(Create {
-                from: first.clone(),
-                name: Some("second".into()),
-                into: None,
-            })
-            .unwrap();
-
-        assert_eq!(first, temp.path().join(".worktrees/app/first"));
-        assert_eq!(second, temp.path().join(".worktrees/app/second"));
-        assert_ne!(
-            fs::read_to_string(source.join(".worktree")).unwrap(),
-            fs::read_to_string(first.join(".worktree")).unwrap()
-        );
-        assert_eq!(manager.children(&source).unwrap(), vec![first.clone()]);
-        assert_eq!(manager.ancestors(&second).unwrap(), vec![first, source]);
-    }
-
-    #[test]
-    fn remove_deletes_a_full_subtree() {
-        let temp = TempDir::new().unwrap();
-        let source = source(&temp);
-        let mut manager = manager(&temp);
-        let first = manager
-            .create(Create {
-                from: source.clone(),
-                name: Some("first".into()),
-                into: None,
-            })
-            .unwrap();
-        let second = manager
-            .create(Create {
-                from: first.clone(),
-                name: Some("second".into()),
-                into: None,
-            })
-            .unwrap();
-
-        manager.remove(&first).unwrap();
-
-        assert!(!first.exists());
-        assert!(!second.exists());
-        assert!(manager.children(&source).unwrap().is_empty());
-        assert!(matches!(
-            manager.remove(&source),
-            Err(Error::CannotRemoveRoot(_))
-        ));
-    }
-
-    #[test]
-    fn remove_refuses_a_subtree_with_an_unlinked_move() {
-        let temp = TempDir::new().unwrap();
-        let source = source(&temp);
-        let mut manager = manager(&temp);
-        let first = manager
-            .create(Create {
-                from: source,
-                name: Some("first".into()),
-                into: None,
-            })
-            .unwrap();
-        let second = manager
-            .create(Create {
-                from: first.clone(),
-                name: Some("second".into()),
-                into: None,
-            })
-            .unwrap();
-        fs::rename(&second, temp.path().join("moved")).unwrap();
-
-        assert!(matches!(
-            manager.remove(&first),
-            Err(Error::MissingWorktree(_))
-        ));
-        assert!(first.exists());
-    }
-
-    #[test]
-    fn link_restores_moves_markers_and_reparents() {
-        let temp = TempDir::new().unwrap();
-        let source = source(&temp);
-        let mut manager = manager(&temp);
-        let first = manager
-            .create(Create {
-                from: source.clone(),
-                name: Some("first".into()),
-                into: None,
-            })
-            .unwrap();
-        let second = manager
-            .create(Create {
-                from: source.clone(),
-                name: Some("second".into()),
-                into: None,
-            })
-            .unwrap();
-        let moved = temp.path().join("moved");
-        fs::rename(&second, &moved).unwrap();
-
-        manager
-            .link(Link {
-                at: moved.clone(),
-                to: Some(first.clone()),
-            })
-            .unwrap();
-        assert_eq!(
-            manager.ancestors(&moved).unwrap(),
-            vec![first, source.clone()]
-        );
-
-        fs::remove_file(source.join(".worktree")).unwrap();
-        manager
-            .link(Link {
-                at: source.clone(),
-                to: None,
-            })
-            .unwrap();
-        assert!(source.join(".worktree").exists());
-    }
-
-    #[test]
-    fn link_does_not_reparent_a_registered_source() {
-        let temp = TempDir::new().unwrap();
-        let source = source(&temp);
-        let mut manager = manager(&temp);
-        let child = manager
-            .create(Create {
-                from: source.clone(),
-                name: Some("child".into()),
-                into: None,
-            })
-            .unwrap();
-
-        assert!(matches!(
-            manager.link(Link {
-                at: source.clone(),
-                to: Some(child),
-            }),
-            Err(Error::CannotLinkRoot(_))
-        ));
-        assert!(matches!(
-            manager.remove(&source),
-            Err(Error::CannotRemoveRoot(_))
-        ));
-    }
-
-    #[test]
-    fn git_copy_detaches_head_and_preserves_dirty_state() {
-        let temp = TempDir::new().unwrap();
-        let source = source(&temp);
-        run(&source, &["init"]);
-        run(&source, &["config", "user.email", "test@example.com"]);
-        run(&source, &["config", "user.name", "Test"]);
-        run(&source, &["add", "file.txt"]);
-        run(&source, &["commit", "-m", "initial"]);
-        fs::write(source.join("file.txt"), "changed").unwrap();
-        run(&source, &["add", "file.txt"]);
-        fs::write(source.join("untracked.txt"), "new").unwrap();
-        let mut manager = manager(&temp);
-
-        let destination = manager
-            .create(Create {
-                from: source.clone(),
-                name: Some("git".into()),
-                into: None,
-            })
-            .unwrap();
-
-        assert!(
-            !Command::new("git")
-                .arg("-C")
-                .arg(&destination)
-                .args(["symbolic-ref", "-q", "HEAD"])
-                .status()
-                .unwrap()
-                .success()
-        );
-        let staged = Command::new("git")
-            .arg("-C")
-            .arg(&destination)
-            .args(["diff", "--cached", "--name-only"])
-            .output()
-            .unwrap();
-        assert!(String::from_utf8_lossy(&staged.stdout).contains("file.txt"));
-        assert!(destination.join("untracked.txt").exists());
-        let status = Command::new("git")
-            .arg("-C")
-            .arg(&destination)
-            .args(["status", "--porcelain", "--", ".worktree"])
-            .output()
-            .unwrap();
-        assert!(status.stdout.is_empty());
-    }
-
-    #[test]
-    fn unsafe_git_source_is_rejected_without_registering_it() {
-        let temp = TempDir::new().unwrap();
-        let source = source(&temp);
-        run(&source, &["init"]);
-        fs::write(source.join(".git/MERGE_HEAD"), "commit").unwrap();
-        let mut manager = manager(&temp);
-
-        assert!(matches!(
-            manager.create(Create {
-                from: source.clone(),
-                name: Some("unsafe".into()),
-                into: None,
-            }),
-            Err(Error::UnsafeGit(_))
-        ));
-        assert!(!source.join(".worktree").exists());
-    }
-
-    #[test]
-    fn unavailable_cow_does_not_register_the_source() {
-        let temp = TempDir::new().unwrap();
-        let source = source(&temp);
-        let mut manager = Manager::with_copier(
-            temp.path().join("registry.sqlite"),
-            Box::new(FailureStrategy),
-        )
-        .unwrap();
-
-        assert!(matches!(
-            manager.create(Create {
-                from: source.clone(),
-                name: Some("failure".into()),
-                into: None,
-            }),
-            Err(Error::CowUnavailable(_))
-        ));
-        assert!(!source.join(".worktree").exists());
-        assert!(manager.record_at_optional(&source).unwrap().is_none());
-    }
-
-    fn run(path: &Path, args: &[&str]) {
-        assert!(
-            Command::new("git")
-                .arg("-C")
-                .arg(path)
-                .args(args)
-                .status()
-                .unwrap()
-                .success()
-        );
-    }
-}

+ 0 - 182
packages/worktree/specs.md

@@ -1,182 +0,0 @@
-# Worktree Specs
-
-## Requirement
-
-`worktree` must be cross-platform as far as practical. Core semantics should work across macOS, Linux, and Windows. Copy-on-write is a platform/filesystem acceleration and must not define the product model.
-
-## API
-
-### `create`
-
-```ts
-create(input: {
-  from: AbsolutePath
-  name?: string
-  into?: AbsolutePath
-}): AbsolutePath
-```
-
-Default behavior:
-
-- Source is `from`.
-- `name` defaults to a generated directory name.
-- `into` defaults to the managed worktree directory.
-- Copy the whole workspace, including dirty, staged, untracked, and ignored files.
-- Detach `HEAD` in the new workspace.
-- Return the path of the new workspace.
-
-If `from` is already a managed worktree, create copies that exact worktree. Do not resolve back to an earlier workspace. Metadata should record the immediate source worktree as its parent.
-
-Default storage is a hidden sibling directory of the original registered workspace:
-
-```text
-/projects/app/                         original workspace
-/projects/.worktrees/app/task-a/       created worktree
-/projects/.worktrees/app/task-b/       created worktree
-```
-
-- Created worktrees must not be stored inside the workspace being copied, because an exact copy would recursively contain existing worktrees.
-- If `from` is an original unregistered workspace, its sibling `.worktrees/<workspace-name>/` directory becomes the default destination directory.
-- If `from` is already managed, descendants use the default destination directory associated with the original workspace rather than nesting storage beside each descendant.
-- If `into` is provided, use it instead of the default destination directory.
-- If the original workspace is itself a filesystem mount root, its sibling default destination may not support copy-on-write with it; provide `into` on the same filesystem in that case.
-
-### `remove`
-
-```ts
-remove(input: {
-  at: AbsolutePath
-}): void
-```
-
-`remove` deletes a managed worktree and its full descendant subtree.
-
-- `at` must identify a worktree created by this tool; the registered source root cannot be removed.
-- Resolve all descendants through `parent_id` and remove their directories deepest-first.
-- Verify each existing directory's `.worktree` marker before deleting it.
-- Refuse removal if any descendant path is missing, because it may be a moved workspace that has not been linked yet.
-- After successful filesystem removal, delete the subtree records from the database.
-
-### `link`
-
-```ts
-link(input: {
-  at: AbsolutePath
-  to?: AbsolutePath
-}): void
-```
-
-`link` reconnects a moved managed worktree to its registry record and can change its parent.
-
-- Read the ULID from `.worktree` at `at`.
-- Look up the existing worktree record by ULID.
-- If its recorded path is `at`, leave its location unchanged.
-- If its recorded path is different and missing, update it to `at`.
-- If its recorded path is different and still exists, fail because this is a duplicate identity, not a move.
-- If the ULID is unknown to the database, fail; `.worktree` alone does not include the ancestry needed to rebuild the record.
-- If `.worktree` is missing, look up `at` by its absolute path. If it matches an existing record, recreate the marker with that record's ULID.
-- If `.worktree` is missing and `at` does not match an existing record, fail. A moved workspace without its marker cannot be identified safely.
-- If `to` is provided, set the worktree's parent to the managed worktree at `to`.
-- Refuse `to` for an original registered workspace; only worktrees created by this tool can be reparented.
-- Refuse `to` if it is `at` or a descendant of `at`, because reparenting must not create a cycle.
-
-### `children`
-
-```ts
-children(input: {
-  of: AbsolutePath
-}): AbsolutePath[]
-```
-
-`children` returns the direct managed children created from `of`.
-
-### `ancestors`
-
-```ts
-ancestors(input: {
-  of: AbsolutePath
-}): AbsolutePath[]
-```
-
-`ancestors` returns the managed ancestry of `of`, ordered from its immediate parent to the root workspace.
-
-## Metadata
-
-Metadata is stored in a central SQLite database in the platform-appropriate user data directory.
-
-SQLite is not overkill: multiple processes and agents may create, inspect, or remove worktrees concurrently. It provides cross-platform transactions and locking without building a safe JSON registry protocol.
-
-Start with one table:
-
-```sql
-CREATE TABLE worktree (
-  id TEXT PRIMARY KEY,
-  parent_id TEXT REFERENCES worktree(id) ON DELETE CASCADE,
-  path TEXT NOT NULL UNIQUE,
-  created_at INTEGER NOT NULL
-);
-
-CREATE INDEX worktree_parent_id_idx ON worktree(parent_id);
-```
-
-- Every managed worktree has a stable generated `id`.
-- `id` is a ULID generated when the workspace is first registered or created.
-- `id` is stored in the central database and in a `.worktree` marker file at the root of the workspace.
-- `.worktree` contains the worktree ULID and allows a moved workspace to be rediscovered and verified against the database.
-- When a managed workspace is copied, the copied `.worktree` marker is replaced with the new workspace's ULID.
-- The original registered workspace has `parent_id = NULL`.
-- A created worktree has `parent_id` set to the source worktree `id`.
-- `path` is its current location, not its identity.
-- Provenance is a rooted tree. Descendants of any worktree can be listed through recursive queries over `parent_id`.
-- `remove` deletes a whole subtree, so no surviving record depends on deleted ancestry.
-
-### Moved Worktrees
-
-If a worktree is moved outside the tool, its recorded path becomes missing. The tool cannot discover an arbitrary new location without being given a path or scanning a configured directory.
-
-When `link` is run against a directory containing `.worktree`, the tool reads its ULID and reconciles the database path if the recorded path no longer exists.
-
-If both the recorded path and the provided path exist with the same ULID, the tool must refuse automatic reconciliation because the directory was copied without assigning a new identity.
-
-## Git Integration
-
-Git support is an integration for directories that contain repositories; it does not define the core worktree model.
-
-When registering or creating from a Git repository:
-
-- Add `/.worktree` to `.git/info/exclude` so the identity marker does not appear in local Git status.
-- Copy the directory with its staged, unstaged, untracked, ignored, and cached state intact.
-- If `HEAD` resolves to a commit, detach `HEAD` in the created destination at that same commit.
-- Preserve the copied index and working tree state while detaching.
-- If the repository has no commits yet, leave its unborn branch state unchanged because there is no commit to detach to.
-
-Refuse creation from a Git repository when:
-
-- It is a linked Git worktree whose `.git` is not an independent directory.
-- A merge, rebase, cherry-pick, revert, or bisect is in progress.
-- Git lock or inconsistent index state makes an exact safe copy unclear.
-
-The tool does not create branches, commit changes, or otherwise replace normal Git commands.
-
-## Copy Strategies
-
-Copying is implemented behind a strategy boundary so platform-specific copy-on-write backends can be added independently.
-
-- The production strategy on Linux uses reflink cloning.
-- The production strategy on macOS uses APFS `clonefile` directory cloning.
-- If no implemented copy-on-write strategy succeeds, `create` fails.
-- Full byte copying is not implemented as a fallback.
-- Future strategies may add Windows copy-on-write support without changing the API.
-
-## Packaging
-
-The project ships four interfaces backed by the same implementation and metadata model:
-
-1. Native library containing the core API and implementation.
-2. CLI package providing the `worktree` executable.
-3. Bun FFI package for use from Bun applications.
-4. Node FFI package for use from Node.js applications.
-
-The CLI and language bindings should remain thin and expose the same API semantics as the native library.
-
-For CLI ergonomics, `worktree create` defaults `from` to the current working directory when no source path is provided.