Commit Graph
2539 Commits
Author SHA1 Message Date
nothariharan 92741d162b docs(security): refresh the threat-surface table (#4643-#4645, #4657, #4566, #4329)
Document mitigations that landed after the last SECURITY.md update:
- URL validation now rejects embedded credentials, port 0, and missing hostnames.
- ingest writes via lstat and refuses to replace an existing entry (symlink escape).
- credential-shaped text is redacted before it reaches graph.json.
- uninstall --purge refuses the project, home directory, and filesystem roots.

(cherry picked from commit 61f9bc82c4)
v0.9.95
2026-10-23 18:20:06 +01:00
nothariharan 85567c95af docs(readme): list the antigravity-windows platform (#4655)
install.py defines antigravity-windows (the Windows shell variant of the
Google Antigravity platform), and the CLI help already lists it, but the
README platform table only had 'Google Antigravity'.

(cherry picked from commit ab742a3058)
2026-10-23 18:20:06 +01:00
nothariharan aa8ca7370b docs: document GRAPHIFY_CLAUDE_CLI_MODEL (#2861)
The claude-cli backend defaults to Opus, so community naming can burn
Opus quota silently. Document the env var in the README environment
section and mention it in the label/cluster-only --model help.

(cherry picked from commit f6d8a9b6b0)
2026-10-23 18:20:05 +01:00
nothariharan e8b1bb571f docs(readme): list the optional [watch] extra (#1203)
graphify watch needs watchdog, but the README extras table omitted the
[watch] extra, so users following the install docs hit 'watchdog not
installed'. Add the row alongside the other extras.

(cherry picked from commit ff015b9a74)
2026-10-23 18:20:04 +01:00
nothariharan 7174d3b972 docs(readme): document labeling batch limits, --missing-only, and html node limit (#3859)
- README: add a compact 'Labeling and rendering limits' section covering
  --batch-size vs context window, --missing-only, .graphify_labels.json.sig,
  export html --node-limit / GRAPHIFY_VIZ_NODE_LIMIT, and the Leiden->Louvain
  fallback.
- CLI help: state the ~16k default sizing for --batch-size and the 5000
  default for export html --node-limit.

Docs/help strings only; no behavior change.

(cherry picked from commit 4bbb93789b)
2026-10-23 18:20:02 +01:00
safishamsiandClaude Opus 4.8 b111c5cea9 docs(changelog): fold the batch into the still-unreleased 0.9.95 (now 21 PRs)
0.9.95 was gated but never shipped, so the follow-on batch belongs in it,
not a separate 0.9.96. pyproject stays at 0.9.95.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-23 18:20:00 +01:00
safishamsiandClaude Opus 4.8 2a0cacb60c docs(changelog): open 0.9.96 with the install-security, type-alias, csharp-overload, tree, and other fixes
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-23 18:16:39 +01:00
safishamsiandClaude Opus 4.8 18e52c4bb8 build(cache): bump schema to 25 for the type-alias, C# overload, and package.json-link extraction changes
Covers #4578/#4579/#4580/#4581 (Python/Go/C++/Rust aliases + Rust union),
#4591 (#4584, C# overload nodes), and #4572 (#4569, package.json dep link).
Each PR shipped a stale 20->21 bump against an older base; this is the single
reconciled bump on top of HEAD's 24.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-23 18:15:41 +01:00
Deepjyoti SarmahandClaude Opus 4.8 e76019466d fix(json_config): link a package.json dependency's manifest entry to its ref node (#4569, #4572)
Applied from PR #4572 (authorship preserved); stale cache-schema bump dropped for one combined bump.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-23 18:15:25 +01:00
Mpasha17andClaude Opus 4.8 87151e40ac fix(csharp): give each method overload its own node and bind calls by argument count (#4584, #4591)
Applied from PR #4591 (authorship preserved); stale cache-schema bump dropped for one combined bump.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-23 18:15:24 +01:00
madhusudanpatnaikandClaude Opus 4.8 14514b37f6 fix(rust): extract type aliases and union items as type nodes (Refs #4285, #4581)
Applied from PR #4581 (authorship preserved); stale cache-schema bump dropped for one combined bump.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-23 18:15:22 +01:00
madhusudanpatnaikandClaude Opus 4.8 3bab26798e fix(cpp): extract namespace-scope using/typedef aliases as type nodes (#4580)
Applied from PR #4580 (authorship preserved); stale cache-schema bump dropped for one combined bump.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-23 18:15:21 +01:00
madhusudanpatnaikandClaude Opus 4.8 8cfda7f1ce fix(go): extract type A = B aliases as type nodes (#4579)
Applied from PR #4579 (authorship preserved); stale cache-schema bump dropped for one combined bump.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-23 18:15:20 +01:00
madhusudanpatnaikandClaude Opus 4.8 291248ef49 fix(python): extract explicit type aliases as type nodes (#4578)
Applied from PR #4578 (authorship preserved); the PR's stale cache-schema
bump is dropped in favour of one combined bump for the alias cluster.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-23 18:15:06 +01:00
Neal 44079cbd9b fix(detect): skip binary media word counting
Co-authored-by: Codex <noreply@openai.com>
(cherry picked from commit 62663244fb)
2026-10-23 18:12:48 +01:00
omsenjalia 73876cbdc7 fix(cache): read and write AST cache entries via extended-length paths on Windows
Under a deep root, the cache entry
<root>/graphify-out/cache/ast/v<ver>-s<schema>-g<grammars>/<64 hex>.json,
and mkstemp's even longer <hash>.<random>.tmp temp name, overrun Windows
MAX_PATH. save_cached raised FileNotFoundError and `graphify extract`
failed with "AST extraction failed" (e.g. any project under the default
%TEMP% of a long user name).

Move detect._os_path (the \?\ helper from #1655) to paths.os_long_path
so cache.py can share it, and use it for the entry and temp file in
save_cached and load_cached. detect._os_path stays as an alias. Keys are
unaffected: _normalize_path still strips the prefix.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
(cherry picked from commit abf9c9f6ab)
2026-10-23 18:12:47 +01:00
AromalBiju1 cbcd8dd601 fix(tree): don't count untagged nodes as a repo when choosing merged layout
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit a751df5f89)
2026-10-23 18:12:46 +01:00
AromalBiju1 97f18cb7b0 fix(tree): group merged graphs by repo tag so repos get their own roots
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
(cherry picked from commit 873795a05b)
2026-10-23 18:12:46 +01:00
m.fotohi 8a19705383 fix(install): use unique temp paths to prevent symlink attacks during skill install
(cherry picked from commit b2d3ffec7a)
2026-10-23 18:12:46 +01:00
safishamsiandClaude Opus 4.8 53d3d80346 fix(deps): declare pyyaml as a base dependency
The 0.9.95 YAML structural extractor (#4706) imports PyYAML, but it was
only a transitive dependency, so a fresh `pip install graphifyy` produced
zero nodes from .yaml/.yml files (the extractor degrades gracefully but
silently). manifest_ingest and markdown also use it via guarded imports.
Declare it like tree-sitter-json so the feature works out of the box.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-23 16:16:07 +01:00
safishamsiandClaude Opus 4.8 ae2b453b7d chore: bump version to 0.9.95
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-23 16:14:52 +01:00
safishamsiandClaude Opus 4.8 4f99094579 docs(changelog): 0.9.95 — add the security, affected, build, cross-repo, hooks, objc, and sql fixes
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-23 16:13:31 +01:00
safishamsiandClaude Opus 4.8 3b213769f0 fix: bump cache schema to 24 (#4705, #4677), clean recovered SQL labels, add security tests
- Bump _AST_CACHE_SCHEMA 23 -> 24: #4705 (ObjC NS_CLOSED_ENUM) and #4677 (SQL
  CREATE TABLE ERROR-node recovery) both change extraction output, and neither
  PR included the bump, so a warm ObjC/SQL cache would serve stale results.
- #4677: display the recovered table label through _clean_ident (consistent
  with the routine/trigger recoveries) while keeping the id keyed off the raw
  name so FK edges still resolve to it.
- Add regression tests the #4708/#4709/#4710 security PRs shipped without
  (multicast/unspecified SSRF block, Obsidian tag injection, Google shortcut
  frontmatter escaping).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-23 16:12:55 +01:00
08raviyadav e69cc9d89e Recover CREATE TABLE statements dropped into ERROR nodes by unsupported column defaults (fixes #4613)
(cherry picked from commit d496e9113e)
2026-10-23 16:11:21 +01:00
Jason Daniel 8a29ab62cb feat(objc): extract NS_CLOSED_ENUM declarations as enums (#4401)
Co-Authored-By: Claude <noreply@anthropic.com>
(cherry picked from commit c0d1001005)
2026-10-23 16:11:20 +01:00
Dextheking1 fedc4e82ed google_workspace: use full YAML scalar escaping for shortcut frontmatter
(cherry picked from commit 9907005e93)
2026-10-23 16:11:11 +01:00
Dextheking1 bf48d2b83e export: sanitize obsidian frontmatter tag values
(cherry picked from commit 420cc5c2be)
2026-10-23 16:11:10 +01:00
Dextheking1 02f62e769d security: block multicast and unspecified IPs in _ip_is_blocked
(cherry picked from commit cd96fbea26)
2026-10-23 16:11:09 +01:00
dherreraj 5c3c47ebea fix(hooks): run the rebuild from the checkout's top level (#3258)
The rebuild bodies read graphify-out/ and its .graphify_root relative to
the cwd, but git does not always start hooks at the top level. With
GIT_DIR exported and no GIT_WORK_TREE (a nested git call from another
hook or tool), git takes the caller's cwd as the work tree and
`git rev-parse --show-toplevel` echoes that subdirectory. A stray
graphify-out/ left there by an earlier run then roots the rebuild at the
subdirectory; for .claude/worktrees/ that walks every linked worktree.
The #3265 guard rejects a `..` marker, but its fallback is the same cwd.

Add a shared _TOPLEVEL_ANCHOR snippet right after _WORKTREE_GUARD in all
four hooks. In the standard layout it cds to the parent of the absolute
common .git dir; with GIT_WORK_TREE or a separate git dir it keeps git's
own --show-toplevel. GIT_DIR is rewritten to its absolute form so later
git calls still resolve. The block already runs in a subshell, so the cd
does not leak into chained hooks.

(cherry picked from commit e6193ab816)
2026-10-23 16:11:08 +01:00
xiongjianxu d709f67941 fix(cross-repo): bind ObjC split decl/impl across repos to its definition
An ObjC class whose @interface(.h) and @implementation(.m) live in
different repositories surfaces as two same-named candidate type nodes
that _merge_decl_def_classes folds within a repo but cannot across the
boundary (its id embeds the directory, #1556). The cross-repo member-call
pass then tripped the single-definition guard (len(candidates) != 1) and
dropped the call edge even though exactly one candidate DEFINES the
selector.

Scoped to lang == "objc": when more than one same-named candidate
survives, union their members and bind the selector to the sole candidate
that DEFINES it (a method node with definition_file set, or a .m/.mm
source). Two candidates that both define it are two unrelated classes and
stay ambiguous, so the guard still catches the collision it was meant to.
Other languages, the C# resolver path, and the single-candidate path are
unchanged.

Co-Authored-By: Claude Code <noreply@anthropic.com>
(cherry picked from commit a82a375c2d)
2026-10-23 16:11:07 +01:00
nothariharan 7598c5dcee fix(build): resolve a raw graph's import endpoints through the alias index (#4670)
`extract --no-cluster` and the clustered path disagreed on where a
framework-style `#import <Kit/ToolEvent.h>` lands. The clustered path
re-pointed the edge at the header's real file node (`kit_event_toolevent`);
the raw path minted an external stub for the header's bare basename
(`toolevent`) and left the edge on it, so the two write paths of one
extractor produced different graphs for the same corpus.

The raw path only had a normalization map. Normalization cannot bridge
`toolevent` and `kit_event_toolevent` - they are not the same string
modulo case and punctuation - so re-pointing was impossible. The
clustered path resolves it through the pre-migration alias index, which
maps a file's old-stem spellings onto its canonical id (#1504/#1509).

Move that index into a shared `_alias_index` helper and use it in both
paths, so the clustered and the raw write path resolve an endpoint the
same way. The raw path now re-points endpoints BEFORE it mints stubs, so
an import that names a real file is no longer frozen onto a stub minted
for its unresolved spelling; only a genuinely external import
(`<Foundation/Foundation.h>`) still mints one (#2873).

An alias is committed only when exactly one node claims it, so two files
sharing a bare stem stay ambiguous and the import keeps its stub rather
than binding to whichever same-stem file was seen first.

Tests: two unit tests over `finalize_raw_graph_endpoints` (an old-stem
alias resolves to the real file node; an ambiguous alias stays
unresolved) and one end-to-end test that `extract --no-cluster` and the
clustered path agree on the import target. The new tests fail without
this change.

(cherry picked from commit acbec5f5c3)
2026-10-23 16:11:06 +01:00
Deepjyoti-Sarmah fb90065380 test: affected <path> on a shared basename with a second L1 node (#4698)
Pins the resolver against the node shapes #2032 and the rationale pass
produce, plus end-to-end runs of the issue's repro and the comment
control that was never broken.

(cherry picked from commit 3b04706956)
2026-10-23 16:11:06 +01:00
Deepjyoti-Sarmah 7aae85ec6e fix: resolve affected <path> for disambiguated file labels (#4698)
A path query matches every node in the file, so resolution ends in
_prefer_file_node. Two of its passes compare the label to the bare
basename, which #2032's disambiguation rewrote to a unique path suffix
(alpha/hooks.py); the third pass trusts L1 uniqueness, which a module
docstring (a rationale node on line 1) or a one-line top-level
definition also occupies.

Accept a label that names its own source_file, using the
_is_file_node_label predicate build/analyze already share. Runs before
the L1 fallback, so extra L1 nodes no longer decide the result.

(cherry picked from commit b80626ed3b)
2026-10-23 16:11:06 +01:00
safishamsiandClaude Opus 4.8 d67b5a0faf docs(changelog): 0.9.95 — add the YAML structural extraction feature (#4706)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-23 15:55:13 +01:00
safishamsiandClaude Opus 4.8 1834b3e703 fix(yaml): handle multi-document streams, gate data YAML, bump cache schema (#4706)
Three fixes on top of #4706's YAML extractor:
- Multi-document YAML (`---` separators) returned zero nodes because it used
  yaml.safe_load, which accepts only one document; k8s manifests, Helm output,
  and CI pipelines (the headline use cases) were silently empty. Use
  safe_load_all and give each document a distinct node (labelled by kind+name
  for k8s) so two docs in one file do not merge their keys.
- Add a _is_config_yaml gate mirroring extract_json's _is_config_json: a
  top-level data array, an OpenAPI/Swagger spec, a JSON-schema-in-YAML, and a
  flat all-scalar bundle (i18n) are skipped as data instead of minting up to
  500 orphan nodes. A mapping with any nested value, a recognized config name,
  or a .github/workflows path is still walked.
- Bump _AST_CACHE_SCHEMA 22 -> 23 (the PR reclassifies .yaml/.yml and adds a
  new extractor, so cached YAML must re-extract).
- Tests: multi-doc, data/OpenAPI/array skip, the safe_load ACE guard, and caps.
- Clearer missing-PyYAML hint (pip install pyyaml).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-23 15:54:58 +01:00
nothariharan 9c5d56f28f test(watch): use the markdown language alias for the document override
The rcfile language table maps 'markdown' -> .md; 'md' is not a recognized
alias, so the override was ignored and the watch test never saw a document.

(cherry picked from commit c1890f376a)
2026-10-23 15:51:09 +01:00
nothariharan e4d8235378 test(yaml): add structural extraction coverage and update document tests (#2214)
- Add tests/test_yaml_extraction.py (classification, key nodes, contains edges,
  stable ids, scalar/empty documents).
- Drop .yaml/.yml from the oversized-document slicing parametrization: they are
  code now, not documents sent to the semantic pass.
- Point the language-override lifecycle test at .md so it still exercises a
  document override (yaml is no longer a document).

(cherry picked from commit b69b32e2f1)
2026-10-23 15:51:09 +01:00
nothariharan 6a21696caa fix(detect/extract): structurally extract .yaml/.yml config files (#2214)
YAML files were classified as documents, so code-only / AST-only runs
(graphify update) produced zero nodes from them. Move .yaml/.yml into the
code path like .json: add a PyYAML-based structural extractor (extract_yaml)
registered in _DISPATCH, add the suffixes to CODE_EXTENSIONS, drop them from
DOC_EXTENSIONS and the splittable-text set.

Deterministic ids derived from the file path; top-level and nested keys become
nodes with contains edges, capped at 500 pairs / depth 6 to avoid exploding on
data-heavy YAML.

(cherry picked from commit 2e5565a684)
2026-10-23 15:51:09 +01:00
safishamsiandClaude Opus 4.8 d4f3cf7f51 docs(changelog): open 0.9.95 with the module-import over-promotion fix (#4707)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-23 15:39:02 +01:00
nothariharan a8ce86f02e test(extract): pin #2945 module-import over-promotion regression
Adds a regression test that fails on the pre-fix resolver: a caller that
imports a module for a different symbol and calls a same-named function must
keep the edge INFERRED, never EXTRACTED.

(cherry picked from commit 7d41be8891)
2026-10-23 15:38:48 +01:00
nothariharan ac4b5e33dd fix(extract): don't promote module-imported same-named calls to EXTRACTED (#2945)
When an ambiguous bare call resolves to a candidate solely because the
caller imported the candidate's source module (not the symbol itself), the
edge was promoted to EXTRACTED, falsely certifying a same-named symbol that
was never actually imported. Keep the resolution (for graph connectivity) but
mark it INFERRED. Symbol-level (from X import Y) imports still promote to
EXTRACTED.

(cherry picked from commit e73ca4e4a7)
2026-10-23 15:38:48 +01:00
safishamsiandClaude Opus 4.8 c588b72d93 chore: bump version to 0.9.94
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
v0.9.94
2026-10-22 17:06:40 +01:00
safishamsiandClaude Opus 4.8 c3dad319ee docs(changelog): 0.9.94 — add the SQL, markdown, mcp, cross-repo, and ObjC fixes
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-22 13:50:44 +01:00
Deepjyoti-Sarmah b8d9f12385 fix: fold SQL table stubs across files and bind schema-qualified names
The stub rewire skipped dotted labels, but a SQL table is schema.name,
so dotted labels from .sql files now count as definitions. SQL stubs
that share a normalized name and bind to nothing fold into one node
(the smallest id, so file order doesn't matter). .sql joins the
language families, so a SQL read no longer binds to a same-named
definition in another language.

(cherry picked from commit 55bc540bbe)
2026-10-22 13:49:38 +01:00
Deepjyoti-Sarmah 8e8a7c3808 test: one node per SQL table across files
A table no file defines, read from N files, becomes N per-file stub
nodes, and a schema-qualified read never binds to its definition in
another file. A SQL read of `Orders` also binds to a Python class
Orders. Guards cover same-file binding and different schemas.

(cherry picked from commit 1dfc710497)
2026-10-22 13:49:38 +01:00
Deepjyoti-Sarmah 9ce1404719 fix(sql): end the statement at GO batch separators
GO is a client-tool command, not SQL, and tree-sitter-sql has no rule
for it. Before parsing, each GO [n] line found on the masked text
(comments and strings blanked) becomes `;` plus spaces, so byte
offsets and line numbers don't move. A go line after a comma, or
before a comma or `)`, is a column and stays.

AST cache schema 20 -> 21 so cached .sql extractions re-run.

(cherry picked from commit 2293c31d79)
2026-10-22 13:49:33 +01:00
Deepjyoti-Sarmah 01715accd5 test(sql): GO batch separators without a semicolon
A GO line with no `;` before it is read as an identifier, so the next
statement lands in an ERROR node and its table is lost. Guards cover
`;`-terminated batches, GO inside a block comment, a column named go,
and GO after a keyword line such as SET NOCOUNT ON.

(cherry picked from commit f00ecc42c9)
2026-10-22 13:48:29 +01:00
Deepjyoti-Sarmah 518bd3f066 fix(sql): strip T-SQL brackets and read routine bodies (#4635)
tree-sitter-sql has no T-SQL bracket identifiers and no create_procedure
node, so bracket names were mangled and procedures only got a node from
the regex recovery, never an edge. Labels and lookup keys now drop the
brackets (ids are unchanged). Each recovered routine's body, up to the
next CREATE/ALTER, a GO line or EOF, is scanned on the masked text:
FROM/JOIN give reads_from, and INSERT/UPDATE/DELETE/MERGE/SELECT INTO
give writes_to. Alias updates resolve to their table; CTEs, variables
and temp tables are skipped.

AST cache schema 20 -> 21 so cached .sql extractions re-run.

(cherry picked from commit 70f521dba9)
2026-10-22 13:48:14 +01:00
Deepjyoti-Sarmah 608722208d test(sql): T-SQL bracket names, procedure reads and writes (#4635)
Bracket names like [dbo].[Orders] come out as the label `dbo].[Orders`,
stored procedures get no reads_from edges in any dialect, and
INSERT/UPDATE/DELETE/MERGE make no edge. Guards cover aliases, CTEs,
temp tables, variables, comments, strings and body boundaries.

(cherry picked from commit a2a666c6d7)
2026-10-22 13:47:26 +01:00
safishamsiandClaude Opus 4.8 7a0b031088 docs(serve): correct _resolve_single_node docstring to name explain (#4618)
The landed docstring listed only the get_node/get_neighbors MCP tools,
but the CLI `explain` command also routes node lookup through this
resolver (cli.py), so a substring-only near-miss now refuses there too.
Name explain in the docstring so the shared surface is accurate.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-10-22 13:47:19 +01:00