Текущий план слишком похож на обычный CRUD CLI и расходится с референсом kubectx.
Главная проблема: в MVP попали лишние пользовательские команды:
list current save use sync delete rename backup doctor
Для kubectx важна не внутренняя модель, а UX:
- без подкоманд для частых операций;
agentctxбез аргументов показывает список;agentctx <NAME>переключает профиль;agentctx -возвращает предыдущий профиль;- операции rename/delete/current/unset оформлены флагами или специальным синтаксисом, а не отдельными глаголами.
Поэтому agentctx должен не изобретать новый интерфейс, а повторять surface area kubectx настолько близко, насколько это безопасно для ~/.codex/auth.json.
Использован Context7: /ahmetb/kubectx. По документации kubectx поддерживает позиционное переключение, - для предыдущего контекста, rename через NEW=OLD, interactive/fzf mode и минимальный CLI без CRUD-подкоманд.
Сделать CLI agentctx, который управляет локальными Codex auth-профилями в стиле kubectx.
Источник истины активной Codex-сессии:
~/.codex/auth.json
Хранилище профилей agentctx:
~/.agentctx/
profiles/
work/
auth.json
metadata.json
personal/
auth.json
metadata.json
backups/
auth-2026-06-25T14-30-00Z.json
current
previous
lock
Секреты никогда не печатаются.
Итоговая справка должна быть близка к kubectx:
Manage and switch between Codex auth profiles.
USAGE:
agentctx : list the profiles
agentctx <NAME> : switch to profile <NAME>
agentctx - : switch to the previous profile
agentctx -c, --current : show the current profile name
agentctx -u, --unset : unset the current profile
agentctx <NEW_NAME>=<NAME> : rename profile <NAME> to <NEW_NAME>
agentctx <NEW_NAME>=. : save or rename current active auth to <NEW_NAME>
agentctx -d <NAME> [<NAME...>] : delete profile <NAME> ('.' for current profile)
agentctx -h, --help : show this message
agentctx -V, --version : show version
Эти команды из старого плана убрать из пользовательского API:
save
use
sync
backup
doctor
rename
delete
list
current
Причина: они дублируют kubectx-стиль и создают «много лишних команд».
Public CLI должен явно reject-ить эти legacy CRUD words как unsupported subcommands, а не пытаться обрабатывать их как старый API. Чтобы поведение было однозначным, эти слова зарезервированы и не могут быть именами профилей.
Внутри проекта могут быть функции save_profile, switch_profile, create_backup, но пользовательский CLI должен оставаться kubectx-like.
Поведение повторяет kubectx/fzf:
- если stdin и stdout являются TTY,
fzfдоступен вPATH, и envAGENTCTX_IGNORE_FZFне равен1, открыть interactive selector; выбранный профиль сразу становится active profile; - если stdin/stdout не TTY,
fzfнедоступен или interactive mode отключён через env, показать простой список профилей по алфавиту.
Простой list output:
* personal
test-account
work
* означает текущий активный профиль по marker/hash rules ниже.
Переключает активный Codex auth на профиль <NAME>.
Пример:
agentctx personalВывод:
Switched to profile "personal".
Flow:
- Проверить синтаксис имени профиля.
- Взять lock.
- Проверить внутри lock, что профиль существует и его
auth.jsonбезопасный/валидный. - Если
currentmarker указывает на существующий профиль и активный~/.codex/auth.jsonизменился — автоматически сохранить обновлённый auth обратно в этот current profile. - Создать backup текущего
~/.codex/auth.json, если файл существует. - Атомарно заменить
~/.codex/auth.jsonвыбранным profile auth. - Записать
previousиcurrent. - Отпустить lock.
Важно: отдельной команды sync не нужно. Обновления токенов сохраняются автоматически перед переключением.
Переключает на предыдущий профиль.
agentctx -Вывод:
Switched to profile "work".
Если предыдущего профиля нет:
error: previous profile is not set
Показывает текущий профиль.
agentctx -cВывод:
work
Current semantics:
- Если
~/.agentctx/currentуказывает на существующий профиль, этот marker считается источником текущего профиля даже если hash активного~/.codex/auth.jsonуже изменился после refresh токена Codex. Это нужно, чтобы auto-sync мог сохранить обновлённый auth обратно в правильный профиль перед следующим switch. - Если marker отсутствует или stale,
agentctxпробует hash-detection: сравнивает active auth с сохранёнными profile auth files. - Если ни marker, ни hash-detection не дают профиль, выводится:
Unknown active profile
Переименовывает профиль <NAME> в <NEW_NAME>.
agentctx prod=workВывод:
Profile "work" renamed to "prod".
Если переименован текущий профиль, marker current обновляется.
Если переименован previous профиль, marker previous обновляется.
Если <NEW_NAME> уже существует, операция должна завершиться ошибкой и ничего не перезаписывать.
Поведение адаптирует kubectx NEW_NAME=. под Codex auth.
Если current marker указывает на существующий профиль или active auth соответствует сохранённому профилю по hash:
- переименовать этот текущий профиль в
<NEW_NAME>; - обновить markers
currentиprevious, если они указывали на старое имя.
Если active ~/.codex/auth.json не связан ни с одним профилем:
- сохранить текущий
~/.codex/auth.jsonкак новый профиль<NEW_NAME>; - сделать
<NEW_NAME>текущим.
Если <NEW_NAME> уже существует, операция должна завершиться ошибкой и не перезаписывать существующий профиль.
Это заменяет старую команду:
agentctx save <name>
Пример:
agentctx work=.Вывод для нового профиля:
Saved current Codex auth as profile "work".
Удаляет один или несколько профилей.
agentctx -d old testВывод:
Deleted profile "old".
Deleted profile "test".
agentctx -d . удаляет текущий профиль.
Удаление профиля не должно удалять backup-и и не должно печатать содержимое auth.
Если удаляется текущий профиль:
- Сначала создать backup активного
~/.codex/auth.json. - Удалить profile dir.
- Очистить marker
current. - Если удалённый профиль был записан в
previous, очистить markerprevious. - Оставить активный
~/.codex/auth.jsonна месте, если он всё ещё валиден.
Если удаляется не-current профиль, но он записан в previous, marker previous тоже очищается.
Это ближе к kubectx: delete удаляет context entry, но не обязательно разрушает связанные сущности.
Сбрасывает текущий marker current без удаления профилей.
MVP-поведение:
- не удалять
~/.codex/auth.json; - не менять auth-файл;
- очистить
~/.agentctx/current; - сохранить
previousкак есть или очистить, если он указывает на несуществующий профиль.
Вывод:
Unset current profile.
Почему так: для Codex удаление auth.json слишком разрушительно. В отличие от kubeconfig current-context, auth.json содержит реальные credentials.
Показывает kubectx-like usage.
Показывает версию пакета.
agentctx 0.1.0
Эти пункты не должны попадать в текущий публичный API, чтобы не раздувать CLI:
В kubectx это shell scoped to context. Для Codex безопасная реализация зависит от того, можно ли запустить Codex с альтернативным home/config/auth path.
До подтверждения механизма не добавлять флаг в MVP.
Read-only shell для Codex auth-профиля не имеет очевидного безопасного аналога в MVP.
Отложить.
Полезно для разработки, но это лишняя публичная команда для kubectx-like MVP.
Если нужен debug, добавить позже как скрытый или advanced subcommand, но не в основной help.
Backup должен быть автоматическим внутренним механизмом, а не пользовательской командой MVP.
Sync должен быть автоматическим только перед switch, а не отдельной командой MVP. Rename/delete/unset не выполняют implicit sync, кроме явно описанных backup/marker updates.
~/.agentctx/
profiles/
<name>/
auth.json
metadata.json
backups/
auth-<timestamp>.json
current
previous
lock
{
"name": "work",
"created_at": "2026-06-25T14:30:00Z",
"updated_at": "2026-06-25T14:30:00Z",
"source": "~/.codex/auth.json",
"auth_sha256": "..."
}Секреты в metadata хранить нельзя.
auth_sha256 нужен для fallback hash-detection и для понимания, что active auth изменился после token refresh. Это hash файла, не токен. Current marker остаётся authoritative, пока указывает на существующий профиль.
Разрешённые имена:
[a-zA-Z0-9._-]+
Reject:
- пустое имя;
/и path separators;.и..;- whitespace;
- любое имя, начинающееся с
-, потому чтоagentctx -и flags зарезервированы; - legacy CRUD words
save,use,sync,backup,doctor,rename,delete,list,current.
. разрешён только как специальный operand в agentctx <NEW_NAME>=. и agentctx -d .; он не является именем профиля.
Реализация должна:
- Никогда не печатать содержимое
auth.json. - Никогда не логировать токены.
- Валидировать JSON перед сохранением или переключением.
- Reject invalid JSON.
- Reject symlink для active auth и profile auth files.
- Reject non-regular files.
- Использовать temp file в той же директории, что и target.
- Выставлять
chmod 0600до replacement. - Использовать
os.replaceдля atomic replacement. - Создавать backup перед переключением.
- Сохранять старый auth при ошибке replacement.
- Использовать lock file во время операций switch/save-current/delete/rename/unset.
- Не использовать symlink для
~/.codex/auth.json, потому что Codex может перезаписывать файл и ломать связь.
Обязательные permissions:
~/.agentctx 0700
~/.agentctx/profiles 0700
~/.agentctx/backups 0700
profile auth.json files 0600
backup auth.json files 0600
~/.codex/auth.json 0600
На платформах без POSIX permissions тесты должны быть условными.
Публичный CLI kubectx-like, но внутри можно использовать понятные функции.
agentctx/
__init__.py
cli.py # argparse + kubectx-like routing
paths.py # paths + env overrides
auth.py # safe auth file operations
profiles.py # profile model operations
errors.py # domain exceptions
Env overrides для тестов:
AGENTCTX_HOME=/tmp/agentctx
AGENTCTX_CODEX_HOME=/tmp/codexОтвечает за:
- JSON validation;
- symlink/non-regular rejection;
- atomic write;
- backup creation;
- chmod;
- fsync там, где поддерживается;
- lock handling.
Внутренние функции:
list_profiles()
switch_profile(name)
switch_previous()
current_profile()
rename_profile(old, new)
save_current_as_profile(name)
delete_profiles(names)
unset_current()Важно: эти функции не диктуют CLI-команды. CLI остаётся kubectx-like.
Routing:
no args -> list
NAME -> switch
- -> switch_previous
-c / --current -> current
-u / --unset -> unset
NEW=OLD -> rename
NEW=. -> save current or rename current
-d names... -> delete
-h / --help -> help
-V / --version -> version
Обновить pyproject.toml:
version = "0.1.0"
[tool.poetry.scripts]
agentctx = "agentctx.cli:main"Исправить metadata inconsistency:
- оставить
python = "^3.8"; - удалить classifier
Programming Language :: Python :: 3.7.
Обновить description:
description = "kubectx-like CLI for switching Codex auth profiles"Добавить pytest dev dependency.
Тесты должны проверять kubectx-like UX, а не старые subcommands.
tests/
test_cli.py
test_paths.py
test_profiles.py
test_auth_safety.py
Required cases:
agentctxlists profiles when non-TTY,fzfis unavailable, orAGENTCTX_IGNORE_FZF=1;agentctxopens fzf selector and switches selected profile when stdin/stdout are TTY and fzf is available;agentctx workswitches profile;agentctx -switches previous profile;agentctx -cprints current profile;agentctx --currentprints current profile;agentctx work=.saves current auth as profile if unknown;agentctx work=.renames the known current profile and updatescurrent/previousmarkers when active auth is already linked to a profile;agentctx prod=workrenames profile;- rename/save via
NEW=...fail without clobbering when target name already exists; agentctx -d workdeletes profile;agentctx -d old testdeletes multiple profiles;agentctx -d .deletes current profile;- delete clears
previouswhen the deleted profile was previous; agentctx -uunsets current marker;agentctx --unsetunsets current marker;agentctx -Vprints version;agentctx --versionprints version;- legacy CRUD subcommands
save,use,sync,backup,doctor,rename,delete,list,currentare rejected/not supported as subcommands and are not documented.
- switch takes lock before checking mutable filesystem state;
- switch replaces current
~/.codex/auth.json; - switch creates backup internally;
- switch updates
currentandprevious; - switch rejects missing profile;
- switch rejects invalid profile auth;
- switch preserves old auth if replacement fails.
currentmarker is trusted while it points to an existing profile, even when active auth hash differs after token refresh;- hash detection is used only when marker is absent or stale;
agentctx -cprintsUnknown active profileonly when neither marker nor hash detection identifies a profile;- if active auth changed while current profile is selected,
agentctx othersaves updated auth into current profile before switching; - auto-sync is required before switch only;
- no token content appears in output.
- invalid JSON rejected;
- symlink profile auth rejected;
- symlink active auth rejected;
- non-regular file rejected;
- permissions are
0600for active, profile, and backup auth files where supported; - output does not contain token strings.
poetry check
poetry run pytest
poetry buildManual QA:
poetry install
poetry run agentctx work=.
poetry run agentctx
poetry run agentctx -c
poetry run agentctx personal
poetry run agentctx -
poetry run agentctx prod=work
poetry run agentctx -d prod
poetry run agentctx -uREADME должен показывать именно kubectx-like UX:
## Usage
```bash
agentctx # list profiles or open fzf selector when interactive
agentctx work # switch to profile
agentctx - # switch to previous profile
agentctx -c # show current profile
agentctx work=. # save current Codex auth as profile
agentctx prod=work # rename profile
agentctx -d old-profile # delete profile
agentctx -u # unset current marker
```Не добавлять в README legacy CRUD subcommands save/use/sync/backup/doctor/rename/delete/list/current.
# Changelog
## 0.1.0
### Added
- Kubectx-like CLI entrypoint `agentctx`.
- Profile listing via `agentctx`.
- Profile switching via `agentctx <NAME>`.
- Previous profile switch via `agentctx -`.
- Current profile display via `agentctx -c` / `--current`.
- Profile save/rename via `agentctx <NEW_NAME>=.` and `agentctx <NEW_NAME>=<NAME>`.
- Profile deletion via `agentctx -d`.
- Current marker unset via `agentctx -u` / `--unset`.
- Local profile storage in `~/.agentctx`.
- Atomic replacement of `~/.codex/auth.json`.
- Automatic backups before profile switch.
- Automatic sync of updated active auth before switch.
- JSON validation and restrictive permissions.MVP готов, когда:
poetry run agentctxпоказывает профили или открывает fzf selector при interactive TTY;poetry run agentctx work=.сохраняет текущий~/.codex/auth.jsonкак профиль, если active auth неизвестен;poetry run agentctx work=.переименовывает known current profile и обновляет markerscurrent/previous, если active auth уже связан с профилем;poetry run agentctx workбезопасно переключает auth;poetry run agentctx -возвращает предыдущий профиль;poetry run agentctx -cпоказывает current marker profile, hash-detected profile илиUnknown active profile;poetry run agentctx prod=workпереименовывает профиль;poetry run agentctx -d prodудаляет профиль;poetry run agentctx -d old testудаляет несколько профилей;poetry run agentctx -uиpoetry run agentctx --unsetочищают current marker;poetry run agentctx -Vиpoetry run agentctx --versionпоказывают версию;- backup создаётся автоматически перед switch и имеет permissions
0600where supported; - обновлённый активный auth автоматически синхронизируется в текущий профиль перед switch only;
- invalid JSON rejected;
- symlinks and non-regular files rejected;
- auth files written with
0600where supported; - token contents are never printed;
- README содержит только kubectx-like команды и не содержит legacy CRUD subcommands;
- tests pass.