You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* docs(troubleshooting): add quick-reference for the 4 most common support patterns
These four issues account for the majority of support questions:
1. "Hooks use a matcher + hooks array" — users on pre-v2.2.3 releases.
Answer: upgrade to v2.2.4 and re-run `code-review-graph install`.
2. "code-review-graph: command not found" after pip install — PATH issue.
Answer: use pipx, uvx, or `python -m code_review_graph`.
3. "Is this project-scoped or user-scoped?" — 4-piece scope table
(package is user-scoped, graph+mcp config are project-scoped,
multi-repo registry is user-scoped).
4. "Built the graph but Claude Code doesn't see it in a new session" —
didn't restart Claude Code / wrong cwd / ran build but not install.
The new section is placed at the top of TROUBLESHOOTING.md so it's
found first by anyone searching for these error messages.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: add Elixir parser support (#112)
Closes#112. tree-sitter-elixir is already bundled with
tree-sitter-language-pack so no new runtime dep.
Elixir's AST is uniform: ``defmodule``, ``def``/``defp``/``defmacro``,
``alias``/``import``/``require``/``use``, and ordinary function
invocations all share the ``call`` node type and are distinguished
only by the leading identifier text. Added a dedicated
``_extract_elixir_constructs`` helper (following the R and Lua pattern)
that dispatches by first-identifier:
- ``defmodule Name do ... end`` -> Class node, recurse into do_block
with ``enclosing_class=Name``
- ``def`` / ``defp`` / ``defmacro`` / ``defmacrop`` -> Function/Test
node attached to the enclosing module, recurse into do_block with
``enclosing_func=<fn>`` so nested calls resolve to qualified names
- ``alias`` / ``import`` / ``require`` / ``use`` -> IMPORTS_FROM edge
targeting the module name (flat or dotted, e.g. ``Foo.Bar``)
- Anything else -> CALLS edge with the leading identifier as the target
name, followed by recursion into arguments + do_block for nested
calls
Helpers added:
- ``_elixir_call_identifier`` — handles both plain identifiers and
``dot > alias + identifier`` for ``IO.puts`` style calls
- ``_elixir_module_name`` — extracts a module name from an arguments
subtree (``alias`` node or ``dot`` node)
- ``_elixir_function_name_and_params`` — extracts the function name
from the inner call node inside ``def``'s arguments
Verified on ``tests/fixtures/sample.ex`` (two modules with 6 functions
total across public/private def/defp, plus alias/import/require):
- 9 nodes, 16 edges
- 2 Class nodes (Calculator, MathHelpers)
- 6 Function nodes with correct parent_name
- 3 IMPORTS_FROM (alias, import, require)
- 5 CALLS with internal resolution: Calculator.compute -> Calculator.add,
MathHelpers.double -> Calculator.compute, MathHelpers.triple ->
MathHelpers.double, etc.
Tests (7 new in TestElixirParsing):
- Language detection (.ex + .exs)
- Module -> Class mapping
- def/defp producing Function nodes with correct parents
- alias/import/require producing IMPORTS_FROM
- Internal call resolution to qualified names
- CONTAINS edges linking module -> functions
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: apply_refactor_tool dry-run mode returning unified diffs (#176)
Closes#176. The existing rename_preview / apply_refactor flow was
technically two-step, but apply_refactor wrote to disk immediately so
there was no way to inspect the exact diff before committing.
apply_refactor() now takes an optional ``dry_run: bool`` parameter. When
true:
- Compute the same edit plan as the real write path, grouping edits by
file so multiple edits to the same file apply sequentially against
updated content (fixes a subtle bug where separate edits on the same
file could stomp each other)
- Return a unified-diff string per would-be-modified file using
``difflib.unified_diff``
- Leave the refactor_id in the pending cache so the user can review the
diff then call again with ``dry_run=False`` to commit
The MCP tool wrapper (``apply_refactor_tool`` in main.py +
``apply_refactor_func`` in tools/refactor_tools.py) now exposes the
``dry_run`` parameter. The real-write path was also refactored to
share the plan-computation step with dry-run — multi-edit files now
work correctly in both modes.
Tests (2 new in test_refactor.py):
- test_apply_refactor_dry_run_returns_diff_without_writing: confirms
the diff contains both old and new names, the file on disk is
unchanged, the refactor_id is still valid after the dry-run, and a
follow-up real apply with the same id succeeds and consumes the id.
- test_apply_refactor_dry_run_no_edits: empty edit list returns an
empty diffs dict.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: CRG_DATA_DIR / CRG_REPO_ROOT env vars for out-of-tree graph storage (#155)
Closes#155 (supersedes #207 which had unresolved review comments about
input()-on-stdio and _local_only fragility). New env vars let users
keep the .code-review-graph directory outside their working tree —
useful for ephemeral workspaces, Docker volumes, shared CI caches, and
multi-repo orchestrators where scripting the CLI from outside the
repo is necessary.
## CRG_DATA_DIR
When set, replaces the default ``<repo>/.code-review-graph`` directory
verbatim. New ``get_data_dir(repo_root)`` helper in incremental.py is
the single source of truth — it creates the directory (respecting the
override) and writes the auto-generated inner ``.gitignore`` with ``*``
so graph files are never committed.
Call sites updated to use ``get_data_dir()``:
- ``incremental.py::get_db_path`` (the graph.db location)
- ``cli.py::visualize`` (graph.html)
- ``cli.py::wiki`` (wiki output dir)
- ``tools/docs.py::generate_wiki_func`` (MCP wiki tool)
- ``tools/docs.py::get_wiki_page_func`` (MCP wiki lookup)
The legacy ``<repo>/.code-review-graph.db`` migration is preserved
but only runs when CRG_DATA_DIR is unset (there's no relationship
between the legacy location and an override location).
## CRG_REPO_ROOT
``find_project_root()`` now checks ``CRG_REPO_ROOT`` before the usual
git-root walk. Useful for anyone scripting ``code-review-graph`` from
a cwd that isn't inside the target repo (daemons, CI jobs, wrappers).
Falls through to normal resolution if the path doesn't exist.
## Deliberate design choices (vs PR #207)
- **No input() / confirmation prompts** — this branch of code runs
from the CLI entry and can also be pulled into MCP wrappers which
pipe stdio. Reading from stdin there would corrupt JSON-RPC.
- **No ``mcp._local_only`` attribute** — that approach was fragile
because it touched a private FastMCP attribute.
- **Tilde expansion** — both env vars go through ``Path.expanduser()``
so ``CRG_DATA_DIR=~/Library/crg`` works.
Tests (5 new in TestDataDir):
- test_default_uses_repo_subdir — unset env, default behavior
- test_env_override_replaces_repo_subdir — CRG_DATA_DIR works
- test_get_db_path_uses_data_dir — graph.db follows the override
- test_find_project_root_env_override — CRG_REPO_ROOT works
- test_find_project_root_env_override_missing_dir_falls_through —
bogus override doesn't crash, falls back to git root
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* feat: stderr warning before cloud embedding providers exfiltrate code (#174)
Closes#174 (supersedes #179 which overlapped with #207 and used
input()-on-stdio that would corrupt MCP transport).
``get_provider()`` now prints an explicit warning to **stderr** (never
stdout/stdin) before returning a Google Gemini or MiniMax provider:
⚠️ code-review-graph: about to embed code via the 'google'
cloud provider.
Your source code (function names, docstrings, file paths)
will be sent to an external API.
To skip this warning in future runs, set
CRG_ACCEPT_CLOUD_EMBEDDINGS=1 in your environment.
To stay fully offline, use the default 'local' provider
instead (no API key needed).
Why stderr: the MCP stdio transport uses stdout for JSON-RPC. Writing
to stdout or reading from stdin would corrupt the transport. stderr
is safe and is captured by Claude Code's MCP logs.
Why a warning not a block: the user already explicitly chose a cloud
provider (either via ``provider="google"`` arg or via the MiniMax API
key + explicit provider selection). Blocking would be paternalistic;
warning is informative.
The ``CRG_ACCEPT_CLOUD_EMBEDDINGS=1`` env var lets CI / scripted
workflows acknowledge once and move on without repeated noise.
Tests (4 new in TestCloudProviderWarning):
- test_minimax_triggers_stderr_warning — captures stderr, asserts
warning content, asserts stdout stays clean
- test_google_triggers_stderr_warning — same for google
- test_accept_env_var_suppresses_warning — CRG_ACCEPT_CLOUD_EMBEDDINGS=1
silences the warning
- test_local_provider_never_warns — offline path stays quiet
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
0 commit comments