Compare commits
2
Commits
d19239bf31
...
ce9270623d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ce9270623d | ||
|
|
d65403efb5 |
No files matched your search
@@ -28,7 +28,7 @@ This repository contains personal dotfiles and configurations for a highly produ
|
||||
* **Gemini CLI:** Configured via `gemini/.gemini/`.
|
||||
* **Policies:** `readonly-commands.toml` allows critical commands like `git status`, `git diff`, and `git add` to run without confirmation.
|
||||
* **Opencode:** MCP-based assistant configuration (`opencode.json`) with support for documentation, search, and web-reading.
|
||||
* **Skill Bridges:** Symlinks are used to share skills across Claude and Opencode.
|
||||
* **Skill Bridges:** Claude gets skills via the `~/.claude/skills` symlink; Opencode reads `~/.agents/skills` natively, no bridge needed.
|
||||
|
||||
## 🚀 Usage & Maintenance
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ Chezmoi использует отдельный source dir `chezmoi/` внутр
|
||||
| `vscode` | `server-env-setup` для VS Code Server в WSL: секреты + авто-прокси |
|
||||
| `chatgpt` | Отдельный ярлык `ChatGPT Proxy` с запретом прямого запуска при ошибке |
|
||||
|
||||
`install.sh` дополнительно настраивает opencode (симлинки, MCP-конфиг), claude (симлинк `~/.claude/skills` на общие скиллы) и подключает bash_custom.
|
||||
`install.sh` дополнительно настраивает opencode (MCP-конфиг), claude (симлинк `~/.claude/skills` на общие скиллы) и подключает bash_custom.
|
||||
|
||||
### Windows (chezmoi)
|
||||
|
||||
@@ -76,9 +76,9 @@ Chezmoi использует отдельный source dir `chezmoi/` внутр
|
||||
| Инструмент | Что настраивается |
|
||||
|------------|-------------------|
|
||||
| **Agents** | Каноничное хранилище скиллов: `~/.agents/skills/<name>/SKILL.md` |
|
||||
| **Claude** | `~/.claude/skills` -> симлинк на agents; настройки (`settings.json`, `settings.local.json`) - управляются Claude Code на машине |
|
||||
| **[Opencode](https://github.com/anomalyco/opencode)** | MCP-серверы (context7, web-reader, playwright и др.), `~/.config/opencode/skills` -> симлинк на agents |
|
||||
| **Codex** | `~/.codex` - обычный каталог, управляется Codex; скиллы - из общего хранилища agents |
|
||||
| **Claude** | `~/.claude/skills` -> симлинк на agents; настройки (`settings.json`, `settings.local.json`) - управляются Claude Code на машине; в `settings.json` один раз добавляется блок `env` с прокси |
|
||||
| **[Opencode](https://github.com/anomalyco/opencode)** | MCP-серверы (context7, web-reader, playwright и др.); скиллы opencode сам читает из `~/.agents/skills` |
|
||||
| **Codex** | `~/.codex` - обычный каталог, управляется Codex; скиллы - из общего хранилища agents; `~/.codex/.env` - прокси по умолчанию, если файла ещё нет |
|
||||
| **Gemini** | Политики для CLI: pre-approved readonly команды |
|
||||
|
||||
## Настройка
|
||||
@@ -114,6 +114,25 @@ Linux/Bash-интерфейс `proxy-client` поддерживает внешн
|
||||
Windows не меняется. Если Codex уже запущен, его нужно полностью закрыть перед
|
||||
запуском через `Codex Proxy`.
|
||||
|
||||
На Linux `install.sh` задаёт прокси для Codex и Claude Code прямо в их
|
||||
настройках, а не в окружении запуска. Поэтому маршрут не зависит от того,
|
||||
откуда агент запущен:
|
||||
|
||||
- Codex: `~/.codex/.env` создаётся из `codex/proxy.env`, если файла ещё нет;
|
||||
- Claude Code: блок `env` из `claude/proxy-settings.json` добавляется в
|
||||
`~/.claude/settings.json`, если там ещё нет ни одной proxy-переменной.
|
||||
|
||||
Шаблон — прокси `127.0.0.1:12334` и короткий список исключений. Значения из
|
||||
этих файлов перекрывают переменные, унаследованные от запускающего
|
||||
приложения. Установщик не трогает уже сделанный на машине выбор.
|
||||
|
||||
Чтобы агент на машине ходил без прокси, очистите в его файле адреса
|
||||
(`HTTP_PROXY`, `HTTPS_PROXY`, `ALL_PROXY` в обоих регистрах) и задайте
|
||||
`NO_PROXY=*` и `no_proxy=*`. Демон Codex читает `.env` только при старте:
|
||||
после правки выполните `codex app-server daemon restart`. Claude Code
|
||||
подхватывает настройки при следующем запуске. С такими настройками `pcodex` и
|
||||
`pclaude` уже не обязательны.
|
||||
|
||||
`Claude Proxy` поддерживает как обычную установку в
|
||||
`%LOCALAPPDATA%\AnthropicClaude`, так и пакет из Microsoft Store. В обоих
|
||||
случаях он передает параметры Electron `--proxy-server` и
|
||||
@@ -201,7 +220,7 @@ PowerShell и VS Code: одного общего файла у Windows и WSL н
|
||||
| Файл | Назначение |
|
||||
|------|------------|
|
||||
| `~/.env.secrets` | API-ключи, подгружается bash и VS Code Server |
|
||||
| `~/.codex/.env` | Общие proxy-переменные для Codex CLI, Codex Desktop и их дочерних процессов |
|
||||
| `~/.codex/.env` | Proxy-переменные для Codex CLI, Codex Desktop и их дочерних процессов. Перекрывают унаследованное окружение. На Linux `install.sh` создаёт его из `codex/proxy.env` |
|
||||
| `~/.bash_aliases` | Персональные алиасы bash |
|
||||
| `~/.gitconfig.local` | Персональные Git-настройки (`user.name`, `core.editor` и т.д.) |
|
||||
| `~/.local/bin/start-claude-proxy.ps1` | Скрипт запуска Claude Desktop для Windows с переменными прокси и параметрами Electron |
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"env": {
|
||||
"HTTP_PROXY": "http://127.0.0.1:12334",
|
||||
"HTTPS_PROXY": "http://127.0.0.1:12334",
|
||||
"NO_PROXY": "localhost,127.0.0.1,::1,[::1],dementev.space,.dementev.space",
|
||||
"http_proxy": "http://127.0.0.1:12334",
|
||||
"https_proxy": "http://127.0.0.1:12334",
|
||||
"no_proxy": "localhost,127.0.0.1,::1,[::1],dementev.space,.dementev.space"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
# Proxy-переменные для Codex. install.sh копирует файл в ~/.codex/.env, если
|
||||
# там ещё нет своего. Codex читает ~/.codex/.env при старте, и значения отсюда
|
||||
# перекрывают унаследованные от запускающего приложения.
|
||||
#
|
||||
# Чтобы Codex на этой машине работал без прокси, очистите адреса и разрешите
|
||||
# обход для всех хостов: HTTP_PROXY= и т. д., NO_PROXY=* и no_proxy=*.
|
||||
# После правки перезапустите демон: codex app-server daemon restart
|
||||
HTTP_PROXY=http://127.0.0.1:12334
|
||||
HTTPS_PROXY=http://127.0.0.1:12334
|
||||
ALL_PROXY=socks5://127.0.0.1:12334
|
||||
NO_PROXY=localhost,127.0.0.1,::1,[::1],dementev.space,.dementev.space
|
||||
http_proxy=http://127.0.0.1:12334
|
||||
https_proxy=http://127.0.0.1:12334
|
||||
all_proxy=socks5://127.0.0.1:12334
|
||||
no_proxy=localhost,127.0.0.1,::1,[::1],dementev.space,.dementev.space
|
||||
+79
-2
@@ -452,8 +452,16 @@ if [ -L "$OPENCODE_DIR/agents" ]; then
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ -d "$SKILLS_SRC" ]; then
|
||||
ensure_symlink "$SKILLS_SRC" "$OPENCODE_DIR/skills"
|
||||
# Скиллы opencode сам берёт из ~/.agents/skills и ~/.claude/skills. Прежняя
|
||||
# ссылка ~/.config/opencode/skills загружала тот же набор ещё раз, и opencode
|
||||
# сыпал предупреждениями о дублях. Чистим её.
|
||||
if [ -L "$OPENCODE_DIR/skills" ]; then
|
||||
case "$(readlink "$OPENCODE_DIR/skills" 2>/dev/null || true)" in
|
||||
"$SKILLS_SRC"*)
|
||||
rm "$OPENCODE_DIR/skills"
|
||||
info "Удалён лишний симлинк ~/.config/opencode/skills"
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
if [ -f "$OPENCODE_JSON_SRC" ]; then
|
||||
@@ -497,12 +505,81 @@ if [ -d "$SKILLS_SRC" ]; then
|
||||
ensure_symlink "$SKILLS_SRC" "$HOME/.claude/skills"
|
||||
fi
|
||||
|
||||
# Прокси для Claude Code: блок env из claude/proxy-settings.json добавляется в
|
||||
# ~/.claude/settings.json. Значения из env перекрывают унаследованное окружение.
|
||||
# Если в env уже есть хоть одна proxy-переменная, файл не трогаем: машинный
|
||||
# выбор (свой адрес или очищенные значения) важнее шаблона.
|
||||
CLAUDE_PROXY_SRC="$DOTFILES_DIR/claude/proxy-settings.json"
|
||||
CLAUDE_SETTINGS="$HOME/.claude/settings.json"
|
||||
if [ -f "$CLAUDE_PROXY_SRC" ]; then
|
||||
if [ -L "$CLAUDE_SETTINGS" ]; then
|
||||
warn "~/.claude/settings.json — симлинк, прокси не добавляю"
|
||||
elif ! command -v python3 &>/dev/null; then
|
||||
warn "python3 не найден, прокси в ~/.claude/settings.json не добавлен"
|
||||
else
|
||||
claude_proxy_status=0
|
||||
python3 -I - "$CLAUDE_PROXY_SRC" "$CLAUDE_SETTINGS" <<'PY' || claude_proxy_status=$?
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import tempfile
|
||||
|
||||
src, dst = sys.argv[1:3]
|
||||
with open(src, encoding="utf-8") as f:
|
||||
template = json.load(f)["env"]
|
||||
|
||||
settings = {}
|
||||
if os.path.exists(dst):
|
||||
try:
|
||||
with open(dst, encoding="utf-8") as f:
|
||||
settings = json.load(f)
|
||||
except ValueError:
|
||||
sys.exit(2)
|
||||
if not isinstance(settings, dict) or not isinstance(settings.get("env", {}), dict):
|
||||
sys.exit(2)
|
||||
|
||||
env = settings.setdefault("env", {})
|
||||
if any(key.lower() in ("http_proxy", "https_proxy", "all_proxy", "no_proxy") for key in env):
|
||||
sys.exit(3)
|
||||
env.update(template)
|
||||
|
||||
mode = os.stat(dst).st_mode & 0o777 if os.path.exists(dst) else 0o600
|
||||
fd, tmp = tempfile.mkstemp(dir=os.path.dirname(dst), prefix=".settings.json.")
|
||||
with os.fdopen(fd, "w", encoding="utf-8") as f:
|
||||
json.dump(settings, f, indent=2, ensure_ascii=False)
|
||||
f.write("\n")
|
||||
os.chmod(tmp, mode)
|
||||
os.replace(tmp, dst)
|
||||
PY
|
||||
case "$claude_proxy_status" in
|
||||
0) info "В ~/.claude/settings.json добавлен прокси 127.0.0.1:12334" ;;
|
||||
3) info "В ~/.claude/settings.json прокси уже задан, оставляю как есть" ;;
|
||||
2) warn "~/.claude/settings.json не разобран как JSON-объект, прокси не добавлен" ;;
|
||||
*) warn "Не удалось добавить прокси в ~/.claude/settings.json" ;;
|
||||
esac
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Codex ────────────────────────────────────────────────────────────
|
||||
# Пакета в репозитории нет: ~/.codex — обычный каталог, его заполняет Codex.
|
||||
# Миграция раскрывает старый свёрнутый симлинк и переносит рантайм-данные.
|
||||
|
||||
migrate_folded_symlink "$HOME/.codex" .gitkeep
|
||||
|
||||
# ~/.codex/.env Codex читает при старте, и его значения перекрывают
|
||||
# унаследованное окружение. По умолчанию кладём прокси на 127.0.0.1:12334.
|
||||
# Готовый файл на машине не трогаем: где прокси не нужен, его очищают вручную.
|
||||
CODEX_ENV_SRC="$DOTFILES_DIR/codex/proxy.env"
|
||||
if [ -f "$CODEX_ENV_SRC" ]; then
|
||||
mkdir -p "$HOME/.codex"
|
||||
if [ -e "$HOME/.codex/.env" ] || [ -L "$HOME/.codex/.env" ]; then
|
||||
info "~/.codex/.env уже есть, оставляю как есть"
|
||||
else
|
||||
cp "$CODEX_ENV_SRC" "$HOME/.codex/.env"
|
||||
info "Создан ~/.codex/.env: Codex работает через прокси 127.0.0.1:12334"
|
||||
fi
|
||||
fi
|
||||
|
||||
# ── Bash: .bashrc ────────────────────────────────────────────────────
|
||||
|
||||
BASHRC="$HOME/.bashrc"
|
||||
|
||||
+100
-2
@@ -39,7 +39,7 @@ setup_fixture() {
|
||||
# stow конфликтами, поэтому в тестовое дерево не попадает.
|
||||
git -C "$REPO_ROOT" ls-files -z --cached --others --exclude-standard -- \
|
||||
install.sh bash tmux vim zed kitty wezterm vscode agents opencode gemini \
|
||||
claude proxy-client chatgpt scripts \
|
||||
claude codex proxy-client chatgpt scripts \
|
||||
| tar -C "$REPO_ROOT" --null -T - -cf - \
|
||||
| tar -xf - -C "$repo_root"
|
||||
|
||||
@@ -245,6 +245,97 @@ test_codex_runtime_dir_is_unmanaged() {
|
||||
assert_file_contains "$home_dir/.codex/auth.json" "{}"
|
||||
}
|
||||
|
||||
test_codex_env_is_created_when_missing() {
|
||||
local fixture repo_root home_dir
|
||||
fixture="$(setup_fixture)"
|
||||
repo_root="$fixture/repo"
|
||||
home_dir="$fixture/home"
|
||||
mkdir -p "$home_dir"
|
||||
|
||||
run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install.out"
|
||||
|
||||
[ -f "$home_dir/.codex/.env" ] && [ ! -L "$home_dir/.codex/.env" ] \
|
||||
|| fail "Expected ~/.codex/.env to be a regular file"
|
||||
cmp -s "$repo_root/codex/proxy.env" "$home_dir/.codex/.env" \
|
||||
|| fail "Expected ~/.codex/.env to match codex/proxy.env"
|
||||
}
|
||||
|
||||
test_codex_env_is_kept_when_present() {
|
||||
local fixture repo_root home_dir backup
|
||||
fixture="$(setup_fixture)"
|
||||
repo_root="$fixture/repo"
|
||||
home_dir="$fixture/home"
|
||||
# Машина, где Codex ходит без прокси: свой .env установщик не трогает.
|
||||
mkdir -p "$home_dir/.codex"
|
||||
printf '%s\n' 'HTTPS_PROXY=' > "$home_dir/.codex/.env"
|
||||
|
||||
run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install.out"
|
||||
|
||||
[ "$(cat "$home_dir/.codex/.env")" = 'HTTPS_PROXY=' ] \
|
||||
|| fail "Expected existing ~/.codex/.env to stay unchanged"
|
||||
backup="$(find "$home_dir/.codex" -name '.env.bak.*' -print -quit)"
|
||||
[ -z "$backup" ] || fail "Existing ~/.codex/.env must not be backed up: $backup"
|
||||
}
|
||||
|
||||
claude_env_value() {
|
||||
python3 -I -c 'import json, sys; print(json.load(open(sys.argv[1]))["env"].get(sys.argv[2], "<нет>"))' "$1" "$2"
|
||||
}
|
||||
|
||||
test_claude_proxy_is_added_to_new_settings() {
|
||||
local fixture repo_root home_dir
|
||||
fixture="$(setup_fixture)"
|
||||
repo_root="$fixture/repo"
|
||||
home_dir="$fixture/home"
|
||||
mkdir -p "$home_dir"
|
||||
|
||||
run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install.out"
|
||||
|
||||
[ "$(claude_env_value "$home_dir/.claude/settings.json" HTTPS_PROXY)" = 'http://127.0.0.1:12334' ] \
|
||||
|| fail "Expected HTTPS_PROXY in new ~/.claude/settings.json"
|
||||
[ "$(stat -c %a "$home_dir/.claude/settings.json")" = 600 ] \
|
||||
|| fail "Expected new ~/.claude/settings.json to be private"
|
||||
}
|
||||
|
||||
test_claude_proxy_is_merged_into_existing_settings() {
|
||||
local fixture repo_root home_dir
|
||||
fixture="$(setup_fixture)"
|
||||
repo_root="$fixture/repo"
|
||||
home_dir="$fixture/home"
|
||||
mkdir -p "$home_dir/.claude"
|
||||
printf '%s\n' '{"theme": "dark", "env": {"FOO": "bar"}}' > "$home_dir/.claude/settings.json"
|
||||
|
||||
run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install.out"
|
||||
|
||||
[ "$(claude_env_value "$home_dir/.claude/settings.json" no_proxy)" != '<нет>' ] \
|
||||
|| fail "Expected no_proxy to be merged into ~/.claude/settings.json"
|
||||
[ "$(claude_env_value "$home_dir/.claude/settings.json" FOO)" = bar ] \
|
||||
|| fail "Expected existing env keys to be preserved"
|
||||
assert_file_contains "$home_dir/.claude/settings.json" '"theme": "dark"'
|
||||
}
|
||||
|
||||
test_claude_existing_proxy_choice_is_kept() {
|
||||
local fixture repo_root home_dir
|
||||
fixture="$(setup_fixture)"
|
||||
repo_root="$fixture/repo"
|
||||
home_dir="$fixture/home"
|
||||
# Машина без прокси: достаточно одной proxy-переменной, чтобы шаблон не применялся.
|
||||
mkdir -p "$home_dir/.claude"
|
||||
printf '%s\n' '{"env": {"no_proxy": "*"}}' > "$home_dir/.claude/settings.json"
|
||||
printf '%s\n' '{not json' > "$fixture/broken.json"
|
||||
|
||||
run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install.out"
|
||||
|
||||
[ "$(cat "$home_dir/.claude/settings.json")" = '{"env": {"no_proxy": "*"}}' ] \
|
||||
|| fail "Expected settings with a proxy choice to stay unchanged"
|
||||
|
||||
cp "$fixture/broken.json" "$home_dir/.claude/settings.json"
|
||||
run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >"$fixture/install2.out"
|
||||
cmp -s "$fixture/broken.json" "$home_dir/.claude/settings.json" \
|
||||
|| fail "Expected invalid settings.json to stay unchanged"
|
||||
grep -Fq 'не разобран как JSON' "$fixture/install2.out" \
|
||||
|| fail "Expected warning about invalid settings.json"
|
||||
}
|
||||
|
||||
test_migrates_folded_codex_symlink() {
|
||||
local fixture repo_root home_dir legacy_dir
|
||||
fixture="$(setup_fixture)"
|
||||
@@ -484,12 +575,14 @@ test_opencode_runtime_dir_is_preserved_and_config_is_managed() {
|
||||
EOF
|
||||
# Артефакт прежнего установщика: симлинк на удалённые профили агентов
|
||||
ln -s "$repo_root/opencode/.config/opencode/agents" "$home_dir/.config/opencode/agents"
|
||||
# Лишняя ссылка на общие скиллы: opencode и так читает ~/.agents/skills
|
||||
ln -s "$repo_root/agents/.agents/skills" "$home_dir/.config/opencode/skills"
|
||||
|
||||
run_install "$repo_root" "$home_dir" env WSL_DISTRO_NAME= INSTALL_INTERACTIVE_MODE=never >/tmp/test-install.out
|
||||
|
||||
[ -f "$home_dir/.config/opencode/package.json" ] || fail "Expected opencode runtime files to be preserved"
|
||||
assert_not_exists "$home_dir/.config/opencode/agents"
|
||||
[ -L "$home_dir/.config/opencode/skills" ] || fail "Expected ~/.config/opencode/skills symlink"
|
||||
assert_not_exists "$home_dir/.config/opencode/skills"
|
||||
assert_file_contains "$home_dir/.config/opencode/opencode.json" '"$schema": "https://opencode.ai/config.json"'
|
||||
assert_file_contains "$home_dir/.config/opencode/permission-reviewer.jsonc" '"confidenceThreshold"'
|
||||
|
||||
@@ -651,6 +744,11 @@ main() {
|
||||
test_rerun_does_not_backup_managed_kitty_config
|
||||
test_existing_wezterm_config_is_backed_up
|
||||
test_codex_runtime_dir_is_unmanaged
|
||||
test_codex_env_is_created_when_missing
|
||||
test_codex_env_is_kept_when_present
|
||||
test_claude_proxy_is_added_to_new_settings
|
||||
test_claude_proxy_is_merged_into_existing_settings
|
||||
test_claude_existing_proxy_choice_is_kept
|
||||
test_migrates_folded_codex_symlink
|
||||
test_claude_stale_artifacts_are_removed
|
||||
test_migrates_legacy_zed_config_symlink
|
||||
|
||||
Reference in new issue
Block a user