@@ -117,7 +117,7 @@ def _refresh_all_version_stamps() -> None:
117117 },
118118 "antigravity" : {
119119 "skill_file" : "skill.md" ,
120- "skill_dst" : Path (".agents " ) / "skills" / "graphify" / "SKILL.md" ,
120+ "skill_dst" : Path (".agent " ) / "skills" / "graphify" / "SKILL.md" ,
121121 "claude_md" : False ,
122122 },
123123 "windows" : {
@@ -148,12 +148,7 @@ def install(platform: str = "claude") -> None:
148148 print (f"error: { cfg ['skill_file' ]} not found in package - reinstall graphify" , file = sys .stderr )
149149 sys .exit (1 )
150150
151- import os as _os
152- if platform in ("claude" , "windows" ) and _os .environ .get ("CLAUDE_CONFIG_DIR" ):
153- _claude_base = Path (_os .environ ["CLAUDE_CONFIG_DIR" ])
154- skill_dst = _claude_base / "skills" / "graphify" / "SKILL.md"
155- else :
156- skill_dst = Path .home () / cfg ["skill_dst" ]
151+ skill_dst = Path .home () / cfg ["skill_dst" ]
157152 skill_dst .parent .mkdir (parents = True , exist_ok = True )
158153 shutil .copy (skill_src , skill_dst )
159154 (skill_dst .parent / ".graphify_version" ).write_text (__version__ , encoding = "utf-8" )
@@ -196,7 +191,6 @@ def install(platform: str = "claude") -> None:
196191Rules:
197192- Before answering architecture or codebase questions, read graphify-out/GRAPH_REPORT.md for god nodes and community structure
198193- If graphify-out/wiki/index.md exists, navigate it instead of reading raw files
199- - For cross-module "how does X relate to Y" questions, prefer `graphify query "<question>"`, `graphify path "<A>" "<B>"`, or `graphify explain "<concept>"` over grep — these traverse the graph's EXTRACTED + INFERRED edges instead of scanning files
200194- After modifying code files in this session, run `graphify update .` to keep the graph current (AST-only, no API cost)
201195"""
202196
@@ -212,7 +206,6 @@ def install(platform: str = "claude") -> None:
212206Rules:
213207- Before answering architecture or codebase questions, read graphify-out/GRAPH_REPORT.md for god nodes and community structure
214208- If graphify-out/wiki/index.md exists, navigate it instead of reading raw files
215- - For cross-module "how does X relate to Y" questions, prefer `graphify query "<question>"`, `graphify path "<A>" "<B>"`, or `graphify explain "<concept>"` over grep — these traverse the graph's EXTRACTED + INFERRED edges instead of scanning files
216209- After modifying code files in this session, run `graphify update .` to keep the graph current (AST-only, no API cost)
217210"""
218211
@@ -226,7 +219,6 @@ def install(platform: str = "claude") -> None:
226219Rules:
227220- Before answering architecture or codebase questions, read graphify-out/GRAPH_REPORT.md for god nodes and community structure
228221- If graphify-out/wiki/index.md exists, navigate it instead of reading raw files
229- - For cross-module "how does X relate to Y" questions, prefer `graphify query "<question>"`, `graphify path "<A>" "<B>"`, or `graphify explain "<concept>"` over grep — these traverse the graph's EXTRACTED + INFERRED edges instead of scanning files
230222- After modifying code files in this session, run `graphify update .` to keep the graph current (AST-only, no API cost)
231223"""
232224
@@ -417,8 +409,8 @@ def vscode_uninstall(project_dir: Path | None = None) -> None:
417409 print (f" { instructions } -> deleted (was empty after removal)" )
418410
419411
420- _ANTIGRAVITY_RULES_PATH = Path (".agents " ) / "rules" / "graphify.md"
421- _ANTIGRAVITY_WORKFLOW_PATH = Path (".agents " ) / "workflows" / "graphify.md"
412+ _ANTIGRAVITY_RULES_PATH = Path (".agent " ) / "rules" / "graphify.md"
413+ _ANTIGRAVITY_WORKFLOW_PATH = Path (".agent " ) / "workflows" / "graphify.md"
422414
423415_ANTIGRAVITY_RULES = """\
424416 ## graphify
@@ -429,7 +421,6 @@ def vscode_uninstall(project_dir: Path | None = None) -> None:
429421- Before answering architecture or codebase questions, read graphify-out/GRAPH_REPORT.md for god nodes and community structure
430422- If graphify-out/wiki/index.md exists, navigate it instead of reading raw files
431423- If the graphify MCP server is active, utilize tools like `query_graph`, `get_node`, and `shortest_path` for precise architecture navigation instead of falling back to `grep`
432- - If the MCP server is not active, the CLI equivalents are `graphify query "<question>"`, `graphify path "<A>" "<B>"`, and `graphify explain "<concept>"` — prefer these over grep for cross-module questions
433424- After modifying code files in this session, run `graphify update .` to keep the graph current (AST-only, no API cost)
434425"""
435426
@@ -439,7 +430,7 @@ def vscode_uninstall(project_dir: Path | None = None) -> None:
439430**Description:** Turn any folder of files into a navigable knowledge graph
440431
441432## Steps
442- Follow the graphify skill installed at ~/.agents /skills/graphify/SKILL.md to run the full pipeline.
433+ Follow the graphify skill installed at ~/.agent /skills/graphify/SKILL.md to run the full pipeline.
443434
444435If no path argument is given, use `.` (current directory).
445436"""
@@ -509,8 +500,8 @@ def _kiro_uninstall(project_dir: Path) -> None:
509500
510501
511502def _antigravity_install (project_dir : Path ) -> None :
512- """Install graphify for Google Antigravity: skill + .agents /rules + .agents /workflows."""
513- # 1. Copy skill file to ~/.agents /skills/graphify/SKILL.md
503+ """Install graphify for Google Antigravity: skill + .agent /rules + .agent /workflows."""
504+ # 1. Copy skill file to ~/.agent /skills/graphify/SKILL.md
514505 install (platform = "antigravity" )
515506
516507 # 1.5. Inject YAML frontmatter for native Antigravity tool discovery
@@ -521,7 +512,7 @@ def _antigravity_install(project_dir: Path) -> None:
521512 frontmatter = "---\n name: graphify-manager\n description: Rebuild the code graph or perform manual CLI queries when MCP server is offline.\n ---\n \n "
522513 skill_dst .write_text (frontmatter + content , encoding = "utf-8" )
523514
524- # 2. Write .agents /rules/graphify.md
515+ # 2. Write .agent /rules/graphify.md
525516 rules_path = project_dir / _ANTIGRAVITY_RULES_PATH
526517 rules_path .parent .mkdir (parents = True , exist_ok = True )
527518 if rules_path .exists ():
@@ -530,7 +521,7 @@ def _antigravity_install(project_dir: Path) -> None:
530521 rules_path .write_text (_ANTIGRAVITY_RULES , encoding = "utf-8" )
531522 print (f"graphify rule written to { rules_path .resolve ()} " )
532523
533- # 3. Write .agents /workflows/graphify.md
524+ # 3. Write .agent /workflows/graphify.md
534525 wf_path = project_dir / _ANTIGRAVITY_WORKFLOW_PATH
535526 wf_path .parent .mkdir (parents = True , exist_ok = True )
536527 if wf_path .exists ():
@@ -648,7 +639,7 @@ def _cursor_uninstall(project_dir: Path) -> None:
648639"""
649640
650641_OPENCODE_PLUGIN_PATH = Path (".opencode" ) / "plugins" / "graphify.js"
651- _OPENCODE_CONFIG_PATH = Path (". opencode" ) / "opencode .json"
642+ _OPENCODE_CONFIG_PATH = Path ("opencode.json" )
652643
653644
654645def _install_opencode_plugin (project_dir : Path ) -> None :
@@ -911,59 +902,6 @@ def claude_uninstall(project_dir: Path | None = None) -> None:
911902 _uninstall_claude_hook (project_dir or Path ("." ))
912903
913904
914- def _clone_repo (url : str , branch : str | None = None , out_dir : Path | None = None ) -> Path :
915- """Clone a GitHub repo to a local cache dir and return the path.
916-
917- Clones into ~/.graphify/repos/<owner>/<repo> by default so repeated
918- runs on the same URL reuse the existing clone (git pull instead of clone).
919- """
920- import subprocess as _sp
921- import re as _re
922-
923- # Normalise URL — strip trailing .git if present
924- url = url .rstrip ("/" )
925- if not url .endswith (".git" ):
926- git_url = url + ".git"
927- else :
928- git_url = url
929- url = url [:- 4 ]
930-
931- # Extract owner/repo from URL
932- m = _re .search (r"github\.com[:/]([^/]+)/([^/]+?)(?:\.git)?$" , url )
933- if not m :
934- print (f"error: not a recognised GitHub URL: { url } " , file = sys .stderr )
935- sys .exit (1 )
936- owner , repo = m .group (1 ), m .group (2 )
937-
938- if out_dir :
939- dest = out_dir
940- else :
941- dest = Path .home () / ".graphify" / "repos" / owner / repo
942-
943- if dest .exists ():
944- print (f"Repo already cloned at { dest } — pulling latest..." , flush = True )
945- cmd = ["git" , "-C" , str (dest ), "pull" ]
946- if branch :
947- cmd += ["origin" , branch ]
948- result = _sp .run (cmd , capture_output = True , text = True )
949- if result .returncode != 0 :
950- print (f"warning: git pull failed:\n { result .stderr } " , file = sys .stderr )
951- else :
952- dest .parent .mkdir (parents = True , exist_ok = True )
953- print (f"Cloning { url } → { dest } ..." , flush = True )
954- cmd = ["git" , "clone" , "--depth" , "1" ]
955- if branch :
956- cmd += ["--branch" , branch ]
957- cmd += [git_url , str (dest )]
958- result = _sp .run (cmd , capture_output = True , text = True )
959- if result .returncode != 0 :
960- print (f"error: git clone failed:\n { result .stderr } " , file = sys .stderr )
961- sys .exit (1 )
962-
963- print (f"Ready at: { dest } " , flush = True )
964- return dest
965-
966-
967905def main () -> None :
968906 # Check all known skill install locations for a stale version stamp.
969907 # Skip during install/uninstall (hook writes trigger a fresh check anyway).
@@ -981,11 +919,6 @@ def main() -> None:
981919 print (" --graph <path> path to graph.json (default graphify-out/graph.json)" )
982920 print (" explain \" X\" plain-language explanation of a node and its neighbors" )
983921 print (" --graph <path> path to graph.json (default graphify-out/graph.json)" )
984- print (" clone <github-url> clone a GitHub repo locally and print its path for /graphify" )
985- print (" merge-graphs <g1> <g2> merge two or more graph.json files into one cross-repo graph" )
986- print (" --out <path> output path (default: graphify-out/merged-graph.json)" )
987- print (" --branch <branch> checkout a specific branch (default: repo default)" )
988- print (" --out <dir> clone to a custom directory (default: ~/.graphify/repos/<owner>/<repo>)" )
989922 print (" add <url> fetch a URL and save it to ./raw, then update the graph" )
990923 print (" --author \" Name\" tag the author of the content" )
991924 print (" --contributor \" Name\" tag who added it to the corpus" )
@@ -1003,8 +936,10 @@ def main() -> None:
1003936 print (" --type T query type: query|path_query|explain (default: query)" )
1004937 print (" --nodes N1 N2 ... source node labels cited in the answer" )
1005938 print (" --memory-dir DIR memory directory (default: graphify-out/memory)" )
1006- print (" check-update <path> check needs_update flag and notify if semantic re-extraction is pending (cron-safe)" )
1007939 print (" benchmark [graph.json] measure token reduction vs naive full-corpus approach" )
940+ print (" dashboard generate human-friendly architecture dashboard" )
941+ print (" --graph <path> path to graph.json (default graphify-out/graph.json)" )
942+ print (" --output <path> output path (default graphify-out/dashboard.html)" )
1008943 print (" hook install install post-commit/post-checkout git hooks (all platforms)" )
1009944 print (" hook uninstall remove git hooks" )
1010945 print (" hook status check if git hooks are installed" )
@@ -1032,8 +967,8 @@ def main() -> None:
1032967 print (" trae uninstall remove graphify section from AGENTS.md" )
1033968 print (" trae-cn install write graphify section to AGENTS.md (Trae CN)" )
1034969 print (" trae-cn uninstall remove graphify section from AGENTS.md" )
1035- print (" antigravity install write .agents /rules + .agents /workflows + skill (Google Antigravity)" )
1036- print (" antigravity uninstall remove .agents /rules, .agents /workflows, and skill" )
970+ print (" antigravity install write .agent /rules + .agent /workflows + skill (Google Antigravity)" )
971+ print (" antigravity uninstall remove .agent /rules, .agent /workflows, and skill" )
1037972 print (" hermes install write skill to ~/.hermes/skills/graphify/ (Hermes)" )
1038973 print (" hermes uninstall remove skill from ~/.hermes/skills/graphify/" )
1039974 print (" kiro install write skill to .kiro/skills/graphify/ + steering file (Kiro IDE/CLI)" )
@@ -1415,73 +1350,6 @@ def main() -> None:
14151350 print ("Nothing to update or rebuild failed — check output above." , file = sys .stderr )
14161351 sys .exit (1 )
14171352
1418- elif cmd == "check-update" :
1419- if len (sys .argv ) < 3 :
1420- print ("Usage: graphify check-update <path>" , file = sys .stderr )
1421- sys .exit (1 )
1422- from graphify .watch import check_update
1423- check_update (Path (sys .argv [2 ]).resolve ())
1424- sys .exit (0 )
1425- elif cmd == "merge-graphs" :
1426- # graphify merge-graphs graph1.json graph2.json ... --out merged.json
1427- args = sys .argv [2 :]
1428- graph_paths : list [Path ] = []
1429- out_path = Path ("graphify-out/merged-graph.json" )
1430- i = 0
1431- while i < len (args ):
1432- if args [i ] == "--out" and i + 1 < len (args ):
1433- out_path = Path (args [i + 1 ]); i += 2
1434- else :
1435- graph_paths .append (Path (args [i ])); i += 1
1436- if len (graph_paths ) < 2 :
1437- print ("Usage: graphify merge-graphs <graph1.json> <graph2.json> [...] [--out merged.json]" , file = sys .stderr )
1438- sys .exit (1 )
1439- import networkx as _nx
1440- from networkx .readwrite import json_graph as _jg
1441- graphs = []
1442- for gp in graph_paths :
1443- if not gp .exists ():
1444- print (f"error: not found: { gp } " , file = sys .stderr )
1445- sys .exit (1 )
1446- data = json .loads (gp .read_text (encoding = "utf-8" ))
1447- try :
1448- G = _jg .node_link_graph (data , edges = "links" )
1449- except TypeError :
1450- G = _jg .node_link_graph (data )
1451- # Tag every node with which repo it came from
1452- repo_tag = gp .parent .parent .name # graphify-out/../ → repo dir name
1453- for node in G .nodes :
1454- G .nodes [node ].setdefault ("repo" , repo_tag )
1455- graphs .append (G )
1456- merged = _nx .compose_all (graphs )
1457- try :
1458- out_data = _jg .node_link_data (merged , edges = "links" )
1459- except TypeError :
1460- out_data = _jg .node_link_data (merged )
1461- out_path .parent .mkdir (parents = True , exist_ok = True )
1462- out_path .write_text (json .dumps (out_data , indent = 2 ), encoding = "utf-8" )
1463- print (f"Merged { len (graphs )} graphs → { merged .number_of_nodes ()} nodes, { merged .number_of_edges ()} edges" )
1464- print (f"Written to: { out_path } " )
1465-
1466- elif cmd == "clone" :
1467- if len (sys .argv ) < 3 :
1468- print ("Usage: graphify clone <github-url> [--branch <branch>] [--out <dir>]" , file = sys .stderr )
1469- sys .exit (1 )
1470- url = sys .argv [2 ]
1471- branch : str | None = None
1472- out_dir : Path | None = None
1473- args = sys .argv [3 :]
1474- i = 0
1475- while i < len (args ):
1476- if args [i ] == "--branch" and i + 1 < len (args ):
1477- branch = args [i + 1 ]; i += 2
1478- elif args [i ] == "--out" and i + 1 < len (args ):
1479- out_dir = Path (args [i + 1 ]); i += 2
1480- else :
1481- i += 1
1482- local_path = _clone_repo (url , branch = branch , out_dir = out_dir )
1483- print (local_path )
1484-
14851353 elif cmd == "benchmark" :
14861354 from graphify .benchmark import run_benchmark , print_benchmark
14871355 graph_path = sys .argv [2 ] if len (sys .argv ) > 2 else "graphify-out/graph.json"
@@ -1496,6 +1364,27 @@ def main() -> None:
14961364 pass
14971365 result = run_benchmark (graph_path , corpus_words = corpus_words )
14981366 print_benchmark (result )
1367+ elif cmd == "dashboard" :
1368+ graph_path = "graphify-out/graph.json"
1369+ output_path = "graphify-out/dashboard.html"
1370+ args = sys .argv [2 :]
1371+ i = 0
1372+ while i < len (args ):
1373+ if args [i ] == "--graph" and i + 1 < len (args ):
1374+ graph_path = args [i + 1 ]; i += 2
1375+ elif args [i ] == "--output" and i + 1 < len (args ):
1376+ output_path = args [i + 1 ]; i += 2
1377+ else :
1378+ i += 1
1379+ gp = Path (graph_path )
1380+ if not gp .exists ():
1381+ print (f"error: graph not found at { gp } — run /graphify first" , file = sys .stderr )
1382+ sys .exit (1 )
1383+ from graphify .dashboard import generate_dashboard
1384+ result = generate_dashboard (str (gp ), output_path )
1385+ n = len (result ["modules" ])
1386+ print (f"Dashboard: { n } modules → { output_path } " )
1387+ print ("Open in browser — no server needed." )
14991388 else :
15001389 print (f"error: unknown command '{ cmd } '" , file = sys .stderr )
15011390 print ("Run 'graphify --help' for usage." , file = sys .stderr )
0 commit comments