graphitect 0.2.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (336) hide show
  1. graphify/__init__.py +30 -0
  2. graphify/__main__.py +757 -0
  3. graphify/_minhash.py +107 -0
  4. graphify/affected.py +318 -0
  5. graphify/always_on/agents-md.md +12 -0
  6. graphify/always_on/antigravity-rules.md +14 -0
  7. graphify/always_on/claude-md.md +9 -0
  8. graphify/always_on/gemini-md.md +9 -0
  9. graphify/always_on/kiro-steering.md +5 -0
  10. graphify/always_on/vscode-instructions.md +17 -0
  11. graphify/analyze.py +769 -0
  12. graphify/benchmark.py +152 -0
  13. graphify/build.py +2300 -0
  14. graphify/cache.py +1746 -0
  15. graphify/callflow_html.py +2051 -0
  16. graphify/cargo_introspect.py +109 -0
  17. graphify/cli.py +4745 -0
  18. graphify/cluster.py +409 -0
  19. graphify/command-kilo.md +15 -0
  20. graphify/cross_repo_calls.py +216 -0
  21. graphify/cross_repo_types.py +75 -0
  22. graphify/csharp_dispatch.py +154 -0
  23. graphify/dedup.py +1213 -0
  24. graphify/detect.py +2566 -0
  25. graphify/diagnostics.py +406 -0
  26. graphify/export.py +1349 -0
  27. graphify/exporters/__init__.py +1 -0
  28. graphify/exporters/base.py +14 -0
  29. graphify/exporters/graphdb.py +173 -0
  30. graphify/exporters/html.py +637 -0
  31. graphify/extract.py +7856 -0
  32. graphify/extractors/MIGRATION.md +107 -0
  33. graphify/extractors/__init__.py +66 -0
  34. graphify/extractors/apex.py +215 -0
  35. graphify/extractors/base.py +85 -0
  36. graphify/extractors/bash.py +579 -0
  37. graphify/extractors/blade.py +53 -0
  38. graphify/extractors/commonlisp.py +540 -0
  39. graphify/extractors/csharp.py +448 -0
  40. graphify/extractors/dart.py +564 -0
  41. graphify/extractors/dm.py +494 -0
  42. graphify/extractors/elixir.py +241 -0
  43. graphify/extractors/engine.py +6509 -0
  44. graphify/extractors/fortran.py +311 -0
  45. graphify/extractors/go.py +527 -0
  46. graphify/extractors/json_config.py +240 -0
  47. graphify/extractors/julia.py +289 -0
  48. graphify/extractors/markdown.py +408 -0
  49. graphify/extractors/models.py +131 -0
  50. graphify/extractors/objc.py +566 -0
  51. graphify/extractors/ocaml.py +289 -0
  52. graphify/extractors/pascal.py +688 -0
  53. graphify/extractors/pascal_forms.py +196 -0
  54. graphify/extractors/powershell.py +522 -0
  55. graphify/extractors/razor.py +192 -0
  56. graphify/extractors/resolution.py +3584 -0
  57. graphify/extractors/robot.py +296 -0
  58. graphify/extractors/rust.py +470 -0
  59. graphify/extractors/sln.py +92 -0
  60. graphify/extractors/sql.py +720 -0
  61. graphify/extractors/terraform.py +181 -0
  62. graphify/extractors/verilog.py +329 -0
  63. graphify/extractors/zig.py +181 -0
  64. graphify/file_slice.py +246 -0
  65. graphify/global_graph.py +194 -0
  66. graphify/google_workspace.py +237 -0
  67. graphify/hooks.py +933 -0
  68. graphify/ids.py +93 -0
  69. graphify/ingest.py +358 -0
  70. graphify/install.py +2366 -0
  71. graphify/llm.py +3544 -0
  72. graphify/manifest.py +4 -0
  73. graphify/manifest_ingest.py +311 -0
  74. graphify/mcp_ingest.py +386 -0
  75. graphify/multigraph_compat.py +212 -0
  76. graphify/pascal_resolution.py +129 -0
  77. graphify/paths.py +436 -0
  78. graphify/pg_introspect.py +165 -0
  79. graphify/prs.py +770 -0
  80. graphify/querylog.py +80 -0
  81. graphify/reflect.py +882 -0
  82. graphify/report.py +346 -0
  83. graphify/resolver_registry.py +85 -0
  84. graphify/ruby_resolution.py +242 -0
  85. graphify/scip_ingest.py +363 -0
  86. graphify/security.py +460 -0
  87. graphify/semantic_cleanup.py +336 -0
  88. graphify/serve.py +2608 -0
  89. graphify/skill-agents.md +710 -0
  90. graphify/skill-aider.md +1283 -0
  91. graphify/skill-amp.md +710 -0
  92. graphify/skill-claw.md +713 -0
  93. graphify/skill-codex.md +710 -0
  94. graphify/skill-copilot.md +713 -0
  95. graphify/skill-devin.md +1410 -0
  96. graphify/skill-droid.md +710 -0
  97. graphify/skill-kilo.md +722 -0
  98. graphify/skill-kiro.md +713 -0
  99. graphify/skill-opencode.md +705 -0
  100. graphify/skill-pi.md +713 -0
  101. graphify/skill-trae.md +711 -0
  102. graphify/skill-vscode.md +709 -0
  103. graphify/skill-windows.md +755 -0
  104. graphify/skill.md +713 -0
  105. graphify/skills/agents/references/add-watch.md +56 -0
  106. graphify/skills/agents/references/exports.md +87 -0
  107. graphify/skills/agents/references/extraction-spec.md +70 -0
  108. graphify/skills/agents/references/github-and-merge.md +46 -0
  109. graphify/skills/agents/references/hooks.md +33 -0
  110. graphify/skills/agents/references/query.md +311 -0
  111. graphify/skills/agents/references/transcribe.md +52 -0
  112. graphify/skills/agents/references/update.md +210 -0
  113. graphify/skills/amp/references/add-watch.md +56 -0
  114. graphify/skills/amp/references/exports.md +87 -0
  115. graphify/skills/amp/references/extraction-spec.md +70 -0
  116. graphify/skills/amp/references/github-and-merge.md +46 -0
  117. graphify/skills/amp/references/hooks.md +33 -0
  118. graphify/skills/amp/references/query.md +311 -0
  119. graphify/skills/amp/references/transcribe.md +52 -0
  120. graphify/skills/amp/references/update.md +210 -0
  121. graphify/skills/claude/references/add-watch.md +56 -0
  122. graphify/skills/claude/references/exports.md +87 -0
  123. graphify/skills/claude/references/extraction-spec.md +70 -0
  124. graphify/skills/claude/references/github-and-merge.md +46 -0
  125. graphify/skills/claude/references/hooks.md +33 -0
  126. graphify/skills/claude/references/query.md +311 -0
  127. graphify/skills/claude/references/transcribe.md +52 -0
  128. graphify/skills/claude/references/update.md +210 -0
  129. graphify/skills/claw/references/add-watch.md +56 -0
  130. graphify/skills/claw/references/exports.md +87 -0
  131. graphify/skills/claw/references/extraction-spec.md +31 -0
  132. graphify/skills/claw/references/github-and-merge.md +46 -0
  133. graphify/skills/claw/references/hooks.md +33 -0
  134. graphify/skills/claw/references/query.md +311 -0
  135. graphify/skills/claw/references/transcribe.md +52 -0
  136. graphify/skills/claw/references/update.md +210 -0
  137. graphify/skills/codex/references/add-watch.md +56 -0
  138. graphify/skills/codex/references/exports.md +87 -0
  139. graphify/skills/codex/references/extraction-spec.md +31 -0
  140. graphify/skills/codex/references/github-and-merge.md +46 -0
  141. graphify/skills/codex/references/hooks.md +33 -0
  142. graphify/skills/codex/references/query.md +311 -0
  143. graphify/skills/codex/references/transcribe.md +52 -0
  144. graphify/skills/codex/references/update.md +210 -0
  145. graphify/skills/copilot/references/add-watch.md +56 -0
  146. graphify/skills/copilot/references/exports.md +87 -0
  147. graphify/skills/copilot/references/extraction-spec.md +70 -0
  148. graphify/skills/copilot/references/github-and-merge.md +46 -0
  149. graphify/skills/copilot/references/hooks.md +33 -0
  150. graphify/skills/copilot/references/query.md +311 -0
  151. graphify/skills/copilot/references/transcribe.md +52 -0
  152. graphify/skills/copilot/references/update.md +210 -0
  153. graphify/skills/droid/references/add-watch.md +56 -0
  154. graphify/skills/droid/references/exports.md +87 -0
  155. graphify/skills/droid/references/extraction-spec.md +70 -0
  156. graphify/skills/droid/references/github-and-merge.md +46 -0
  157. graphify/skills/droid/references/hooks.md +33 -0
  158. graphify/skills/droid/references/query.md +311 -0
  159. graphify/skills/droid/references/transcribe.md +52 -0
  160. graphify/skills/droid/references/update.md +210 -0
  161. graphify/skills/kilo/references/add-watch.md +56 -0
  162. graphify/skills/kilo/references/exports.md +87 -0
  163. graphify/skills/kilo/references/extraction-spec.md +70 -0
  164. graphify/skills/kilo/references/github-and-merge.md +46 -0
  165. graphify/skills/kilo/references/hooks.md +33 -0
  166. graphify/skills/kilo/references/query.md +311 -0
  167. graphify/skills/kilo/references/transcribe.md +52 -0
  168. graphify/skills/kilo/references/update.md +210 -0
  169. graphify/skills/kiro/references/add-watch.md +56 -0
  170. graphify/skills/kiro/references/exports.md +87 -0
  171. graphify/skills/kiro/references/extraction-spec.md +31 -0
  172. graphify/skills/kiro/references/github-and-merge.md +46 -0
  173. graphify/skills/kiro/references/hooks.md +33 -0
  174. graphify/skills/kiro/references/query.md +311 -0
  175. graphify/skills/kiro/references/transcribe.md +52 -0
  176. graphify/skills/kiro/references/update.md +210 -0
  177. graphify/skills/opencode/references/add-watch.md +56 -0
  178. graphify/skills/opencode/references/exports.md +87 -0
  179. graphify/skills/opencode/references/extraction-spec.md +70 -0
  180. graphify/skills/opencode/references/github-and-merge.md +46 -0
  181. graphify/skills/opencode/references/hooks.md +33 -0
  182. graphify/skills/opencode/references/query.md +311 -0
  183. graphify/skills/opencode/references/transcribe.md +52 -0
  184. graphify/skills/opencode/references/update.md +210 -0
  185. graphify/skills/pi/references/add-watch.md +56 -0
  186. graphify/skills/pi/references/exports.md +87 -0
  187. graphify/skills/pi/references/extraction-spec.md +31 -0
  188. graphify/skills/pi/references/github-and-merge.md +46 -0
  189. graphify/skills/pi/references/hooks.md +33 -0
  190. graphify/skills/pi/references/query.md +311 -0
  191. graphify/skills/pi/references/transcribe.md +52 -0
  192. graphify/skills/pi/references/update.md +210 -0
  193. graphify/skills/trae/references/add-watch.md +56 -0
  194. graphify/skills/trae/references/exports.md +87 -0
  195. graphify/skills/trae/references/extraction-spec.md +70 -0
  196. graphify/skills/trae/references/github-and-merge.md +46 -0
  197. graphify/skills/trae/references/hooks.md +35 -0
  198. graphify/skills/trae/references/query.md +311 -0
  199. graphify/skills/trae/references/transcribe.md +52 -0
  200. graphify/skills/trae/references/update.md +210 -0
  201. graphify/skills/vscode/references/add-watch.md +56 -0
  202. graphify/skills/vscode/references/exports.md +87 -0
  203. graphify/skills/vscode/references/extraction-spec.md +70 -0
  204. graphify/skills/vscode/references/github-and-merge.md +46 -0
  205. graphify/skills/vscode/references/hooks.md +33 -0
  206. graphify/skills/vscode/references/query.md +311 -0
  207. graphify/skills/vscode/references/transcribe.md +52 -0
  208. graphify/skills/vscode/references/update.md +210 -0
  209. graphify/skills/windows/references/add-watch.md +56 -0
  210. graphify/skills/windows/references/exports.md +87 -0
  211. graphify/skills/windows/references/extraction-spec.md +70 -0
  212. graphify/skills/windows/references/github-and-merge.md +46 -0
  213. graphify/skills/windows/references/hooks.md +33 -0
  214. graphify/skills/windows/references/query.md +311 -0
  215. graphify/skills/windows/references/transcribe.md +52 -0
  216. graphify/skills/windows/references/update.md +210 -0
  217. graphify/symbol_resolution.py +556 -0
  218. graphify/transcribe.py +186 -0
  219. graphify/tree_html.py +603 -0
  220. graphify/validate.py +95 -0
  221. graphify/watch.py +2280 -0
  222. graphify/wiki.py +405 -0
  223. graphitect/__init__.py +28 -0
  224. graphitect/__main__.py +4 -0
  225. graphitect/_vendor/__init__.py +2 -0
  226. graphitect/_vendor/archify/LICENSE +22 -0
  227. graphitect/_vendor/archify/SKILL.md +137 -0
  228. graphitect/_vendor/archify/THIRD_PARTY_NOTICES.md +69 -0
  229. graphitect/_vendor/archify/assets/JetBrainsMono-OFL.txt +93 -0
  230. graphitect/_vendor/archify/assets/template.html +14935 -0
  231. graphitect/_vendor/archify/bin/archify.mjs +2091 -0
  232. graphitect/_vendor/archify/bin/open-artifact.mjs +86 -0
  233. graphitect/_vendor/archify/bin/preview.mjs +653 -0
  234. graphitect/_vendor/archify/bin/visual-check.mjs +829 -0
  235. graphitect/_vendor/archify/brand-marks/README.md +31 -0
  236. graphitect/_vendor/archify/brand-marks/catalog.json +131 -0
  237. graphitect/_vendor/archify/delta/architecture-delta.mjs +1221 -0
  238. graphitect/_vendor/archify/examples/agent-run.lifecycle.json +60 -0
  239. graphitect/_vendor/archify/examples/agent-tool-call.workflow.json +94 -0
  240. graphitect/_vendor/archify/examples/async-job-roundtrip.sequence.json +61 -0
  241. graphitect/_vendor/archify/examples/brand-aware-delivery.architecture.json +47 -0
  242. graphitect/_vendor/archify/examples/cache-miss-request.sequence.json +82 -0
  243. graphitect/_vendor/archify/examples/checkout-platform.base.architecture.json +31 -0
  244. graphitect/_vendor/archify/examples/checkout-platform.head.architecture.json +31 -0
  245. graphitect/_vendor/archify/examples/dataflow-product-analytics.html +15045 -0
  246. graphitect/_vendor/archify/examples/deployment-release.lifecycle.json +49 -0
  247. graphitect/_vendor/archify/examples/event-stream.dataflow.json +57 -0
  248. graphitect/_vendor/archify/examples/incident-response.workflow.json +64 -0
  249. graphitect/_vendor/archify/examples/lifecycle-agent-run.html +14980 -0
  250. graphitect/_vendor/archify/examples/product-analytics.dataflow.json +76 -0
  251. graphitect/_vendor/archify/examples/production-deployment.architecture.json +71 -0
  252. graphitect/_vendor/archify/examples/release-delivery.workflow.json +62 -0
  253. graphitect/_vendor/archify/examples/sequence-cache-miss-request.html +15060 -0
  254. graphitect/_vendor/archify/examples/web-app-rendered.html +15009 -0
  255. graphitect/_vendor/archify/examples/web-app.architecture.json +46 -0
  256. graphitect/_vendor/archify/examples/workflow-agent-tool-call-rendered.html +15051 -0
  257. graphitect/_vendor/archify/migrations/workflow-v2.mjs +279 -0
  258. graphitect/_vendor/archify/package-lock.json +149 -0
  259. graphitect/_vendor/archify/package.json +39 -0
  260. graphitect/_vendor/archify/recipes/scenarios.mjs +391 -0
  261. graphitect/_vendor/archify/references/authoring-contract.md +243 -0
  262. graphitect/_vendor/archify/references/brand-marks.md +65 -0
  263. graphitect/_vendor/archify/references/delivery-contract.md +120 -0
  264. graphitect/_vendor/archify/references/viewer-runtime.md +45 -0
  265. graphitect/_vendor/archify/renderers/architecture/grid.mjs +62 -0
  266. graphitect/_vendor/archify/renderers/architecture/render-architecture.mjs +1078 -0
  267. graphitect/_vendor/archify/renderers/dataflow/README.md +104 -0
  268. graphitect/_vendor/archify/renderers/dataflow/render-dataflow.mjs +483 -0
  269. graphitect/_vendor/archify/renderers/lifecycle/README.md +115 -0
  270. graphitect/_vendor/archify/renderers/lifecycle/render-lifecycle.mjs +561 -0
  271. graphitect/_vendor/archify/renderers/sequence/README.md +114 -0
  272. graphitect/_vendor/archify/renderers/sequence/render-sequence.mjs +464 -0
  273. graphitect/_vendor/archify/renderers/shared/brand-marks.mjs +563 -0
  274. graphitect/_vendor/archify/renderers/shared/cli.mjs +218 -0
  275. graphitect/_vendor/archify/renderers/shared/desktop-readability.mjs +26 -0
  276. graphitect/_vendor/archify/renderers/shared/diagnostics.mjs +127 -0
  277. graphitect/_vendor/archify/renderers/shared/engineering-profiles.mjs +157 -0
  278. graphitect/_vendor/archify/renderers/shared/generated-brand-marks.mjs +2003 -0
  279. graphitect/_vendor/archify/renderers/shared/generated-validators.mjs +13 -0
  280. graphitect/_vendor/archify/renderers/shared/geometry.mjs +1423 -0
  281. graphitect/_vendor/archify/renderers/shared/i18n.mjs +595 -0
  282. graphitect/_vendor/archify/renderers/shared/layout-report.mjs +40 -0
  283. graphitect/_vendor/archify/renderers/shared/legend.mjs +217 -0
  284. graphitect/_vendor/archify/renderers/shared/output-path.mjs +340 -0
  285. graphitect/_vendor/archify/renderers/shared/repository-evidence.mjs +238 -0
  286. graphitect/_vendor/archify/renderers/shared/repository-location.mjs +58 -0
  287. graphitect/_vendor/archify/renderers/shared/text-fit.mjs +49 -0
  288. graphitect/_vendor/archify/renderers/shared/utils.mjs +232 -0
  289. graphitect/_vendor/archify/renderers/shared/validator.mjs +86 -0
  290. graphitect/_vendor/archify/renderers/workflow/README.md +223 -0
  291. graphitect/_vendor/archify/renderers/workflow/render-workflow.mjs +35 -0
  292. graphitect/_vendor/archify/renderers/workflow/workflow-compiler.mjs +4400 -0
  293. graphitect/_vendor/archify/renderers/workflow/workflow-migration-geometry.mjs +144 -0
  294. graphitect/_vendor/archify/schemas/README.md +211 -0
  295. graphitect/_vendor/archify/schemas/architecture.schema.json +178 -0
  296. graphitect/_vendor/archify/schemas/common.schema.json +115 -0
  297. graphitect/_vendor/archify/schemas/dataflow.schema.json +243 -0
  298. graphitect/_vendor/archify/schemas/lifecycle.schema.json +266 -0
  299. graphitect/_vendor/archify/schemas/sequence.schema.json +223 -0
  300. graphitect/_vendor/archify/schemas/workflow.schema.json +428 -0
  301. graphitect/_vendor/archify/scripts/check-render-output.mjs +836 -0
  302. graphitect/_vendor/archify/scripts/check-update.mjs +1667 -0
  303. graphitect/_vendor/archify/scripts/generate-brand-marks.mjs +141 -0
  304. graphitect/_vendor/archify/scripts/generate-validators.mjs +66 -0
  305. graphitect/_vendor/archify/scripts/render-examples.mjs +26 -0
  306. graphitect/_vendor/archify/scripts/update-contract.mjs +182 -0
  307. graphitect/_vendor/archify/skill-release.json +10 -0
  308. graphitect/cli.py +981 -0
  309. graphitect/deliver/__init__.py +5 -0
  310. graphitect/deliver/archify_adapter.py +1877 -0
  311. graphitect/deliver/archify_ir.py +160 -0
  312. graphitect/deliver/archify_repair.py +135 -0
  313. graphitect/deliver/doc_compiler.py +916 -0
  314. graphitect/ground/__init__.py +5 -0
  315. graphitect/ground/describe_source.py +27 -0
  316. graphitect/ground/fullread_source.py +56 -0
  317. graphitect/ground/graphify_source.py +107 -0
  318. graphitect/models.py +118 -0
  319. graphitect/skill/SKILL.md +80 -0
  320. graphitect/skill/agents/openai.yaml +4 -0
  321. graphitect/synthesize/__init__.py +5 -0
  322. graphitect/synthesize/engine.py +281 -0
  323. graphitect/synthesize/llm_backend.py +331 -0
  324. graphitect/synthesize/questions.py +139 -0
  325. graphitect/synthesize/rubric.py +104 -0
  326. graphitect-0.2.0.dist-info/METADATA +284 -0
  327. graphitect-0.2.0.dist-info/RECORD +336 -0
  328. graphitect-0.2.0.dist-info/WHEEL +5 -0
  329. graphitect-0.2.0.dist-info/entry_points.txt +2 -0
  330. graphitect-0.2.0.dist-info/licenses/LICENSE +21 -0
  331. graphitect-0.2.0.dist-info/licenses/LICENSE-ARCHIFY-MIT +22 -0
  332. graphitect-0.2.0.dist-info/licenses/LICENSE-GRAPHIFY-APACHE-2.0 +202 -0
  333. graphitect-0.2.0.dist-info/licenses/LICENSE-GRAPHIFY-MIT +21 -0
  334. graphitect-0.2.0.dist-info/licenses/NOTICE-ARCHIFY-THIRD-PARTY.md +69 -0
  335. graphitect-0.2.0.dist-info/licenses/NOTICE-GRAPHIFY +8 -0
  336. graphitect-0.2.0.dist-info/top_level.txt +2 -0
graphify/__main__.py ADDED
@@ -0,0 +1,757 @@
1
+ """graphify CLI - `graphify install` sets up the Claude Code skill."""
2
+
3
+ from __future__ import annotations
4
+ import errno
5
+ import functools
6
+ import json
7
+ import os
8
+ import platform
9
+ import re
10
+ import shutil
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ try:
15
+ from importlib.metadata import version as _pkg_version
16
+
17
+ __version__ = _pkg_version("graphifyy")
18
+ except Exception:
19
+ __version__ = "0.9.58-bundled"
20
+
21
+ # Output directory — override with GRAPHIFY_OUT env var for worktrees or shared-output setups.
22
+ # Accepts a relative name ("graphify-out-feature") or an absolute path ("/shared/graphify-out").
23
+ # Defined once in graphify.paths so the security/callflow path guards honour the
24
+ # same override (#1423).
25
+ from graphify.paths import GRAPHIFY_OUT as _GRAPHIFY_OUT
26
+
27
+ # Install/uninstall subsystem moved to graphify/install.py; re-exported here so
28
+ # `from graphify.__main__ import <name>` keeps working unchanged.
29
+ from graphify.install import ( # noqa: E402,F401
30
+ dispatch_install_cli,
31
+ _agents_install,
32
+ _agents_platform_install,
33
+ _agents_platform_uninstall,
34
+ _agents_uninstall,
35
+ _always_on,
36
+ _amp_install,
37
+ _amp_legacy_cleanup,
38
+ _amp_uninstall,
39
+ _antigravity_finalize,
40
+ _antigravity_install,
41
+ _antigravity_uninstall,
42
+ _canonical_platform,
43
+ _claude_pretooluse_hooks,
44
+ _copy_skill_file,
45
+ _cursor_install,
46
+ _cursor_uninstall,
47
+ _devin_rules_install,
48
+ _devin_rules_uninstall,
49
+ _gemini_hook,
50
+ _install_claude_hook,
51
+ _install_codebuddy_hook,
52
+ _install_codex_hook,
53
+ _install_gemini_hook,
54
+ _install_kilo_plugin,
55
+ _install_opencode_plugin,
56
+ _install_skill_references,
57
+ _kilo_config_path,
58
+ _kilo_config_write_path,
59
+ _kilo_install,
60
+ _kilo_uninstall,
61
+ _kilo_uninstall_global,
62
+ _kiro_install,
63
+ _kiro_uninstall,
64
+ _load_json_like,
65
+ _packaged_skill_refs_dir,
66
+ _platform_skill_destination,
67
+ _print_banner,
68
+ _print_install_usage,
69
+ _print_project_git_add_hint,
70
+ _project_install,
71
+ _project_scope_root,
72
+ _project_uninstall,
73
+ _project_uninstall_all,
74
+ _remove_claude_skill_registration,
75
+ _remove_skill_file,
76
+ _replace_or_append_section,
77
+ _resolve_graphify_exe,
78
+ _skill_registration,
79
+ _strip_graphify_hook,
80
+ _strip_graphify_md_section,
81
+ _strip_json_comments,
82
+ _uninstall_claude_hook,
83
+ _uninstall_codebuddy_hook,
84
+ _uninstall_codex_hook,
85
+ _uninstall_gemini_hook,
86
+ _uninstall_kilo_plugin,
87
+ _uninstall_opencode_plugin,
88
+ claude_install,
89
+ claude_uninstall,
90
+ codebuddy_install,
91
+ codebuddy_uninstall,
92
+ gemini_install,
93
+ gemini_uninstall,
94
+ install,
95
+ uninstall_all,
96
+ vscode_install,
97
+ vscode_uninstall,
98
+ _PLATFORM_ALIASES,
99
+ _CLAUDE_MD_MARKER,
100
+ _CODEBUDDY_MD_MARKER,
101
+ _AGENTS_MD_MARKER,
102
+ _GEMINI_MD_MARKER,
103
+ _VSCODE_INSTRUCTIONS_MARKER,
104
+ _ANTIGRAVITY_RULES_PATH,
105
+ _ANTIGRAVITY_WORKFLOW_PATH,
106
+ _ANTIGRAVITY_WORKFLOW,
107
+ _CURSOR_RULE_PATH,
108
+ _CURSOR_RULE,
109
+ _DEVIN_RULES_PATH,
110
+ _DEVIN_RULES,
111
+ _KILO_PLUGIN_JS,
112
+ _KILO_PLUGIN_PATH,
113
+ _KILO_CONFIG_JSON_PATH,
114
+ _KILO_CONFIG_JSONC_PATH,
115
+ _OPENCODE_PLUGIN_JS,
116
+ _OPENCODE_PLUGIN_PATH,
117
+ _OPENCODE_CONFIG_PATH,
118
+ _PLATFORM_CONFIG,
119
+ )
120
+ from graphify.cli import ( # noqa: E402,F401
121
+ dispatch_command,
122
+ _StageTimer,
123
+ _clone_repo,
124
+ _default_graph_path,
125
+ _enforce_graph_size_cap_or_exit,
126
+ _run_hook_guard,
127
+ _SEARCH_NUDGE,
128
+ _READ_NUDGE,
129
+ _HOOK_SOURCE_EXTS,
130
+ _GEMINI_NUDGE_TEXT,
131
+ )
132
+
133
+
134
+
135
+
136
+ _ALWAYS_ON_ALIASES = {
137
+ "_CLAUDE_MD_SECTION": "claude-md",
138
+ "_AGENTS_MD_SECTION": "agents-md",
139
+ "_GEMINI_MD_SECTION": "gemini-md",
140
+ "_VSCODE_INSTRUCTIONS_SECTION": "vscode-instructions",
141
+ "_ANTIGRAVITY_RULES": "antigravity-rules",
142
+ "_KIRO_STEERING": "kiro-steering",
143
+ }
144
+
145
+
146
+ def __getattr__(name: str) -> str:
147
+ # PEP 562: lazily resolve the legacy always-on section constants for external
148
+ # importers (e.g. the install-string tests). In-module code calls _always_on()
149
+ # directly; nothing is read at import time, so a missing block can no longer
150
+ # brick the CLI on `import graphify.__main__` (#1121 follow-up).
151
+ base = _ALWAYS_ON_ALIASES.get(name)
152
+ if base is not None:
153
+ return _always_on(base)
154
+ raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
155
+
156
+
157
+
158
+
159
+
160
+
161
+
162
+
163
+ def _check_skill_version(skill_dst: Path, platform_names: "list[str] | None" = None) -> None:
164
+ """Warn if the installed skill is from an older graphify version.
165
+
166
+ ``platform_names`` are the platforms installing into this destination
167
+ (resolved here when not given - the call site passes one positional
168
+ argument only, because tests stub this function with one-arg lambdas), so
169
+ the warning can name the exact command that refreshes THIS copy (#3144):
170
+ a plain `graphify install` only refreshes the detected platform, and a
171
+ stale marker at another platform's destination made the warning permanent
172
+ - the user followed the advice, the warning stayed, and only editing the
173
+ marker by hand cleared it.
174
+ """
175
+ if platform_names is None:
176
+ try:
177
+ platform_names = [
178
+ name for name in _PLATFORM_CONFIG
179
+ if _platform_skill_destination(name) == skill_dst
180
+ ]
181
+ except Exception:
182
+ platform_names = []
183
+ version_file = skill_dst.parent / ".graphify_version"
184
+ try:
185
+ if not version_file.exists():
186
+ return
187
+ except OSError:
188
+ return
189
+ try:
190
+ skill_exists = skill_dst.exists()
191
+ except OSError:
192
+ return
193
+ if not skill_exists:
194
+ print(" warning: skill dir exists but SKILL.md is missing. Run 'graphify install' to repair.", file=sys.stderr)
195
+ return
196
+ # A progressive SKILL.md links to its references/ sidecar. If the body points
197
+ # at references/ but the dir is gone (manual delete, partial upgrade), the
198
+ # on-demand fragments won't load — flag it for repair.
199
+ try:
200
+ body = skill_dst.read_text(encoding="utf-8")
201
+ except OSError:
202
+ body = ""
203
+ if "references/" in body and not (skill_dst.parent / "references").exists():
204
+ print(" warning: skill references/ sidecar is missing. Run 'graphify install' to repair.", file=sys.stderr)
205
+ try:
206
+ installed = version_file.read_text(encoding="utf-8").strip()
207
+ except OSError:
208
+ return
209
+ if installed != __version__:
210
+ if _version_tuple(installed) > _version_tuple(__version__):
211
+ # The skill on disk is NEWER than the running package. `graphify install`
212
+ # writes the package's OWN (older) bundled skill and re-stamps the version,
213
+ # so following the old "run install" advice would silently DOWNGRADE the
214
+ # skill. The real fix is to upgrade the package (#1568). Common for a stale
215
+ # `uv tool` CLI, or a contributor whose dev checkout stamped a newer skill.
216
+ print(
217
+ f" warning: skill is from graphify {installed}, but the package is "
218
+ f"{__version__} (older). Upgrade the package "
219
+ f"(e.g. 'uv tool upgrade graphifyy' or 'pip install -U graphifyy'); "
220
+ f"running 'graphify install' would downgrade the skill.",
221
+ file=sys.stderr,
222
+ )
223
+ else:
224
+ _cmd = (
225
+ f"graphify install --platform {platform_names[0]}"
226
+ if platform_names else "graphify install"
227
+ )
228
+ print(
229
+ f" warning: skill at {skill_dst.parent} is from graphify {installed}, "
230
+ f"package is {__version__}. Run '{_cmd}' to update it "
231
+ f"(a plain 'graphify install' refreshes only the detected platform).",
232
+ file=sys.stderr,
233
+ )
234
+
235
+
236
+ def _version_tuple(version: str) -> tuple[int, ...]:
237
+ """Parse a version string into a comparable integer tuple (``0.9.2`` -> ``(0, 9, 2)``).
238
+
239
+ Reads the leading digits of each dot-segment, so pre/post-release suffixes
240
+ (``1.0.0rc1``) compare by their numeric core. A non-numeric or empty segment
241
+ becomes 0, so a malformed stamp degrades to a conservative comparison rather
242
+ than raising.
243
+ """
244
+ parts: list[int] = []
245
+ for segment in str(version).split("."):
246
+ digits = ""
247
+ for ch in segment:
248
+ if ch.isdigit():
249
+ digits += ch
250
+ else:
251
+ break
252
+ parts.append(int(digits) if digits else 0)
253
+ return tuple(parts)
254
+
255
+
256
+
257
+
258
+
259
+
260
+
261
+
262
+
263
+
264
+
265
+
266
+
267
+
268
+
269
+
270
+
271
+
272
+
273
+ # PreToolUse nudge payloads, emitted verbatim by the shell-agnostic
274
+ # `graphify hook-guard` subcommand (see _run_hook_guard). The previous hooks
275
+ # inlined POSIX bash (case/esac, [ -f ], single-quoted echo) which Windows
276
+ # cmd.exe/PowerShell cannot parse, so on Windows the hook failed and the nudge
277
+ # silently vanished — users had to invoke /graphify by hand (#522). Moving the
278
+ # logic into a Python subcommand invoked via an absolute exe path makes the hook
279
+ # parse identically under sh, cmd.exe and PowerShell. Claude Code accepts
280
+ # additionalContext on PreToolUse (Codex Desktop does not — that path stays a
281
+ # no-op via `hook-check`). Compact separators keep the payload byte-for-byte the
282
+ # same JSON the old `echo` emitted.
283
+
284
+
285
+ # Source/doc extensions the Read|Glob guard nudges on (verbatim from the old hook).
286
+ # The trailing-extension test (real final path segment, then its last '.') means
287
+ # '.json' never false-matches '.js', and framework files like '.astro' are kept.
288
+
289
+
290
+
291
+
292
+
293
+
294
+
295
+
296
+
297
+
298
+
299
+
300
+
301
+
302
+
303
+
304
+
305
+
306
+ # The always-on instruction blocks are packaged markdown under graphify/always_on/,
307
+ # generated by tools/skillgen and guarded by `skillgen --check`. Reading them at
308
+ # load keeps the install-string / issue-#580 contract byte-for-byte while letting
309
+ # a human edit one fragment instead of a triple-quoted literal here.
310
+
311
+
312
+
313
+ # AGENTS.md section for Codex, OpenCode, and OpenClaw.
314
+ # All three platforms read AGENTS.md in the project root for persistent instructions.
315
+
316
+
317
+
318
+
319
+ # Gemini CLI BeforeTool hook nudge text. The hook always returns
320
+ # {"decision":"allow"} (never blocks a tool) and appends this as additionalContext
321
+ # when a graph exists. Emitted by `graphify hook-guard gemini`. The old hook was a
322
+ # `python -c "..."` one-liner that depended on a bare `python` on PATH (often
323
+ # `python`/`py` or absent on Windows) and embedded backticks + escaped quotes that
324
+ # Windows PowerShell mangles (#522 follow-up); the subcommand form has no such
325
+ # dependency and parses under every shell.
326
+
327
+
328
+
329
+
330
+
331
+
332
+
333
+
334
+
335
+
336
+
337
+
338
+
339
+
340
+
341
+
342
+
343
+
344
+
345
+
346
+
347
+
348
+
349
+ _KIRO_STEERING_MARKER = "graphify: A knowledge graph of this project"
350
+
351
+
352
+
353
+
354
+
355
+
356
+
357
+
358
+
359
+
360
+
361
+
362
+
363
+
364
+
365
+
366
+
367
+
368
+
369
+
370
+
371
+
372
+
373
+
374
+
375
+
376
+
377
+
378
+
379
+
380
+
381
+
382
+
383
+
384
+
385
+
386
+
387
+
388
+
389
+
390
+
391
+
392
+
393
+
394
+
395
+
396
+ _CODEX_HOOK = {
397
+ "hooks": {
398
+ "PreToolUse": [
399
+ {
400
+ "matcher": "Bash",
401
+ "hooks": [
402
+ {
403
+ "type": "command",
404
+ # Use the graphify CLI itself so the hook is shell-agnostic:
405
+ # no [ -f ] bash syntax, no python3 vs python Conda issue,
406
+ # no JSON escaping inside PowerShell strings. Works on
407
+ # Windows (PowerShell/cmd.exe), macOS, and Linux.
408
+ "command": "graphify hook-check",
409
+ }
410
+ ],
411
+ }
412
+ ]
413
+ }
414
+ }
415
+
416
+
417
+
418
+
419
+
420
+
421
+
422
+
423
+
424
+
425
+
426
+
427
+
428
+
429
+
430
+
431
+
432
+
433
+
434
+
435
+
436
+
437
+
438
+
439
+
440
+
441
+
442
+
443
+
444
+
445
+
446
+
447
+
448
+
449
+
450
+
451
+
452
+
453
+
454
+
455
+
456
+
457
+
458
+
459
+
460
+
461
+
462
+
463
+
464
+
465
+
466
+
467
+
468
+
469
+
470
+
471
+
472
+
473
+
474
+ def _silence_broken_pipe() -> None:
475
+ """Handle a downstream reader that closed the pipe early. Redirect stdout to
476
+ devnull so the interpreter's shutdown flush does not raise a second time, then
477
+ exit 0 — the reader (head, `Select-Object -First N`, `sed q`) has what it needs."""
478
+ try:
479
+ devnull = os.open(os.devnull, os.O_WRONLY)
480
+ os.dup2(devnull, sys.stdout.fileno())
481
+ except Exception:
482
+ pass
483
+ sys.exit(0)
484
+
485
+
486
+ def main() -> None:
487
+ """Console entry point. Wraps the CLI so that when a downstream consumer closes
488
+ stdout early, graphify treats it as success instead of crashing with an
489
+ unhandled write-to-closed-pipe error and exit 255 — which made CI wrappers and
490
+ agent harnesses read a successful query as a command failure (#1807)."""
491
+ try:
492
+ _run_cli()
493
+ # Flush explicitly, inside the guard. Piped stdout is block-buffered, so a
494
+ # small fully-buffered output would otherwise only flush at interpreter
495
+ # shutdown — outside this try — where a reader that closed the pipe surfaces
496
+ # as a noisy "Exception ignored on flushing sys.stdout" and a nonzero exit.
497
+ sys.stdout.flush()
498
+ except BrokenPipeError:
499
+ _silence_broken_pipe()
500
+ except OSError as exc:
501
+ # Windows surfaces a write to a closed pipe as OSError(EINVAL) rather than
502
+ # BrokenPipeError; EPIPE is the POSIX form when it slips past the above.
503
+ if getattr(exc, "errno", None) in (errno.EPIPE, errno.EINVAL):
504
+ _silence_broken_pipe()
505
+ else:
506
+ raise
507
+
508
+
509
+ def _run_cli() -> None:
510
+ for _stream in (sys.stdout, sys.stderr):
511
+ if _stream is not None and hasattr(_stream, "reconfigure"):
512
+ try:
513
+ _stream.reconfigure(encoding="utf-8", errors="replace")
514
+ except Exception:
515
+ pass
516
+ # Check all known skill install locations for a stale version stamp.
517
+ # Skip during install/uninstall (hook writes trigger a fresh check anyway).
518
+ # Skip during hook-check — it runs on every editor tool use and must be silent.
519
+ # Deduplicate paths so platforms sharing the same install dir don't warn twice.
520
+ _silent_cmds = {"install", "uninstall", "hook-check", "hook-guard"}
521
+ if not os.environ.get("GRAPHIFY_EMBEDDED") and not any(
522
+ arg in _silent_cmds for arg in sys.argv
523
+ ):
524
+ # Resolve each platform's real user-scope destination so per-platform
525
+ # overrides (gemini, opencode, devin, antigravity, amp) check the dir
526
+ # they actually install into, not the bare cfg['skill_dst'].
527
+ for skill_dst in {_platform_skill_destination(name) for name in _PLATFORM_CONFIG}:
528
+ _check_skill_version(skill_dst)
529
+
530
+ if len(sys.argv) >= 2 and sys.argv[1] in ("-v", "--version", "version"):
531
+ print(f"graphify {__version__}")
532
+ return
533
+
534
+ if len(sys.argv) < 2 or sys.argv[1] in ("-h", "--help", "-?"):
535
+ print("Usage: graphify <command>")
536
+ print()
537
+ print("Commands:")
538
+ print(" install [--platform P] copy skill to platform config dir (claude|windows|codebuddy|codex|opencode|aider|amp|agents|claw|droid|trae|trae-cn|gemini|cursor|antigravity|hermes|kiro|pi|devin)")
539
+ print(" uninstall remove graphify from all detected platforms in one shot")
540
+ print(" --purge also delete graphify-out/ directory")
541
+ print(" path \"A\" \"B\" shortest path between two nodes in graph.json")
542
+ print(" --graph <path> path to graph.json (default graphify-out/graph.json)")
543
+ print(" explain \"X\" plain-language explanation of a node and its neighbors")
544
+ print(" --graph <path> path to graph.json (default graphify-out/graph.json)")
545
+ print(" diagnose multigraph report same-endpoint edge collapse risk in graph.json")
546
+ print(" --graph <path> path to graph/extraction JSON")
547
+ print(" (default graphify-out/graph.json)")
548
+ print(" --json emit machine-readable JSON")
549
+ print(" --max-examples N max same-endpoint examples to print (default 5)")
550
+ print(" --directed force directed post-build simulation")
551
+ print(" --undirected force undirected post-build simulation")
552
+ print(" (default follows JSON directed flag;")
553
+ print(" raw extraction with no flag defaults directed)")
554
+ print(" --extract-path PATH extractor source for suppression scan")
555
+ print(" clone <github-url> clone a GitHub repo locally and print its path for /graphify")
556
+ print(" merge-driver <base> <current> <other> git merge driver: union-merge two graph.json files (set up via hook install)")
557
+ print(" merge-graphs <g1> <g2> merge two or more graph.json files into one cross-repo graph")
558
+ print(" --out <path> output path (default: graphify-out/merged-graph.json)")
559
+ print(" --branch <branch> checkout a specific branch (default: repo default)")
560
+ print(" --out <dir> clone to a custom directory (default: ~/.graphify/repos/<owner>/<repo>)")
561
+ print(" add <url> fetch a URL and save it to ./raw, then update the graph")
562
+ print(" --author \"Name\" tag the author of the content")
563
+ print(" --contributor \"Name\" tag who added it to the corpus")
564
+ print(" --dir <path> target directory (default: ./raw)")
565
+ print(" watch <path> watch a folder and rebuild the graph on code changes")
566
+ print(" update <path> re-extract code files and update the graph (no LLM needed)")
567
+ print(" --force overwrite graph.json even if the rebuild has fewer nodes")
568
+ print(" (also: GRAPHIFY_FORCE=1 env var; use after refactors that delete code)")
569
+ print(" --no-cluster skip clustering, write raw extraction only")
570
+ print(" cluster-only <path> rerun clustering on an existing graph.json and regenerate report")
571
+ print(" --no-viz skip graph.html generation (useful for >5000 node graphs / CI)")
572
+ print(" --graph <path> path to graph.json (default <path>/graphify-out/graph.json)")
573
+ print(" --no-label keep 'Community N' placeholders (skip LLM community naming)")
574
+ print(" --backend=<name> backend to use for community naming (default: auto-detect)")
575
+ print(" --model=<name> model to use for community naming")
576
+ print(" --max-concurrency=N parallel community-labeling LLM calls (default 4; forced to 1 for ollama/claude-cli)")
577
+ print(" --batch-size=N communities per labeling LLM call (default 100)")
578
+ print(" label <path> (re)name communities with the configured LLM backend, regenerate report")
579
+ print(" --missing-only keep existing labels and only name missing/placeholder communities")
580
+ print(" --backend=<name> backend to use (default: auto-detect from API keys)")
581
+ print(" --model=<name> model to use for community naming")
582
+ print(" --max-concurrency=N parallel labeling LLM calls (default 4; forced to 1 for ollama/claude-cli)")
583
+ print(" --batch-size=N communities per labeling LLM call (default 100)")
584
+ print(" query \"<question>\" BFS traversal of graph.json for a question")
585
+ print(" --dfs use depth-first instead of breadth-first")
586
+ print(" --context C explicit edge-context filter (repeatable)")
587
+ print(" --budget N cap output at N tokens (default 2000)")
588
+ print(" --graph <path> path to graph.json (default graphify-out/graph.json)")
589
+ print(" affected \"X\" reverse traversal to find nodes impacted by X")
590
+ print(" --relation R edge relation to traverse in reverse (repeatable)")
591
+ print(" --depth N reverse traversal depth (default 2)")
592
+ print(" --graph <path> path to graph.json (default graphify-out/graph.json)")
593
+ print(" god-nodes list the most connected nodes (architectural hubs)")
594
+ print(" --top N how many to show (default 10)")
595
+ print(" --graph <path> path to graph.json (default graphify-out/graph.json)")
596
+ print(" --json emit JSON instead of text")
597
+ print(" prs PR dashboard: CI state, review status, worktree mapping")
598
+ print(" save-result save a Q&A result to graphify-out/memory/ for graph feedback loop")
599
+ print(" --question Q the question asked")
600
+ print(" --answer A the answer to save")
601
+ print(
602
+ " --type T query type: query|path_query|explain (default: query)"
603
+ )
604
+ print(" --nodes N1 N2 ... source node labels cited in the answer")
605
+ print(" --outcome O work-memory signal: useful|dead_end|corrected")
606
+ print(" --correction TEXT what the right answer was (pairs with --outcome corrected)")
607
+ print(" --memory-dir DIR memory directory (default: graphify-out/memory)")
608
+ print(" reflect aggregate graphify-out/memory/ outcomes into a deterministic lessons doc")
609
+ print(" --memory-dir DIR memory directory (default: graphify-out/memory)")
610
+ print(" --out FILE output path (default: graphify-out/reflections/LESSONS.md)")
611
+ print(" --graph PATH graph.json, for community grouping + dropping stale nodes (optional)")
612
+ print(" --analysis PATH .graphify_analysis.json (optional, auto-detected next to --graph)")
613
+ print(" --labels PATH .graphify_labels.json (optional, auto-detected next to --graph)")
614
+ print(" --half-life-days N signal weight halves every N days (default 30)")
615
+ print(" --min-corroboration N distinct useful results to prefer a node (default 2)")
616
+ print(" check-update <path> check needs_update flag and notify if semantic re-extraction is pending (cron-safe)")
617
+ print(" tree emit a D3 v7 collapsible-tree HTML for graph.json")
618
+ print(" --graph PATH path to graph.json (default graphify-out/graph.json)")
619
+ print(" --output HTML output path (default graphify-out/GRAPH_TREE.html)")
620
+ print(" --root PATH filesystem root for the hierarchy")
621
+ print(" --max-children N cap children per node (default 200)")
622
+ print(" --top-k-edges N per-symbol outbound edges in inspector (default 12)")
623
+ print(" --label NAME project label in header")
624
+ print(" extract <path> headless full extraction (AST + semantic LLM) for CI/scripts")
625
+ print(" --backend B gemini|kimi|claude|openai|deepseek|ollama (default: whichever API key is set)")
626
+ print(" openai also reaches self-hosted OpenAI-compatible servers (llama.cpp,")
627
+ print(" vLLM, LM Studio): set OPENAI_BASE_URL (e.g. http://localhost:8080/v1)")
628
+ print(" and OPENAI_MODEL to the model name your server serves")
629
+ print(" claude also reaches custom Anthropic-compatible endpoints (LiteLLM")
630
+ print(" proxy, gateways): set ANTHROPIC_BASE_URL and ANTHROPIC_MODEL")
631
+ print(" --model M override backend default model")
632
+ print(" --mode deep aggressive INFERRED-edge semantic extraction")
633
+ print(" --force full re-scan and re-dispatch: skip the incremental")
634
+ print(" manifest gate and semantic cache reads (env: GRAPHIFY_FORCE=1)")
635
+ print(" --max-workers N AST extraction subprocess count (default: cpu_count)")
636
+ print(" --token-budget N per-chunk token cap for semantic extraction (default: 60000)")
637
+ print(" --max-concurrency N parallel semantic chunks in flight (default: 4; set 1 for local LLMs)")
638
+ print(" --api-timeout S per-request timeout in seconds for the LLM client (default: 600)")
639
+ print(" --out DIR, --output DIR output dir (default: <path>); writes <DIR>/graphify-out/")
640
+ print(" --google-workspace export .gdoc/.gsheet/.gslides shortcuts via gws before extraction")
641
+ print(" --no-gitignore ignore .gitignore and .git/info/exclude (prioritizes .graphifyignore)")
642
+ print(" --no-cluster skip clustering, write raw extraction only")
643
+ print(" --code-only index code (local AST, no API key) and skip doc/paper/image files")
644
+ print(" --postgres DSN extract schema from a live PostgreSQL database")
645
+ print(" maps tables, views, functions + FK relationships;")
646
+ print(" column-level detail is not represented in the graph")
647
+ print(" --cargo extract crate→crate deps from Cargo.toml")
648
+ print(" --global also merge the resulting graph into the global graph")
649
+ print(" --as <tag> repo tag for --global (default: target directory name)")
650
+ print(" provider [list|show|add|remove] manage custom LLM providers")
651
+ print(" global add <graph.json> add/update a project graph in the global graph (~/.graphify/global-graph.json)")
652
+ print(" --as <tag> repo tag (default: parent directory name)")
653
+ print(" global remove <tag> remove a repo's nodes from the global graph")
654
+ print(" global list list repos in the global graph")
655
+ print(" global path print path to the global graph file")
656
+ print(" benchmark [graph.json] measure token reduction vs naive full-corpus approach")
657
+ print(" export html emit interactive graph.html [--graph PATH] [--labels PATH] [--node-limit N] [--no-viz]")
658
+ print(" export callflow-html emit Mermaid-based architecture/call-flow HTML")
659
+ print(" [GRAPH|DIR] [--graph PATH] [--labels PATH] [--report PATH] [--sections PATH] [--output HTML]")
660
+ print(" [--lang auto|zh-CN|en] [--max-sections N] [--diagram-scale N]")
661
+ print(" export obsidian emit Obsidian vault notes + canvas [--graph PATH] [--labels PATH] [--dir PATH]")
662
+ print(" export wiki emit wiki markdown articles [--graph PATH] [--labels PATH]")
663
+ print(" export svg emit graph.svg [--graph PATH] [--labels PATH]")
664
+ print(" export graphml emit GraphML [--graph PATH]")
665
+ print(" export neo4j emit Cypher or push to Neo4j [--graph PATH] [--push URI] [--user U] [--password P]")
666
+ print(" (or set NEO4J_PASSWORD instead of --password to keep it off argv)")
667
+ print(" export falkordb emit Cypher or push to FalkorDB [--graph PATH] [--push URI] [--user U] [--password P]")
668
+ print(" (or set FALKORDB_PASSWORD instead of --password to keep it off argv)")
669
+ print(" hook install install post-commit/post-checkout git hooks (all platforms)")
670
+ print(" hook uninstall remove git hooks")
671
+ print(" hook status check if git hooks are installed")
672
+ print(
673
+ " gemini install write GEMINI.md section + BeforeTool hook (Gemini CLI)"
674
+ )
675
+ print(" gemini uninstall remove GEMINI.md section + BeforeTool hook")
676
+ print(" cursor install write .cursor/rules/graphify.mdc (Cursor)")
677
+ print(" cursor uninstall remove .cursor/rules/graphify.mdc")
678
+ print(" claude install write graphify section to CLAUDE.md + PreToolUse hook (Claude Code)")
679
+ print(" claude uninstall remove graphify section from CLAUDE.md + PreToolUse hook")
680
+ print(" codebuddy install write graphify section to CODEBUDDY.md + PreToolUse hook (CodeBuddy)")
681
+ print(" codebuddy uninstall remove graphify section from CODEBUDDY.md + PreToolUse hook")
682
+ print(" codex install write graphify section to AGENTS.md (Codex)")
683
+ print(" codex uninstall remove graphify section from AGENTS.md")
684
+ print(
685
+ " opencode install write graphify section to AGENTS.md + tool.execute.before plugin (OpenCode)"
686
+ )
687
+ print(
688
+ " opencode uninstall remove graphify section from AGENTS.md + plugin"
689
+ )
690
+ print(
691
+ " kilo install install native Kilo skill + command + AGENTS.md + .kilo plugin"
692
+ )
693
+ print(
694
+ " kilo uninstall remove native Kilo skill + command + AGENTS.md + .kilo plugin"
695
+ )
696
+ print(" aider install write graphify section to AGENTS.md (Aider)")
697
+ print(" aider uninstall remove graphify section from AGENTS.md")
698
+ print(
699
+ " copilot install copy graphify skill to ~/.copilot/skills (GitHub Copilot CLI)"
700
+ )
701
+ print(" copilot uninstall remove graphify skill from ~/.copilot/skills")
702
+ print(
703
+ " vscode install configure VS Code Copilot Chat (skill + .github/copilot-instructions.md)"
704
+ )
705
+ print(" vscode uninstall remove VS Code Copilot Chat configuration")
706
+ print(
707
+ " claw install write graphify section to AGENTS.md (OpenClaw)"
708
+ )
709
+ print(" claw uninstall remove graphify section from AGENTS.md")
710
+ print(
711
+ " droid install write graphify section to AGENTS.md (Factory Droid)"
712
+ )
713
+ print(" droid uninstall remove graphify section from AGENTS.md")
714
+ print(" trae install write graphify section to AGENTS.md (Trae)")
715
+ print(" trae uninstall remove graphify section from AGENTS.md")
716
+ print(" trae-cn install write graphify section to AGENTS.md (Trae CN)")
717
+ print(" trae-cn uninstall remove graphify section from AGENTS.md")
718
+ print(
719
+ " antigravity install write .agents/rules + .agents/workflows + skill (Google Antigravity)"
720
+ )
721
+ print(
722
+ " antigravity uninstall remove .agents/rules, .agents/workflows, and skill"
723
+ )
724
+ print(
725
+ " hermes install write skill to ~/.hermes/skills/graphify/ (Hermes)"
726
+ )
727
+ print(" hermes uninstall remove skill from ~/.hermes/skills/graphify/")
728
+ print(
729
+ " kiro install write skill to .kiro/skills/graphify/ + steering file (Kiro IDE/CLI)"
730
+ )
731
+ print(" kiro uninstall remove skill + steering file")
732
+ print(" pi install write skill to ~/.pi/agent/skills/graphify/ (Pi coding agent)")
733
+ print(" pi uninstall remove skill from ~/.pi/agent/skills/graphify/")
734
+ print(" devin install write skill to ~/.config/devin/skills/graphify/ (Devin CLI)")
735
+ print(" devin uninstall remove skill from ~/.config/devin/skills/graphify/")
736
+ print()
737
+ return
738
+
739
+ cmd = sys.argv[1]
740
+
741
+ # Universal help guard: -h/--help/-? anywhere after the command shows help
742
+ # and stops — prevents flags from silently triggering destructive subcommands
743
+ # (e.g. "cursor install --help" was silently installing into Cursor, #821).
744
+ # Exempt: free-text commands (user string may contain these tokens), and
745
+ # "install"/"uninstall" which have their own per-subcommand help handlers.
746
+ _FREE_TEXT_CMDS = {"query", "explain", "path", "save-result", "install", "uninstall"}
747
+ if cmd not in _FREE_TEXT_CMDS and any(a in {"-h", "--help", "-?"} for a in sys.argv[2:]):
748
+ print(f"Run 'graphify --help' for full usage.")
749
+ return
750
+
751
+ if dispatch_install_cli(cmd):
752
+ return
753
+ dispatch_command(cmd)
754
+
755
+
756
+ if __name__ == "__main__":
757
+ main()