Repository navigation
fix(install): give gemini its own global skill dir on Windows (#2800) - #4770
nothariharan wants to merge 1 commit into
Conversation
…fy-Labs#2800) On Windows the gemini user-global skill went to ~/.agents/skills/graphify, the same path the `agents` platform installs to. The two ship different SKILL.md files, so installing either one overwrote the other, and uninstalling either one deleted the other's skill, references/ and stamp. The Windows branch came from Graphify-Labs#368 on the belief that Gemini CLI prefers ~/.agents/skills there. Gemini CLI reads both ~/.gemini/skills and the ~/.agents/skills alias on every OS (docs/cli/skills.md), with the alias winning a name clash, so the branch bought nothing and caused the clash. gemini now uses ~/.gemini/skills/graphify everywhere. Because the alias wins, a copy an older Windows gemini install left in ~/.agents/skills would hide the new one. gemini install now prints a note naming that folder instead of deleting it, since it may be a real `agents` install.
|
Thanks for the pull request, @nothariharan. A maintainer will review it soon. Want to talk it through while it is in review? Come join us on our Discord server. For longer-form discussion there is also GitHub Discussions. A couple of things that speed up review: make sure the test suite passes on Python 3.10 and 3.13, and that the change keeps extraction deterministic. |
There was a problem hiding this comment.
Graphify reviewed this change.
No issues found by static checks — no coupling regressions or blocking issues in the code graph. That is not the same as safe to merge: see what was not checked.
Not checked: tests were not run; formal verification proved 0 of 2 changed function(s) (2 not verified).
Formal verification. PR-changed functions: 0/2 verified (0 proven, 0 may-equivalent, 0 distinguished) · 2 not verified (2 vacuous).
Not verified on this run: \_platform\_skill\_destination (vacuous: never exercised), gemini\_install (vacuous: never exercised).
Graphify review — findings
Fixes #2800 by giving Gemini its own skill directory, ~/.gemini/skills/graphify, on every OS. On Windows it used to install into ~/.agents/skills, which is the agents platform's directory, so each platform could overwrite the other's skill and a Gemini uninstall deleted the agents one. Adds a note on user-level Gemini installs when an old ~/.agents/skills copy exists. Gemini CLI loads that copy ahead of the new one, so the message points at the folder instead of deleting it, since it may belong to the agents platform. Replaces the "shared directory is not guessed" test with a per-OS check that the two destinations differ. Adds a Windows install and uninstall test that confirms the agents skill tree is left intact.
No blocking issues surfaced. 1 lower-confidence candidate did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 714 functions depend on the 308 functions this change touches.
Health — this change adds coupling hotspots:
- worse:
gemini_install()— 14 callers, 10 callees - worse:
gemini_uninstall()— 10 callers, 6 callees
Verification — 714 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 598 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
43 of 453 test file(s) selected (9%) via static blast radius.
tests/test_affected_cli.py— impacttests/test_agents_platform.py— impacttests/test_atomic_version_stamp.py— impacttests/test_claude_md.py— impacttests/test_codebuddy.py— impacttests/test_dedup_shrink_refuses_force_write.py— impacttests/test_detect.py— impacttests/test_devin.py— impacttests/test_explain_cli.py— impacttests/test_explain_same_file_tie.py— impacttests/test_extract_cli.py— impacttests/test_gemini_hook.py— impacttests/test_global_add_tag_inference.py— impacttests/test_god_nodes_cli.py— impacttests/test_hollow_chunks_arm_shrink_guard.py— impacttests/test_home_sandbox.py— impacttests/test_hook_strict.py— impacttests/test_incomplete_build_guard.py— impacttests/test_install.py— impacttests/test_install_hook_identity.py— impacttests/test_install_line_endings.py— impacttests/test_install_references.py— impact, changed-testtests/test_install_strings.py— impacttests/test_install_version_warning.py— impacttests/test_lookup_exit_codes.py— impacttests/test_merge_chunks_validation.py— impacttests/test_multigraph_diagnostics.py— impacttests/test_no_dedup_flag.py— impacttests/test_opencode_plugin_refresh.py— impacttests/test_path_cli.py— impacttests/test_pi_guard_install.py— impacttests/test_query_cli.py— impacttests/test_query_induced_edges.py— impacttests/test_query_seeds.py— impacttests/test_read_hook.py— impacttests/test_replace_or_append_section.py— impacttests/test_search_hook.py— impacttests/test_skill_auto_refresh.py— impact, changed-testtests/test_skill_version_warning.py— impacttests/test_truncation_full_answer.py— impacttests/test_uninstall_purge_safety.py— impacttests/test_uninstall_scope.py— impact, changed-testtests/test_unverified_semantic_shrink.py— impact
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
Formal verification
Could not verify: Could not verify \_platform\_skill\_destination.
The verifier did not have enough to check \_platform\_skill\_destination, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: no capturable inputs from the test suite; property tier: not verifiable: all 41 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly KeyError — names the real obstacle, not a sampling gap)
Could not verify: Could not verify gemini\_install.
The verifier did not have enough to check gemini\_install, so it is saying so rather than guessing. No false assurance is the whole point.
Guarantee: No guarantee either way, this is an honest abstention, not a pass.
Note: Reason: no capturable inputs from the test suite; property tier: not verifiable: all 10 sampled inputs raised on both versions — the function never executed, so 'no divergence' would be vacuous (mostly NameError — names the real obstacle, not a sampling gap)
· 1 grounded finding(s) anchored inline below; 1 more finding(s) on lines outside this diff (see the check run).
| print(f" note: Gemini CLI loads {shadow} ahead of this install. If an older") | ||
| print(" `graphify install --platform gemini` wrote it, delete that folder;") | ||
| print(" if you use the `agents` platform, keep it.") | ||
| def gemini_install(project_dir: Path | None = None, *, project: bool = False) -> None: |
There was a problem hiding this comment.
gemini_install()
fans out to 10 callees (efferent coupling); 14 callers depend on it (afferent coupling).
Grounded coupling-delta finding (deterministic), not an LLM guess.
What does this PR do?
Fixes #2800. On Windows,
graphify install --platform geminiwrote its user-global skill to~/.agents/skills/graphify/SKILL.md, the same path theagentsplatform uses. The two platforms ship different files (skill.mdvsskill-agents.md), so installing one overwrote the other, andgraphify uninstallfor either one deleted the other'sSKILL.md,references/and version stamp.The issue asked which layout is intended. The Windows branch came from #368 (v0.4.15), on the belief that Gemini CLI prefers
~/.agents/skillson Windows. Gemini CLI's own docs (docs/cli/skills.md) list the user tier as "~/.gemini/skills/or the~/.agents/skills/alias" with nothing OS-specific. Both paths are read on every OS, and the alias wins a name clash. The #368 report itself was a missing--platformflag. So this takes option 2 from the issue: gemini uses~/.gemini/skills/graphifyeverywhere.Since the alias wins a name clash, a copy that an older Windows gemini install left in
~/.agents/skills/graphifywould hide the fresh one.gemini installnow prints a note naming that folder. It does not delete it, because it may be a realagentsinstall.Type of change
Verification & Invariants
Invariant: two platforms never share one skill destination, so one platform's install or uninstall cannot touch another's files.
test_windows_gemini_install_and_uninstall_leave_the_agents_skillfails because the gemini skill lands in~/.agentsover the planted agents skill.test_gemini_and_agents_own_separate_skill_dirs[Windows]fails because the destination is.agents/skills/graphify/SKILL.md.tests/test_uninstall_scope.pyandtests/test_skill_auto_refresh.py.test_a_directory_shared_by_two_platforms_is_not_guessedrelied on the collision itself (it asserted gemini == agents on Windows), so it is replaced by the destination test above. The shared-dir guard it exercised is still covered by the copilot/vscode and symlink tests beside it.~/.agents/skills/graphifyis left in place. They get the note instead.How was this tested?
On Windows 11 with Python 3.12, the only OS where the old branch is reachable (CI is ubuntu-only).
Graphify-specific checklist