diff --git a/.github/workflows/codespell.yml b/.github/workflows/codespell.yml new file mode 100644 index 000000000..6d28abebf --- /dev/null +++ b/.github/workflows/codespell.yml @@ -0,0 +1,23 @@ +# Codespell configuration is within pyproject.toml +--- +name: Codespell + +on: + push: + branches: [v8] + pull_request: + branches: [v8] + +permissions: + contents: read + +jobs: + codespell: + name: Check for spelling errors + runs-on: ubuntu-latest + + steps: + - name: Checkout + uses: actions/checkout@v6 + - name: Codespell + uses: codespell-project/actions-codespell@8f01853be192eb0f849a5c7d721450e7a467c579 # v2.2 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 5e762eb4d..ae8cbbc06 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -20,3 +20,11 @@ repos: hooks: - id: ruff args: ["--config", "pyproject.toml"] + + - repo: https://github.com/codespell-project/codespell + # Configuration for codespell is in pyproject.toml + rev: v2.4.2 + hooks: + - id: codespell + additional_dependencies: + - tomli; python_version<'3.11' diff --git a/docs/superpowers/plans/2026-05-04-incremental-updates-dedup.md b/docs/superpowers/plans/2026-05-04-incremental-updates-dedup.md index 488591b8d..83c4d7871 100644 --- a/docs/superpowers/plans/2026-05-04-incremental-updates-dedup.md +++ b/docs/superpowers/plans/2026-05-04-incremental-updates-dedup.md @@ -101,7 +101,7 @@ def test_entropy_empty_string(): def test_shingles_produces_trigrams(): s = _shingles("hello") - assert "hel" in s + assert "hel" in s # codespell:ignore hel assert "ell" in s assert "llo" in s diff --git a/graphify/extract.py b/graphify/extract.py index 30f31d329..1e45d25a4 100644 --- a/graphify/extract.py +++ b/graphify/extract.py @@ -5510,7 +5510,7 @@ def _has_import_evidence(candidate_id: str) -> bool: # `indirect_call` and ONLY when the target is a real callable def — # never a same-named data symbol. Stays INFERRED even with import # evidence: the name is referenced as a value here, not invoked. Dedup - # is call-aware (an existing direct `calls` edge pre-empts it; a benign + # is call-aware (an existing direct `calls` edge preempts it; a benign # `imports` edge to the same symbol does NOT suppress it). if tgt != caller and (caller, tgt) not in call_like_pairs and tgt in callable_nids and tgt not in class_nids: call_like_pairs.add((caller, tgt)) diff --git a/graphify/extractors/dart.py b/graphify/extractors/dart.py index acbe19583..200f6528e 100644 --- a/graphify/extractors/dart.py +++ b/graphify/extractors/dart.py @@ -480,7 +480,7 @@ def _find_matching_brace(text: str, start_pos: int) -> int: add_node(bloc_nid, bloc_name, source_file=None) add_edge(nid, bloc_nid, "references", context="bloc_lookup") - # Universal Navigation Patters (GoRouter, AutoRoute, Navigator) + # Universal Navigation Patterns (GoRouter, AutoRoute, Navigator) for nm in re.finditer(r"\b(?:go|push|goNamed|pushNamed|replace|replaceNamed)\s*\(\s*(?:context\s*,\s*)?['\"]([a-zA-Z0-9_/?=&%-]+)['\"]", func_body): route_path = nm.group(1) route_nid = _make_id("route", route_path.replace("/", "_").replace("?", "_").replace("=", "_").replace("&", "_")) diff --git a/graphify/llm.py b/graphify/llm.py index 30d7a6d6f..e9e5c903c 100644 --- a/graphify/llm.py +++ b/graphify/llm.py @@ -470,7 +470,7 @@ def _thinking_disabled_via_env() -> bool: Edge direction rule — source is always the ACTOR, target is the ACTED-UPON: - calls: source = the function/method that CONTAINS the call site; target = the function/method BEING CALLED. Never reverse this. - imports/references: source = the file/entity that imports or references; target = the thing imported or referenced. -- implements/inherits: source = the subclass/implementor; target = the base class/interface. +- implements/inherits: source = the subclass/implementer; target = the base class/interface. Hyperedges: if 3 or more nodes clearly participate together in a shared concept, flow, or pattern that is not captured by pairwise edges alone, add a hyperedge to the top-level `hyperedges` array (e.g. all classes implementing one protocol, all functions in one auth flow even if they don't all call each other, all concepts from a paper section forming one coherent idea). Use sparingly — only when the group relationship adds information beyond the pairwise edges. Maximum 3 hyperedges per chunk. diff --git a/graphify/tree_html.py b/graphify/tree_html.py index 1dc658ff1..b77157f9c 100644 --- a/graphify/tree_html.py +++ b/graphify/tree_html.py @@ -1,6 +1,6 @@ """tree_html — emit a D3 v7 collapsible-tree HTML view of a graph. -A self-contained printable / browseable tree-of-modules view +A self-contained printable / browsable tree-of-modules view intended to complement the existing force-directed ``graph.html``. Key visual elements: diff --git a/pyproject.toml b/pyproject.toml index c9b550983..15504fd9a 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -150,3 +150,26 @@ select = ["E9", "F63", "F7", "F82"] include = ["graphify", "tests"] pythonVersion = "3.10" typeCheckingMode = "basic" + +[tool.codespell] +# Ref: https://github.com/codespell-project/codespell#using-a-config-file +# worked/ contains sample outputs (graph.json/html snapshots from real repos) +# whose truncated string identifiers extracted from source are not typos in +# the codebase itself. translations/ (basename glob, matches docs/translations/) +# hosts foreign-language README variants that codespell (English-only) misreads. +skip = '.git,.gitignore,.gitattributes,*.svg,*.lock,worked,translations' +check-hidden = true +# Per-word rationale for ignore-words-list (single TOML string, per-entry +# comments are not possible inside the value): +# unparseable - consistent alternate spelling used across graphify/ (35x) +# bloc - Dart BLoC pattern (Business Logic Component), not "block" +# inout - SQL PROCEDURE param mode / Verilog keyword (not "input") +# dependant - FastAPI class name shown in README example output +# inh - test-local variable holding "inherits" edge set (tests/) +# datas - matches edge_datas() function name from graphify.build +# nd - short variable for graph node data (wiki.py, engine.py) +# bu - short variable for Bedrock usage dict (llm.py) +# anc - short variable for ancestor path (resolution.py) +# ans - short variable for answer file path (tests/test_reflect.py) +# oder,ist - German stopwords in serve.py's stopword list +ignore-words-list = 'unparseable,bloc,inout,dependant,inh,datas,nd,bu,anc,ans,oder,ist' diff --git a/tests/test_dedup.py b/tests/test_dedup.py index e1370fc7a..36fc80c15 100644 --- a/tests/test_dedup.py +++ b/tests/test_dedup.py @@ -20,7 +20,7 @@ def test_entropy_empty_string(): def test_shingles_produces_trigrams(): s = _shingles("hello") - assert "hel" in s + assert "hel" in s # codespell:ignore hel assert "ell" in s assert "llo" in s