diff --git a/.env.example b/.env.example index ea8e344..08749a2 100644 --- a/.env.example +++ b/.env.example @@ -9,7 +9,7 @@ OPENCODE_SERVER_PASSWORD=your-opencode-server-password GITHUB_TOKEN=your-github-token-here # Context7 MCP -CONTEX7_API_KEY=your-context7-api-key-here +CONTEXT7_API_KEY=your-context7-api-key-here # Telegram (optional) TELEGRAM_API_ID=your-telegram-api-id diff --git a/.opencode/agents/docs-reviewer.md b/.opencode/agents/docs-reviewer.md index 4efc1ba..f5c3bc7 100644 --- a/.opencode/agents/docs-reviewer.md +++ b/.opencode/agents/docs-reviewer.md @@ -58,12 +58,12 @@ permission: "echo *": allow "python3*": allow "python3 *pipeline-status.py*": deny - "python3 config/scripts/pipeline-status.py*": deny + "python3 .opencode/scripts/pipeline-status.py*": deny "python3 */pipeline-status.py*": deny "python *pipeline-status.py*": deny "python */pipeline-status.py*": deny "python3 *spec-status.py*": deny - "python3 config/scripts/spec-status.py*": deny + "python3 .opencode/scripts/spec-status.py*": deny "python3 */spec-status.py*": deny "python *spec-status.py*": deny "python */spec-status.py*": deny diff --git a/.opencode/agents/reviewer.md b/.opencode/agents/reviewer.md index a4dbe4d..8729c81 100644 --- a/.opencode/agents/reviewer.md +++ b/.opencode/agents/reviewer.md @@ -69,12 +69,12 @@ permission: "python3*": allow "python *": allow "python3 *pipeline-status.py*": deny - "python3 config/scripts/pipeline-status.py*": deny + "python3 .opencode/scripts/pipeline-status.py*": deny "python3 */pipeline-status.py*": deny "python *pipeline-status.py*": deny "python */pipeline-status.py*": deny "python3 *spec-status.py*": deny - "python3 config/scripts/spec-status.py*": deny + "python3 .opencode/scripts/spec-status.py*": deny "python3 */spec-status.py*": deny "python *spec-status.py*": deny "python */spec-status.py*": deny diff --git a/.opencode/opencode.json b/.opencode/opencode.json index c35b199..143877e 100644 --- a/.opencode/opencode.json +++ b/.opencode/opencode.json @@ -215,12 +215,12 @@ "npm *": "allow", "python*": "allow", "python3 *pipeline-status.py*": "deny", - "python3 config/scripts/pipeline-status.py*": "deny", + "python3 .opencode/scripts/pipeline-status.py*": "deny", "python3 */pipeline-status.py*": "deny", "python *pipeline-status.py*": "deny", "python */pipeline-status.py*": "deny", "python3 *spec-status.py*": "deny", - "python3 config/scripts/spec-status.py*": "deny", + "python3 .opencode/scripts/spec-status.py*": "deny", "python3 */spec-status.py*": "deny", "python *spec-status.py*": "deny", "python */spec-status.py*": "deny", @@ -366,7 +366,7 @@ "-y", "@upstash/context7-mcp", "--api-key", - "{env:CONTEX7_API_KEY}" + "{env:CONTEXT7_API_KEY}" ], "enabled": true }, diff --git a/.opencode/scripts/check-permissions.py b/.opencode/scripts/check-permissions.py index a26b083..913be56 100755 --- a/.opencode/scripts/check-permissions.py +++ b/.opencode/scripts/check-permissions.py @@ -7,8 +7,8 @@ import sys from pathlib import Path REPO_ROOT = Path(__file__).resolve().parent.parent.parent -AGENTS_DIR = REPO_ROOT / "config" / "agents" -OPENCODE_JSON = REPO_ROOT / "config" / "opencode.json" +AGENTS_DIR = REPO_ROOT / ".opencode" / "agents" +OPENCODE_JSON = REPO_ROOT / ".opencode" / "opencode.json" DANGEROUS_PATTERNS = [ (r"^gh api\*?$", "broad — allows gh api -X DELETE/PUT/POST"), diff --git a/docs/decisions/018-pr-44-hotfix-config-paths.md b/docs/decisions/018-pr-44-hotfix-config-paths.md new file mode 100644 index 0000000..d808bc4 --- /dev/null +++ b/docs/decisions/018-pr-44-hotfix-config-paths.md @@ -0,0 +1,39 @@ +# ADR-018: Hotfix CONTEX7 typo + stale config/scripts paths + +## Статус +Accepted (2026-07-24) + +## Контекст +После миграции `config/` → `.opencode/` (PR#23, ADR-002) остались stale пути в deny rules, check-permissions.py и agent frontmatter. Также обнаружен typo `CONTEX7_API_KEY` (должно быть `CONTEXT7_API_KEY`) в 2 активных файлах — Context7 MCP не резолвит API key из-за несовпадения имён env var (`{env:CONTEX7_API_KEY}` в opencode.json не матчит `CONTEXT7_API_KEY` в окружении, и наоборот). + +Зафиксировано как known issue: +- PR#24 digest: "env var `CONTEX7_API_KEY` (typo) захардкожена в 3 файлах... Не исправлено в PR#24 (вне scope). Следующий PR должен rename." +- PR#40 watch out: "check-permissions.py пути: скрипт использует `config/agents` и `config/opencode.json`, но файлы в `.opencode/`... Исправление путей — отдельный PR." + +Stale `config/scripts/` deny rules в opencode.json (lines 218, 223) — мёртвый код (широкие паттерны `python3 *pipeline-status.py*` / `python3 */pipeline-status.py*` уже работали, матчат любой путь). Узкие `python3 config/scripts/...` — нет, т.к. директория мигрировала. + +## Решение + +### 1. CONTEX7 typo fix (2 файла) +- `.env.example:12` — `CONTEX7_API_KEY` → `CONTEXT7_API_KEY` +- `.opencode/opencode.json:369` — `"{env:CONTEX7_API_KEY}"` → `"{env:CONTEXT7_API_KEY}"` + +Исторические `docs/handoff/pr-27-*` и `docs/decisions/007-pr-27-*` НЕ тронуты — это историческая документация миграции, не активный код. `CONTEX7` в них описывает что было dropped. + +### 2. Stale config/scripts paths → .opencode/scripts (4 файла) +- `.opencode/opencode.json:218,223` — deny rules `python3 config/scripts/pipeline-status.py*` / `python3 config/scripts/spec-status.py*` → `.opencode/scripts/...` +- `.opencode/scripts/check-permissions.py:10-11` — `REPO_ROOT / "config" / "agents"` / `REPO_ROOT / "config" / "opencode.json"` → `.opencode/agents` / `.opencode/opencode.json` +- `.opencode/agents/reviewer.md:72,77` — deny rules → `.opencode/scripts/...` +- `.opencode/agents/docs-reviewer.md:61,66` — deny rules → `.opencode/scripts/...` + +Узкие deny паттерны обновлены (НЕ удалены) — explicit > implicit. Широкие паттерны `python3 *pipeline-status.py*` / `python3 */pipeline-status.py*` уже покрывали (матчат любой путь), но узкие паттерны документируют конкретный path и добавляют defense-in-depth. + +### 3. Тесты (6 новых) +- `tests/test_permissions.py` — 4 теста: `CONTEXT7_API_KEY` present в `.env.example` и `opencode.json`; `CONTEX7_API_KEY` (typo) absent из обоих. +- `tests/test_check_permissions.py` — 2 теста: `AGENTS_DIR` points to `.opencode/agents/` (exists); `OPENCODE_JSON` points to `.opencode/opencode.json` (exists). + +## Альтернативы +- **Удалить узкие deny паттерны вместо обновления** — отклонено: issue допускал оба варианта, но обновление сохраняет explicit path documentation + defense-in-depth. Удаление оставило бы gap если широкие паттерны когда-то изменят. Consistency с actual `.opencode/scripts/` path. +- **Тронуть исторические `CONTEX7` references в docs/handoff/pr-27 и docs/decisions/007** — отклонено: issue явно запрещает ("НЕ трогать `docs/handoff/pr-27-*` и `docs/decisions/007-pr-27-*` — исторические"). Это историческая документация миграции, `CONTEX7` описывает что было dropped в PR#27. +- **Fix all `config/scripts` references (docstrings, prompt body)** — отклонено: вне scope hotfix. Issue перечисляет конкретные строки (deny rules, check-permissions.py paths, agent frontmatter deny rules). Cosmetic references в docstrings/usage comments (pipeline-status.py:10-11,319; spec-status.py:12-13; scaffold-handoff.sh:3; reviewer.md:99; docs-reviewer.md:111; spec/SKILL.md:28) — potential follow-up cleanup PR. +- **Добавить auto-migration для `.env` пользователей** — отклонено: `.env` gitignored, скрипт не может его редактировать. User-facing note в handoff Watch out — достаточно. \ No newline at end of file diff --git a/docs/handoff/pr-44-hotfix-config-paths.md b/docs/handoff/pr-44-hotfix-config-paths.md new file mode 100644 index 0000000..68e1e86 --- /dev/null +++ b/docs/handoff/pr-44-hotfix-config-paths.md @@ -0,0 +1,37 @@ +--- +pr: 44 +title: rename CONTEX7 typo + update stale config/scripts paths +--- + +# PR: rename CONTEX7 typo + update stale config/scripts paths + +## Что сделано +- `.env.example:12` — `CONTEX7_API_KEY` → `CONTEXT7_API_KEY` (typo fix). Context7 MCP теперь резолвит API key. +- `.opencode/opencode.json:369` — `"{env:CONTEX7_API_KEY}"` → `"{env:CONTEXT7_API_KEY}"` (typo fix в context7 MCP server config). +- `.opencode/opencode.json:218,223` — deny rules `python3 config/scripts/pipeline-status.py*` / `python3 config/scripts/spec-status.py*` → `.opencode/scripts/...` (stale paths после миграции config/ → .opencode/ PR#23). Широкие паттерны `python3 *pipeline-status.py*` / `python3 */pipeline-status.py*` уже работали (матчат любой путь); узкие `config/scripts/...` — мёртвый код. Узкие обновлены на актуальный путь. +- `.opencode/scripts/check-permissions.py:10-11` — `REPO_ROOT / "config" / "agents"` / `REPO_ROOT / "config" / "opencode.json"` → `.opencode/agents` / `.opencode/opencode.json`. Скрипт был бесполезен (paths не существовали → `AGENTS_DIR.exists()` / `OPENCODE_JSON.exists()` → False → false negative "OK"). +- `.opencode/agents/reviewer.md:72,77` — deny rules `python3 config/scripts/...` → `.opencode/scripts/...` (консистентно с global opencode.json). +- `.opencode/agents/docs-reviewer.md:61,66` — deny rules `python3 config/scripts/...` → `.opencode/scripts/...` (консистентно с global opencode.json). +- `tests/test_permissions.py` — 4 новых теста: `CONTEXT7_API_KEY` present в `.env.example` и `opencode.json`; `CONTEX7_API_KEY` (typo) absent из обоих файлов. +- `tests/test_check_permissions.py` — 2 новых теста: `AGENTS_DIR` points to `.opencode/agents/` (exists); `OPENCODE_JSON` points to `.opencode/opencode.json` (exists). +- ADR-018 + этот handoff + +## Почему +После миграции `config/` → `.opencode/` (PR#23) остались stale пути в deny rules, check-permissions.py и agent frontmatter (зафиксировано в PR#24 digest как known issue, в PR#40 watch out как pre-existing). Также обнаружен typo `CONTEX7_API_KEY` (должно быть `CONTEXT7_API_KEY`) — Context7 MCP не резолвит env var из-за несовпадения имён. + +Этот PR — hotfix, чинит 2 проблемы в минимальном scope: +1. Typo `CONTEX7` → `CONTEXT7` в 2 активных файлах (`.env.example`, `.opencode/opencode.json`). Исторические `docs/handoff/pr-27-*` и `docs/decisions/007-pr-27-*` НЕ тронуты — это историческая документация, не активный код. +2. Stale `config/scripts/` → `.opencode/scripts/` в deny rules (opencode.json), check-permissions.py paths, agent frontmatter (reviewer.md, docs-reviewer.md). Узкие deny паттерны актуализированы (широкие уже работали). + +Остальные `config/scripts` references (docstrings в pipeline-status.py, spec-status.py, scaffold-handoff.sh usage comment, prompt-body explanations в agents/skills) — ВНЕ scope этого hotfix. Они cosmetic (не functional — deny rules и paths исправлены), cleanup возможен в отдельном PR. + +## Pending +- Cosmetic `config/scripts` references в docstrings/usage comments (pipeline-status.py:10-11,319; spec-status.py:12-13; scaffold-handoff.sh:3; reviewer.md:99 prompt body; docs-reviewer.md:111 prompt body; spec/SKILL.md:28) — вне scope этого hotfix, potential follow-up cleanup PR. +- `CONTEX7_API_KEY` в `.env` пользователей (если скопировали из старого `.env.example`) — rename не автоматический, user-facing note может потребоваться. + +## Watch out +- **`.env` пользователей НЕ обновляется автоматически** — typo fix только в `.env.example`. Пользователи, скопировавшие `.env` из старого `.env.example`, имеют `CONTEX7_API_KEY` (typo) в своём `.env`. После pull им нужно вручную rename в `.env` → `CONTEXT7_API_KEY`, иначе context7 MCP не резолвит key. +- **Исторические `CONTEX7` references сохранены** — `docs/handoff/pr-27-configure-opencode-rewrite.md:6` и `docs/decisions/007-pr-27-configure-opencode-rewrite.md:11` НЕ тронуты (историческая документация). `check-adr-refs.py` не падает — эти файлы не содержат `ADR-NNN` refs к `CONTEX7`. +- **Узкие deny паттерны обновлены, НЕ удалены** — issue допускал оба варианта (обновить ИЛИ удалить, т.к. широкие `python3 *pipeline-status.py*` / `python3 */pipeline-status.py*` покрывают). Решено обновить (consistency с `.opencode/scripts/` actual path, explicit > implicit). Удаление оставило бы gap если широкие паттерны когда-то изменят. +- **check-permissions.py теперь реально проверяет конфиги** — до фикса скрипт возвращал false negative "OK" (paths не существовали → violations=[] → exit 0). После фикса он находит `.opencode/agents/*.md` и `.opencode/opencode.json` и реально валидирует. Существующие тесты `test_check_permissions_passes` / `test_clean_configs_pass` подтверждают exit 0 (конфиги чистые). +- ADR number = sequential (018), НЕ PR number. Проверить ADR naming в handoff до push (эволюция паттерна PR#26 docs-reviewer typo). \ No newline at end of file diff --git a/tests/test_check_permissions.py b/tests/test_check_permissions.py index 3c0072e..4e51fe0 100644 --- a/tests/test_check_permissions.py +++ b/tests/test_check_permissions.py @@ -39,6 +39,21 @@ def _load_script(): cp = _load_script() +# ── paths point to .opencode (issue #43) ──────────────────────────────────── + + +def test_agents_dir_points_to_opencode(): + """AGENTS_DIR points to .opencode/agents/ and the directory exists.""" + assert cp.AGENTS_DIR == REPO_ROOT / ".opencode" / "agents" + assert cp.AGENTS_DIR.exists(), f"AGENTS_DIR does not exist: {cp.AGENTS_DIR}" + + +def test_opencode_json_path_points_to_opencode(): + """OPENCODE_JSON points to .opencode/opencode.json and the file exists.""" + assert cp.OPENCODE_JSON == REPO_ROOT / ".opencode" / "opencode.json" + assert cp.OPENCODE_JSON.exists(), f"OPENCODE_JSON does not exist: {cp.OPENCODE_JSON}" + + # ── clean configs (subprocess black-box) ──────────────────────────────────── diff --git a/tests/test_permissions.py b/tests/test_permissions.py index 95a6a92..50de131 100644 --- a/tests/test_permissions.py +++ b/tests/test_permissions.py @@ -19,6 +19,7 @@ from pathlib import Path REPO_ROOT = Path(__file__).resolve().parent.parent OPENCODE_JSON = REPO_ROOT / ".opencode" / "opencode.json" CHECK_PERM_SCRIPT = REPO_ROOT / ".opencode" / "scripts" / "check-permissions.py" +ENV_EXAMPLE = REPO_ROOT / ".env.example" def _load_config() -> dict: @@ -26,6 +27,33 @@ def _load_config() -> dict: return json.load(f) +# ── CONTEXT7_API_KEY typo fix (issue #43) ──────────────────────────────────── + + +def test_context7_api_key_present_in_env_example(): + """CONTEXT7_API_KEY (correct) is present in .env.example.""" + content = ENV_EXAMPLE.read_text() + assert "CONTEXT7_API_KEY=" in content, "CONTEXT7_API_KEY missing from .env.example" + + +def test_context7_api_key_absent_in_env_example(): + """CONTEX7_API_KEY (typo) is absent from .env.example.""" + content = ENV_EXAMPLE.read_text() + assert "CONTEX7_API_KEY=" not in content, "typo CONTEX7_API_KEY still in .env.example" + + +def test_context7_api_key_present_in_opencode_json(): + """CONTEXT7_API_KEY (correct) is referenced in opencode.json (context7 MCP).""" + content = OPENCODE_JSON.read_text() + assert "{env:CONTEXT7_API_KEY}" in content, "CONTEXT7_API_KEY missing from opencode.json" + + +def test_context7_api_key_absent_in_opencode_json(): + """CONTEX7_API_KEY (typo) is absent from opencode.json.""" + content = OPENCODE_JSON.read_text() + assert "{env:CONTEX7_API_KEY}" not in content, "typo CONTEX7_API_KEY still in opencode.json" + + # ── global deny rules ───────────────────────────────────────────────────────