Skip to content

fix(install): give gemini its own global skill dir on Windows (#2800) - #4770

Open
nothariharan wants to merge 1 commit into
Graphify-Labs:v8from
nothariharan:fix/gemini-windows-skill-path
Open

nothariharan wants to merge 1 commit into
Graphify-Labs:v8from
nothariharan:fix/gemini-windows-skill-path

Conversation

@nothariharan

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes #2800. On Windows, graphify install --platform gemini wrote its user-global skill to ~/.agents/skills/graphify/SKILL.md, the same path the agents platform uses. The two platforms ship different files (skill.md vs skill-agents.md), so installing one overwrote the other, and graphify uninstall for either one deleted the other's SKILL.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/skills on 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 --platform flag. So this takes option 2 from the issue: gemini uses ~/.gemini/skills/graphify everywhere.

Since the alias wins a name clash, a copy that an older Windows gemini install left in ~/.agents/skills/graphify would hide the fresh one. gemini install now prints a note naming that folder. It does not delete it, because it may be a real agents install.

Type of change

  • Bug fix

Verification & Invariants

Invariant: two platforms never share one skill destination, so one platform's install or uninstall cannot touch another's files.

  • Reproduced: on v8, both new tests fail. test_windows_gemini_install_and_uninstall_leave_the_agents_skill fails because the gemini skill lands in ~/.agents over the planted agents skill. test_gemini_and_agents_own_separate_skill_dirs[Windows] fails because the destination is .agents/skills/graphify/SKILL.md.
  • Smallest fix: one branch removed, plus the shadowing note.
  • Regression tests added in tests/test_uninstall_scope.py and tests/test_skill_auto_refresh.py.
  • test_a_directory_shared_by_two_platforms_is_not_guessed relied 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.
  • Limitation: an existing Windows user's old copy in ~/.agents/skills/graphify is 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).

uv run --frozen pytest tests/test_uninstall_scope.py tests/test_skill_auto_refresh.py tests/test_install_references.py tests/test_install.py -q
  -> 204 passed, 4 skipped
same new tests with graphify/install.py reverted to v8 -> 2 failed (the Windows cases)
uv run --frozen pytest tests/ -q -> 7 failed, 8651 passed, 86 skipped. The same 7 tests fail on unmodified v8 on this machine (8648 passed): aider/devin code-only update, a shebang parse test, a watch relative-roots test, two getcwd memoization tests, one alias-resolution test. None is new.
uv run --frozen ruff check (changed files) -> All checks passed
uv run --frozen pyright graphify/install.py -> no errors beyond the ones v8 already has

Graphify-specific checklist

  • No generated skill artifacts touched.
  • No change to AST/structural extraction.
  • No shell/Python interpolation added.
  • No API keys or local graph data included.

…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.
@github-actions

Copy link
Copy Markdown

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.

@graphify-labs graphify-labs Bot left a comment •

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 — impact
  • tests/test_agents_platform.py — impact
  • tests/test_atomic_version_stamp.py — impact
  • tests/test_claude_md.py — impact
  • tests/test_codebuddy.py — impact
  • tests/test_dedup_shrink_refuses_force_write.py — impact
  • tests/test_detect.py — impact
  • tests/test_devin.py — impact
  • tests/test_explain_cli.py — impact
  • tests/test_explain_same_file_tie.py — impact
  • tests/test_extract_cli.py — impact
  • tests/test_gemini_hook.py — impact
  • tests/test_global_add_tag_inference.py — impact
  • tests/test_god_nodes_cli.py — impact
  • tests/test_hollow_chunks_arm_shrink_guard.py — impact
  • tests/test_home_sandbox.py — impact
  • tests/test_hook_strict.py — impact
  • tests/test_incomplete_build_guard.py — impact
  • tests/test_install.py — impact
  • tests/test_install_hook_identity.py — impact
  • tests/test_install_line_endings.py — impact
  • tests/test_install_references.py — impact, changed-test
  • tests/test_install_strings.py — impact
  • tests/test_install_version_warning.py — impact
  • tests/test_lookup_exit_codes.py — impact
  • tests/test_merge_chunks_validation.py — impact
  • tests/test_multigraph_diagnostics.py — impact
  • tests/test_no_dedup_flag.py — impact
  • tests/test_opencode_plugin_refresh.py — impact
  • tests/test_path_cli.py — impact
  • tests/test_pi_guard_install.py — impact
  • tests/test_query_cli.py — impact
  • tests/test_query_induced_edges.py — impact
  • tests/test_query_seeds.py — impact
  • tests/test_read_hook.py — impact
  • tests/test_replace_or_append_section.py — impact
  • tests/test_search_hook.py — impact
  • tests/test_skill_auto_refresh.py — impact, changed-test
  • tests/test_skill_version_warning.py — impact
  • tests/test_truncation_full_answer.py — impact
  • tests/test_uninstall_purge_safety.py — impact
  • tests/test_uninstall_scope.py — impact, changed-test
  • tests/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).

Comment thread graphify/install.py
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:

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Health regression — 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.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

contributor Pull request from a returning community contributor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

On Windows, gemini and agents share one global skill path (~/.agents/skills/graphify), so installing or uninstalling either one clobbers the other

1 participant