feat(install): symlink myclaude to ~/.local/bin, make uninstall, docs

- Makefile: uv sync or venv pip install -e, then ln -sf to ~/.local/bin/myclaude
- Add scripts/rm_user_local_myclaude.py for safe unlink on make uninstall
- README: install path, shell completion guarded by command -v
- Track uv.lock for reproducible uv sync

Made-with: Cursor
This commit is contained in:
Zhengshou Lai
2026-04-10 08:23:14 +08:00
parent 596da55753
commit 21a7f032bf
4 changed files with 146 additions and 27 deletions
+20 -5
View File
@@ -1,15 +1,30 @@
ROOT_DIR := $(shell pwd)
VENV_MYCLAUDE := $(ROOT_DIR)/.venv/bin/myclaude
USER_LOCAL_MYCLAUDE := $(HOME)/.local/bin/myclaude
.PHONY: help install
.PHONY: help install uninstall _symlink-myclaude
help:
@echo "Usage: make [target]"
@echo ""
@echo " install Editable pip install of myclaude (uses uv when available)"
@echo " install Sync venv (uv or pip), symlink $(USER_LOCAL_MYCLAUDE) -> .venv/bin/myclaude"
@echo " uninstall Remove $(USER_LOCAL_MYCLAUDE) if it is a symlink to this repo"
install:
@if command -v uv >/dev/null 2>&1; then \
uv pip install -e "$(ROOT_DIR)"; \
@cd "$(ROOT_DIR)" && \
if command -v uv >/dev/null 2>&1; then \
uv sync; \
else \
python3 -m pip install -e "$(ROOT_DIR)"; \
test -x .venv/bin/pip || python3 -m venv .venv; \
.venv/bin/pip install -e .; \
fi
@$(MAKE) _symlink-myclaude
_symlink-myclaude:
@test -x "$(VENV_MYCLAUDE)" || { echo "error: missing $(VENV_MYCLAUDE)"; exit 1; }
@mkdir -p "$(HOME)/.local/bin"
@ln -sf "$(VENV_MYCLAUDE)" "$(USER_LOCAL_MYCLAUDE)"
@echo "Linked $(USER_LOCAL_MYCLAUDE) -> $(VENV_MYCLAUDE)"
uninstall:
@ROOT_DIR="$(ROOT_DIR)" python3 "$(ROOT_DIR)/scripts/rm_user_local_myclaude.py"
+7 -22
View File
@@ -151,38 +151,23 @@ git --version # 确认已安装
git clone <repository-url> ~/your-path/myclaude
cd ~/your-path/myclaude
# 安装(会自动检测并使用 uv 或 pip
# 安装(uv 则 uv sync,否则创建 .venv 并用 pip install -e
make install
```
> 💡 **提示**:如未安装 uv,会使用系统 pip 安装,速度稍慢
会在 `~/.local/bin/myclaude` 创建指向本仓库 `.venv/bin/myclaude` 的符号链接;请保证 `~/.local/bin``PATH` 中(多数发行版默认已包含)。移除该链接:`make uninstall`(仅当链接目标为本仓库的 `.venv/bin/myclaude` 时才会删除)。
**配置快捷命令:**
> 💡 **提示**:无 uv 时会创建项目 `.venv` 并用其中的 pip 做可编辑安装。
`myclaude` 添加到 shell 配置,方便全局调用
**Shell 补全(可选):**`~/.zshrc``~/.bashrc` 中加入(有命令再注册,避免未安装时报错)
```bash
# 编辑 ~/.zshrc 或 ~/.bashrc
MYCLAUDE_PATH="$HOME/your-path/myclaude" # 修改为你的实际路径
if [ -d "$MYCLAUDE_PATH/.venv/bin" ]; then
alias myclaude=$MYCLAUDE_PATH/.venv/bin/myclaude
eval "$(_MYCLAUDE_COMPLETE=zsh_source myclaude)" # zsh 补全
# eval "$(_MYCLAUDE_COMPLETE=bash_source myclaude)" # bash 补全
if command -v myclaude >/dev/null 2>&1; then
eval "$(_MYCLAUDE_COMPLETE=zsh_source myclaude)"
fi
# 使配置生效
source ~/.zshrc # 或 source ~/.bashrc
```
验证并启动:
```bash
# 验证安装
myclaude --version
# 启动本地交互模式
myclaude chat
```
Bash 将 `zsh_source` 换成 `bash_source`。保存后 `source` 该配置文件。
### 5. 安装 Metabot
+37
View File
@@ -0,0 +1,37 @@
#!/usr/bin/env python3
"""Remove ~/.local/bin/myclaude if it is a symlink to this repo's .venv/bin/myclaude."""
from __future__ import annotations
import os
import sys
def main() -> int:
root = os.path.normpath(os.environ.get("ROOT_DIR", os.getcwd()))
link = os.path.expanduser("~/.local/bin/myclaude")
want = os.path.normpath(os.path.join(root, ".venv", "bin", "myclaude"))
if not os.path.islink(link):
if os.path.lexists(link):
print("Skip: not a symlink, leaving untouched")
else:
print("Nothing to remove")
return 0
target = os.readlink(link)
if not os.path.isabs(target):
target = os.path.join(os.path.dirname(link), target)
target = os.path.normpath(target)
if target == want:
os.unlink(link)
print("Removed", link)
return 0
print("Skip: points to", target, "(not this repo's", want + ")")
return 0
if __name__ == "__main__":
sys.exit(main())
Generated
+82
View File
@@ -0,0 +1,82 @@
version = 1
revision = 3
requires-python = ">=3.10"
[[package]]
name = "click"
version = "8.3.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/57/75/31212c6bf2503fdf920d87fee5d7a86a2e3bcf444984126f13d8e4016804/click-8.3.2.tar.gz", hash = "sha256:14162b8b3b3550a7d479eafa77dfd3c38d9dc8951f6f69c78913a8f9a7540fd5", size = 302856, upload-time = "2026-04-03T19:14:45.118Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e4/20/71885d8b97d4f3dde17b1fdb92dbd4908b00541c5a3379787137285f602e/click-8.3.2-py3-none-any.whl", hash = "sha256:1924d2c27c5653561cd2cae4548d1406039cb79b858b747cfea24924bbc1616d", size = 108379, upload-time = "2026-04-03T19:14:43.505Z" },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "markdown-it-py"
version = "4.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "mdurl" },
]
sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" },
]
[[package]]
name = "mdurl"
version = "0.1.2"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" },
]
[[package]]
name = "myclaude"
version = "0.1.0"
source = { editable = "." }
dependencies = [
{ name = "click" },
{ name = "rich" },
]
[package.metadata]
requires-dist = [
{ name = "click", specifier = ">=8.0.0" },
{ name = "rich", specifier = ">=13.0.0" },
]
[[package]]
name = "pygments"
version = "2.20.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/c3/b2/bc9c9196916376152d655522fdcebac55e66de6603a76a02bca1b6414f6c/pygments-2.20.0.tar.gz", hash = "sha256:6757cd03768053ff99f3039c1a36d6c0aa0b263438fcab17520b30a303a82b5f", size = 4955991, upload-time = "2026-03-29T13:29:33.898Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
]
[[package]]
name = "rich"
version = "14.3.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "markdown-it-py" },
{ name = "pygments" },
]
sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" },
]