490 Commits

Author SHA1 Message Date
Safi 96e17adf4d bump version to 0.8.9 v0.8.9 2026-05-17 11:59:57 +01:00
Safi 46738a1365 add DeepSeek to README privacy section 2026-05-17 11:54:54 +01:00
Safi 9b884f7d1a add deepseek backend (deepseek-v4-flash, DEEPSEEK_API_KEY) 2026-05-17 11:49:11 +01:00
cinos ec4c87c86e fix(export): accept edges-only graph JSON for wiki export (#909)
Co-authored-by: Hermes Agent <hermes-agent@users.noreply.github.com>
2026-05-17 11:32:55 +01:00
Christopher Beaulieu 2aaa216825 fix(analyze): exclude npm dep-block keys from god-node selection (#905)
* fix(analyze): exclude npm dep-block keys from god-node selection

Extends _JSON_NOISE_LABELS in graphify/analyze.py with the six npm
package.json dependency-block keys (dependencies, devDependencies,
peerDependencies, optionalDependencies, bundledDependencies,
bundleDependencies — lowercased to match the existing .strip().lower()
comparison in _is_json_key_node).

On JS/TS corpora with non-trivial dependency counts, the dep-block key
node accumulates contains+imports edges to every package entry and was
surfacing as the top god-node in the report. The fix is a one-line
extension of the existing frozenset; no new helpers or code paths.

Includes a parametrized regression test in tests/test_analyze.py
covering all five npm keys: dependencies, devDependencies,
peerDependencies, optionalDependencies, bundledDependencies.

Validated live against rsl-siege-manager @ 6085fd66 — zero npm dep-block
keys in the top-10 god-nodes after the fix. Pre-existing failures
(Windows symlink + SQL tree-sitter): 17, unchanged.

Closes #2

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* style: drop fork-local issue refs from analyze comments

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-17 11:32:53 +01:00
Christopher Beaulieu 6018831193 fix(llm): force UTF-8 encoding on _call_claude_cli subprocess + loud failure on chunk errors (#906)
* fix(extract): force UTF-8 encoding on subprocess + loud failure on chunk errors

On Windows cp1252, subprocess.run(..., text=True) without encoding= raises
UnicodeEncodeError for chars like →  ≥ in chunk content. Both
_call_claude_cli (llm.py:426) and the _call_llm claude-cli branch (llm.py:959)
lacked encoding=.

- Add encoding="utf-8" to both subprocess.run sites.
- Track failed_chunks in extract_corpus_parallel merged result dict.
- Print [graphify] WARNING: N/M semantic chunk(s) failed summary to stderr at
  end of run when any chunk failed, so silent partial failures are visible.
- Add tests/test_charmap_encoding.py: 10 regression tests covering subprocess
  encoding kwarg, loud-failure summary, and substitution validation.

Closes #3

* style: drop fork-local issue refs from llm/test comments
2026-05-17 11:32:38 +01:00
Safi 0ca8d3d9f7 bump version to 0.8.8 2026-05-16 23:52:24 +01:00
Safi b4c0f01bfd update README and CHANGELOG for graphify prs (0.8.8) v0.8.8 2026-05-16 22:55:43 +01:00
Safi 6d54b25d20 stop tracking uv.lock (library, not app) 2026-05-16 22:38:33 +01:00
Safi cc9e5816a7 add graphify prs: graph-aware PR dashboard with triage, worktrees, conflict detection
- new `graphify prs` subcommand: terminal dashboard of open PRs with CI/review
  state, worktree mapping, and graph impact (blast radius / communities touched)
- `graphify prs <number>`: deep dive on a single PR
- `graphify prs --triage`: AI triage ranking via any configured backend
  (claude, kimi, openai, gemini, claude-cli, ollama — auto-detected from env)
- `graphify prs --worktrees`: worktree → branch → PR mapping
- `graphify prs --conflicts`: PRs sharing graph communities with node labels
- concurrent gh pr diff fetching via ThreadPoolExecutor (up to 8 workers)
- graph impact lazy: only fetched when needed (deep dive / triage / conflicts)
- MCP tools: list_prs, get_pr_impact, triage_prs
- auto-detects default branch via gh repo view → git symbolic-ref → main
- 41 tests, all passing; uv.lock added to .gitignore

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 22:36:58 +01:00
Safi d717415522 Bump version to 0.8.7, update CHANGELOG
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.8.7
2026-05-16 20:25:04 +01:00
Safi b82d5d147f fix review findings from #898 #895 #899 corrections
- Revert .h -> extract_c (C++ grammar rejects C++ keywords used as identifiers
  in Linux-kernel-style headers; .hpp/.hxx/.hh already route to extract_cpp)
- Fix field_declaration block: use children_by_field_name("declarator") instead
  of iterating all children with wrong type guard; replace ensure_node (undefined)
  with add_node
- Fix _import_c include resolution: use _make_id(str(resolved)) to match the
  file_nid scheme _extract_generic uses, not _make_id(_file_stem(resolved))
- Fix exact_merges counter in dedup Pass 1 to count only within-file merges
  actually performed, not the raw unpartitioned group sizes

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 20:20:49 +01:00
Safi 500e4a732d fix #898 #895 #899: C++ header extraction, dedup cross-file merge, include path resolution
- Route .h files through extract_cpp (was extract_c), fixing missing method nodes in C++ headers
- Extend _get_cpp_func_name to handle field_identifier, destructor_name, operator_name
- Add CPP-specific field_declaration branch in _extract_generic to emit class method/field nodes
- Partition dedup Pass 1 by source_file: only union same-label nodes within the same file;
  cross-file matches fall through to Pass 2 fuzzy, preventing generic-label god nodes
- Add _resolve_c_include_path: resolve quoted #include paths to real files on disk so
  target node IDs match what _extract_generic creates, fixing dangling include edges

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 20:12:09 +01:00
Safi a316590adc Fix query seed scoring: IDF weighting, dynamic K seeds, actionable truncation
Common terms like 'error'/'exception' were stealing BFS seed slots from
rare identifiers like 'FooBarService', burning the token budget on noise.

- _compute_idf: weights query terms by inverse document frequency, cached
  on G.graph so cost is paid once per graph load not per query
- _score_nodes: multiplies each tier bonus by IDF weight
- _pick_seeds: replaces fixed top-3 with gap-ratio selection — stops adding
  seeds when score drops below 20% of the top match
- _subgraph_to_text: truncation hint now tells Claude to narrow with
  context_filter or use get_node instead of just saying 'truncated'

Fixes #897

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 16:20:20 +01:00
Safi b1ade00ece Bump version to 0.8.6, update CHANGELOG
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.8.6
2026-05-16 14:37:32 +01:00
Safi 0157d5f6ad Add pytest testpaths to stop crawling external repos in subdirs
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 14:30:38 +01:00
Safi 766566f6a8 Fill test gaps from PR #890 and fix case-insensitive path in _is_json_key_node
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 14:27:01 +01:00
Safi aa541a680a Suppress code-doc INFERRED edges and filter JSON key nodes from god_nodes
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-16 14:23:38 +01:00
Safi 8105266800 Add .gitattributes to fix GitHub language detection showing HTML instead of Python 2026-05-16 14:16:07 +01:00
Safi f86b0309e7 Merge pull request #891 from jattree/fix/install-guidance-query-first
Clean merge — fixes real token-bloat regression where GRAPH_REPORT.md was loaded on every question. All 10 install surfaces now route to graphify query first. Also fixes hidden bug where re-running graphify install after upgrade didn't refresh stale hook text.
2026-05-16 14:13:01 +01:00
Safi c74b95c44c Merge pull request #888 from kanya-approve/feat/nuitka-build
Clean merge — PEP 735 [dependency-groups] is invisible to pip/uv tool install, zero runtime impact.
2026-05-16 14:12:58 +01:00
Safi 1ba3e37d27 Merge pull request #887 from alphanury/feat/auto-detect-symlinks-default
Clean merge — fixes real silent data-loss bug in --update on symlinked repos, backward-compatible, inherits existing cycle protection. Follow-up: consider tightening _auto_follow_symlinks to is_symlink() and is_dir() to avoid tripping on incidental symlinked files at root.
2026-05-16 14:12:54 +01:00
Safi 31a9f13cc7 Merge pull request #886 from kanya-approve/chore/commit-uv-lock
Clean merge — uv.lock is correct to commit per current uv guidance, all 152 packages from PyPI only, zero impact on pip/wheel consumers.
2026-05-16 14:12:52 +01:00
Jon Attree d1a2c3f958 Stop telling assistants to read GRAPH_REPORT.md first (#580)
The current install writes "ALWAYS read graphify-out/GRAPH_REPORT.md
before reading any source files, running grep/glob searches, or
answering codebase questions" into CLAUDE.md and equivalents, plus a
PreToolUse hook with the same instruction. On real corpora that report
is 47-91K characters, so Claude Code sessions pay roughly 12-25K tokens
of context up front for every search-able question. Three users on #580
reported this making token usage worse than no install at all.

Reproduced on a 1500-file Go monorepo: a "where is X defined" question
takes 5 tool calls and 34k agent tokens with stock graphify, 4 calls
and 30k tokens with no install, and 1 call and 30k tokens after this
patch. Stock graphify's Read of GRAPH_REPORT.md hit Claude Code's 25k
token cap and failed entirely, then recovered via a partial read plus
graphify explain.

Demote GRAPH_REPORT.md to a fallback for broad architecture review and
route first action to the existing scoped commands: graphify query,
path, explain. The 2k-budget BFS subgraph already exists in serve.py;
the install just wasn't pointing at it.

Updated across all ten install surfaces: _SETTINGS_HOOK,
_CLAUDE_MD_SECTION, _AGENTS_MD_SECTION, _GEMINI_MD_SECTION,
_GEMINI_HOOK, _VSCODE_INSTRUCTIONS_SECTION, _ANTIGRAVITY_RULES,
_KIRO_STEERING, _CURSOR_RULE, _OPENCODE_PLUGIN_JS. Plus the matching
sentence in README.md, which also fixes an inaccuracy about Codex
hooks (Codex's installed hook is intentionally a no-op because Codex
rejects additionalContext, so the guidance there comes from AGENTS.md,
not the hook).

Five installers (claude, agents, vscode, gemini, kiro, cursor) were
also writing their section only when no marker was present, so users
who installed pre-fix kept the old "ALWAYS read" text after upgrading.
Added _replace_or_append_section helper that updates in place when the
graphify marker is found. claude_install also no longer returns before
re-running _install_claude_hook, so stale settings.json hook payloads
get refreshed on upgrade.

Tests:
- tests/test_install_strings.py (3): every install constant still
  mentions `graphify query` and matches no banned report-first regex.
- tests/test_install_upgrade.py (7): seeds each platform's instruction
  file with pre-fix text, runs install, asserts the on-disk file
  reflects the new policy.
- test_claude_md.py idempotency tests still pass.

Fixes #580.
2026-05-15 20:17:52 -07:00
Brian Kanya 246f4001d2 build: add dev deps for Nuitka onefile binary builds
Run with:
  uv run python -m nuitka --project --mode=onefile \
    --output-dir=build --output-filename=graphify \
    --assume-yes-for-downloads

Produces build/graphify, a self-contained ~60 MB binary that bundles
the package's skill *.md files (picked up automatically from
[tool.setuptools.package-data] via Nuitka's --project flow). The
existing wheel-build path (uv build via setuptools.build_meta) is
unchanged.

[tool.nuitka] config in pyproject.toml is not used: in --project mode
Nuitka builds the option list from setuptools distribution metadata
(_dumpBuildConfiguration in nuitka/distutils/DistutilsCommands.py)
and never reads [tool.nuitka], so output-dir / output-filename / etc.
must be passed on the CLI.

patchelf is required at build time on Linux and macOS for Nuitka's
standalone/onefile linking; gated by sys_platform != 'win32' so
Windows dev installs skip it.
2026-05-15 21:58:16 -04:00
Alpha Nury 5e178b9cd4 feat(detect): auto-detect symlinked children when follow_symlinks is unset
When `root` has at least one direct symlinked child, default to following
symlinks instead of silently dropping their contents. This makes "fake
working dir" patterns (a folder full of symlinks pointing at scattered
source dirs) work transparently — the caller no longer has to know to
pass `follow_symlinks=True`.

Concrete motivating shape:

    ~/projects/research-corpus/
    ├── papers   -> /external/drive/papers
    ├── notes    -> /external/drive/notes
    └── code     -> /external/drive/code

Before: `--update` invoked `detect_incremental(root)` without passing
`follow_symlinks=True`, so the scan saw the small set of files actually
inside the root and missed everything reachable only through a symlink.
Result: every legitimate new file was missed, and the manifest paths
were marked as deleted.

After: when `follow_symlinks` is left at its default `None`, `detect()`
runs `_auto_follow_symlinks(root)` (one cheap `iterdir()` + `is_symlink()`
loop) and follows symlinks if any direct child is one. Behaviour is
unchanged for ordinary scans (no direct symlinks → False, as before).

Override is always possible by passing an explicit `follow_symlinks=True`
or `follow_symlinks=False`; existing tests confirming the explicit
behaviour continue to pass unchanged.

Backwards compatibility:
- Type annotation: `bool` -> `bool | None`. Callers passing `True`/`False`
  continue to work identically. Callers passing nothing get the new
  auto-detect.
- No new dependencies.
- Cheap: one `iterdir()` call once per detect() invocation.

Tests:
- 3 new tests in tests/test_detect.py:
  - test_detect_auto_detects_direct_symlink_child
  - test_detect_default_does_not_follow_when_no_symlinks
  - test_detect_explicit_false_overrides_auto_detect
- Full tests/test_detect.py + tests/test_incremental.py: 49/49 pass.
2026-05-16 02:23:02 +02:00
Brian Kanya 0b54974719 chore: commit uv.lock for reproducible installs
Removes uv.lock from .gitignore and tracks the lockfile so contributors
and CI get the same resolved dependency tree. graphify is shipped as a
CLI (uv tool install graphifyy / uvx graphifyy), so the application
convention of committing the lockfile fits better than the library
convention of ignoring it.
2026-05-15 19:36:40 -04:00
Safi d14e8a72d1 Suppress cross-language INFERRED calls/uses edges in surprising connections
In Python+TypeScript monorepos, the call and import resolvers match by
label across language boundaries (AuthError -> Member), producing false
positives that dominate 'Surprising Connections' due to cross-dir (+2)
and cross-community (+1) bonuses.

Expand the existing calls guard to also cover uses edges, and zero the
cross-dir and cross-community bonuses for these pairs — not just conf_bonus.
Leaves semantically_similar_to, EXTRACTED, and AMBIGUOUS edges unaffected.

5 new tests: calls suppressed, uses suppressed, semantically_similar_to
preserved, same-language INFERRED preserved, cross-language EXTRACTED preserved.
2026-05-16 00:07:18 +01:00
Safi fa70449d80 Suppress autogenerated module docstrings from rationale extraction (#882)
Alembic/Flask-Migrate revisions, Django migrations, and protobuf/OpenAPI
generated files produce hundreds of degree-1 rationale nodes labeled as
'possible documentation gaps'. Their module docstrings are revision
annotations or boilerplate, not architectural rationale.

- Add _is_autogenerated_python() in extract.py detecting Alembic, Django
  migrations, and generic DO-NOT-EDIT markers; skip module docstring only
- Function/class docstrings inside those files still extracted as normal
- report.py: exclude file_type=rationale nodes from isolated-node gaps
  section — rationale nodes are degree-1 by construction; flagging them
  as missing edges was always wrong
- 5 new tests covering Alembic, Django, protobuf, false-positive guard,
  and function-docstring passthrough
2026-05-16 00:01:57 +01:00
Safi 63926c5e39 Add rsl-siege-manager case study to 0.8.5 CHANGELOG 2026-05-15 23:42:12 +01:00
Safi 9ca27aad31 Update CHANGELOG for 0.8.2 through 0.8.5 2026-05-15 23:40:14 +01:00
Safi aaf66f1cee Bump version to 0.8.5 v0.8.5 2026-05-15 23:35:36 +01:00
Safi Shamsi 8076687d52 worked(case-study): add rsl-siege-manager (Python + TypeScript monorepo)
Cherry-picked from PR #881 (merged into v7) — adds case study artifacts
matching the worked/karpathy-repos/ convention.
2026-05-15 23:28:37 +01:00
Safi 6f8e6c59f5 fix gitignore parent-exclusion rule (#882) and dedup false merges on short labels (#878)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 22:57:36 +01:00
Safi 299b6baa26 v0.8.4: SQL FK/trigger extraction, deletion pruning fixes, community label normalization
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 15:31:22 +01:00
Safi 1b3296a093 Fix Windows skill temp files polluting project root
Move all intermediate data files and temp step scripts from the
project root into graphify-out/ to match the bash skill behaviour.
Also fix the > redirect on detect.json which wrote UTF-16LE in
PowerShell 5.1 - now uses Out-File -Encoding utf8 consistently.

Files moved: .graphify_detect.json, .graphify_ast.json,
.graphify_extract.json, .graphify_semantic.json,
.graphify_analysis.json, .graphify_labels.json,
.graphify_incremental.json, .graphify_old.json,
all .graphify_step_*.py temp scripts.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 15:07:59 +01:00
Safi 5f2c55bc95 Bump version to 0.8.3 2026-05-15 14:12:59 +01:00
Safi ac9587bfda Fix antigravity Windows skill and Python detection for uv/pipx (#831)
antigravity install on Windows now installs skill-windows.md instead
of skill.md by redirecting to a new antigravity-windows platform config
at install time via sys.platform check.

Python detection in Find-GraphifyPython now uses uv tool dir (respects
UV_TOOL_DIR) and pipx environment --value PIPX_LOCAL_VENVS (respects
PIPX_HOME) instead of guessing from the shim location. The graphify.exe
shim and python.exe live in different directories for both uv and pipx
on Windows so the previous Get-Command approach was wrong.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 14:09:31 +01:00
Safi da4ec1d5e7 Bump version to 0.8.2 2026-05-15 11:29:31 +01:00
Safi 1ac3b96897 Fix Windows skill Python detection for uv tool installs (#831)
graphify.exe and python.exe are co-located in the same Scripts\
directory for both uv tool and pipx installs. Use Get-Command graphify
to find that directory and pick up the correct Python instead of
falling through to bare python (which is a different interpreter
that doesn't have graphify).

Also replace all bare `python .graphify_*` invocations throughout
the skill with `& (Get-Content graphify-out\.graphify_python)` so
every step uses the saved interpreter, and fix the bash $(cat ...)
syntax to PowerShell & (Get-Content ...).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 11:25:59 +01:00
Safi df40e4d818 fix #873 index dot dirs, fix #874 MCP hot-reload on graph change
#873: Remove blanket dot-prefix exclusion from detect.py and
extract.py collect_files(). Add framework caches (.next, .nuxt,
.turbo, .angular, .idea, .cache, .parcel-cache, .svelte-kit,
.terraform, .serverless, .graphify) to _SKIP_DIRS so they stay
blocked. Meaningful dot dirs (.github, .claude, etc.) are now
indexed.

#874: Add _maybe_reload() with mtime+size stat key and threading.Lock
to serve.py. call_tool and read_resource call _maybe_reload() on
every request; the graph reloads automatically when graph.json changes
without restarting the MCP server.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 08:51:46 +01:00
Safi d172650065 Note callflow-html auto-regeneration in README
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 00:38:48 +01:00
Safi 5f5b70ff91 Add 0.8.1 to CHANGELOG
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
v0.8.1
2026-05-15 00:34:32 +01:00
Safi daafec20ba Update README for v0.8.1: CI badge, file types, branch references
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 00:25:30 +01:00
Safi 914a32d000 Bump version to 0.8.1
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 00:23:26 +01:00
Safi 4d6818190d Fix detect.py missing .sh/.json extensions and security harden extractors
- Add .sh, .bash, .json to CODE_EXTENSIONS in detect.py so files reach extractor
- Fix test_detect_incremental manifest path collision with new .json extension
- Update test_watch to reflect .json/.sh are now watched extensions
- B-1: only emit source imports for paths that exist on disk
- J-1: replace stat()+read() with bounded read to eliminate TOCTOU
- J-3: move pair_count cap inside loop so it is honoured exactly
- J-4: namespace $ref/extends refs with "ref_" prefix to prevent ID collision

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 00:18:33 +01:00
Safi 03dd794a80 Add v8 to CI branch list
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 00:09:16 +01:00
Safi 40b9b84caa Add tree-sitter bash and JSON extractors (#866)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-15 00:07:09 +01:00
Safi 7c561e6f82 Skip coverage/visual-test/snapshot/storybook-static noise dirs in detect (#869, #870)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 23:51:39 +01:00
Safi 4e7648b4e2 fix #865: use git rev-parse --git-path hooks so hook install works in linked worktrees
In a linked worktree .git is a file not a directory, so root/.git/hooks
fails with NotADirectoryError. Fall back to git rev-parse --path-format=absolute
--git-path hooks which resolves the correct hooks dir from any worktree.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-14 23:37:05 +01:00