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
@@ -0,0 +1,139 @@
1
+ """Two renderers, one RationaleQuestion object - plan.md §03.
2
+
3
+ The skill surface maps a RationaleQuestion straight onto the host's
4
+ structured-question tool (e.g. Claude Code's AskUserQuestion) - that mapping
5
+ lives in the SKILL.md itself, not here, since it's a host-tool-call, not
6
+ Python. This module is the standalone-CLI renderer: a numbered terminal
7
+ prompt when interactive, or a JSON file round-trip when not - matching
8
+ Graphify's own `--update` re-run convention rather than inventing a new one.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import json
14
+ import sys
15
+ from pathlib import Path
16
+
17
+ from ..models import Claim, Confidence, Evidence, RationaleQuestion
18
+
19
+
20
+ def ask_interactive(questions: list[RationaleQuestion]) -> dict[str, str]:
21
+ """Numbered terminal prompt with a free-text escape hatch. Returns
22
+ {claim_id: answer_text} - either the chosen option's becomes_text or the
23
+ typed free text, verbatim.
24
+ """
25
+ answers: dict[str, str] = {}
26
+ total = len(questions)
27
+ for i, q in enumerate(questions, start=1):
28
+ print(f"\n[{i}/{total}] {q.question}")
29
+ for j, opt in enumerate(q.options, start=1):
30
+ marker = " [our best guess]" if j == 1 else ""
31
+ print(f" {j}) {opt.label}{marker}")
32
+ if q.allow_free_text:
33
+ print(f" {len(q.options) + 1}) Something else (type your own)")
34
+ try:
35
+ choice = input("> ").strip()
36
+ except EOFError:
37
+ # No real terminal behind stdin despite isatty() saying so (seen
38
+ # under some sandboxed/wrapped shells) - stop asking rather than
39
+ # crash; caller falls back to writing out whatever's left unanswered.
40
+ print("\n(no input available - leaving remaining questions unanswered)")
41
+ break
42
+
43
+ if choice.isdigit() and 1 <= int(choice) <= len(q.options):
44
+ answers[q.claim_id] = q.options[int(choice) - 1].becomes_text
45
+ elif q.allow_free_text:
46
+ free_text = choice if not choice.isdigit() else input("Your answer: ").strip()
47
+ answers[q.claim_id] = free_text
48
+ else:
49
+ print("No valid option chosen - leaving this claim inferred.")
50
+ return answers
51
+
52
+
53
+ def write_pending(questions: list[RationaleQuestion], out_path: Path) -> Path:
54
+ """Non-interactive path: don't block CI/piped runs. Write pending
55
+ questions to disk for a human to answer later via `graphitect synthesize
56
+ --answers <file>`. Set each emitted `answer` field to a string, then pass
57
+ the same file back to `--answers`.
58
+ """
59
+ payload = [{**q.model_dump(), "answer": None} for q in questions]
60
+ out_path.parent.mkdir(parents=True, exist_ok=True)
61
+ out_path.write_text(json.dumps(payload, indent=2), encoding="utf-8")
62
+ return out_path
63
+
64
+
65
+ def read_answers(answers_path: Path) -> dict[str, str]:
66
+ """Load answers written by a human editing the file written by
67
+ write_pending. The preferred format is its question list with an `answer`
68
+ string filled per item. The older {"claim_id": "answer text"} mapping is
69
+ accepted for compatibility.
70
+ """
71
+ payload = json.loads(answers_path.read_text(encoding="utf-8"))
72
+ if isinstance(payload, dict):
73
+ if not all(isinstance(claim_id, str) and isinstance(answer, str) for claim_id, answer in payload.items()):
74
+ raise TypeError("answer mappings must contain string claim IDs and answer text")
75
+ return payload
76
+ if not isinstance(payload, list):
77
+ raise TypeError("answers must be an object or the generated question list")
78
+
79
+ answers: dict[str, str] = {}
80
+ for index, item in enumerate(payload, start=1):
81
+ if not isinstance(item, dict) or not isinstance(item.get("claim_id"), str):
82
+ raise TypeError(f"question {index} must contain a string claim_id")
83
+ answer = item.get("answer")
84
+ if answer is None or answer == "":
85
+ continue
86
+ if not isinstance(answer, str):
87
+ raise TypeError(f"question {index} answer must be a string or null")
88
+ if item["claim_id"] in answers:
89
+ raise ValueError(f"duplicate answer for claim_id {item['claim_id']!r}")
90
+ answers[item["claim_id"]] = answer
91
+ return answers
92
+
93
+
94
+ def apply_answer(claim: Claim, answer_text: str) -> Claim:
95
+ """An answer always confirms - and, if it's free text rather than a
96
+ listed option, *rewrites* claim.text to match what was actually said.
97
+ That's what let a wrong guess (e.g. Phase 0's Word-COM inference) get
98
+ genuinely corrected instead of rubber-stamped (plan.md §03).
99
+ """
100
+ return claim.model_copy(
101
+ update={
102
+ "text": answer_text,
103
+ "confidence": Confidence.CONFIRMED,
104
+ "cites": [*claim.cites, Evidence(source="user", note=answer_text)],
105
+ }
106
+ )
107
+
108
+
109
+ def resolve_questions(
110
+ questions: list[RationaleQuestion],
111
+ claims_by_id: dict[str, Claim],
112
+ pending_path: Path,
113
+ *,
114
+ non_interactive: bool = False,
115
+ ) -> tuple[dict[str, Claim], list[RationaleQuestion]]:
116
+ """Interactive when attached to a real terminal, otherwise write pending
117
+ questions to disk and leave those claims inferred for this run.
118
+
119
+ `non_interactive` is an explicit override for `--non-interactive`/CI use -
120
+ isatty() alone isn't reliable everywhere (some wrapped/sandboxed shells
121
+ report a tty even with stdin redirected from /dev/null), and a dropped
122
+ stdin mid-prompt raises EOFError rather than silently hanging, so this
123
+ always degrades to write_pending for whatever wasn't answered rather than
124
+ crashing.
125
+ """
126
+ if not questions:
127
+ return claims_by_id, []
128
+
129
+ if non_interactive or not sys.stdin.isatty():
130
+ write_pending(questions, pending_path)
131
+ return claims_by_id, questions
132
+
133
+ answers = ask_interactive(questions)
134
+ for claim_id, answer_text in answers.items():
135
+ claims_by_id[claim_id] = apply_answer(claims_by_id[claim_id], answer_text)
136
+ unanswered = [q for q in questions if q.claim_id not in answers]
137
+ if unanswered:
138
+ write_pending(unanswered, pending_path)
139
+ return claims_by_id, unanswered
@@ -0,0 +1,104 @@
1
+ """The rationale-mining rubric - plan.md §03.
2
+
3
+ For each claim: code -> README -> git log -> other docs -> stop. If nothing
4
+ found, only *descriptive* claims in a load-bearing section become an askable
5
+ RationaleQuestion; *prescriptive* claims (recommendations, "not a stated
6
+ plan") stay `inferred` forever by construction - asking "did you really mean
7
+ this suggestion" doesn't make sense the same way a factual gap does.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import subprocess
13
+ from pathlib import Path
14
+
15
+ from ..models import Claim, Confidence, Evidence, QuestionOption, RationaleQuestion
16
+
17
+ # Only these sections are worth a targeted question - incidental color in
18
+ # Overview/Components/Workflows isn't "why" material (plan.md §03).
19
+ ASKABLE_SECTIONS = {"Technology choices & why", "Tradeoffs & alternatives considered"}
20
+
21
+
22
+ def search_text(text: str, keywords: list[str]) -> str | None:
23
+ """Return the first line containing any keyword, or None. A real
24
+ implementation would use embeddings/fuzzy match; keyword search is the
25
+ honest floor - it either finds explicit textual evidence or it doesn't,
26
+ with no risk of a false-confident semantic match standing in for a
27
+ citation.
28
+ """
29
+ lowered_keywords = [k.lower() for k in keywords]
30
+ for line in text.splitlines():
31
+ low = line.lower()
32
+ if any(k in low for k in lowered_keywords):
33
+ return line.strip()
34
+ return None
35
+
36
+
37
+ def mine_readme(repo_path: Path, keywords: list[str]) -> Evidence | None:
38
+ readme = repo_path / "README.md"
39
+ if not readme.exists():
40
+ return None
41
+ hit = search_text(readme.read_text(encoding="utf-8", errors="ignore"), keywords)
42
+ return Evidence(source="readme", file="README.md", note=hit) if hit else None
43
+
44
+
45
+ def mine_git_log(repo_path: Path, keywords: list[str]) -> Evidence | None:
46
+ changelog = repo_path / "CHANGELOG.md"
47
+ if changelog.exists():
48
+ hit = search_text(changelog.read_text(encoding="utf-8", errors="ignore"), keywords)
49
+ if hit:
50
+ return Evidence(source="git_log", file="CHANGELOG.md", note=hit)
51
+
52
+ try:
53
+ result = subprocess.run(
54
+ ["git", "log", "--all", "--grep", "|".join(keywords), "-i", "--oneline", "-n", "1"],
55
+ cwd=repo_path,
56
+ capture_output=True,
57
+ text=True,
58
+ encoding="utf-8",
59
+ errors="replace",
60
+ check=False,
61
+ )
62
+ except FileNotFoundError:
63
+ return None
64
+ hit = result.stdout.strip()
65
+ return Evidence(source="git_log", note=hit) if hit else None
66
+
67
+
68
+ def mine_other_docs(repo_path: Path, keywords: list[str]) -> Evidence | None:
69
+ docs_dir = repo_path / "docs"
70
+ if not docs_dir.is_dir():
71
+ return None
72
+ for doc in sorted(docs_dir.rglob("*.md")):
73
+ hit = search_text(doc.read_text(encoding="utf-8", errors="ignore"), keywords)
74
+ if hit:
75
+ return Evidence(source="docs", file=str(doc.relative_to(repo_path)), note=hit)
76
+ return None
77
+
78
+
79
+ def mine_rationale(repo_path: Path, claim_text: str, keywords: list[str]) -> Evidence | None:
80
+ """Walk the rubric in order, stop at the first hit. Code-level evidence
81
+ (step 1) isn't handled here - that comes pre-attached from Graphify's own
82
+ extraction (EXTRACTED edges) or a direct source read, not a text search.
83
+ """
84
+ for miner in (mine_readme, mine_git_log, mine_other_docs):
85
+ evidence = miner(repo_path, keywords)
86
+ if evidence:
87
+ return evidence
88
+ return None
89
+
90
+
91
+ def build_question(claim: Claim, claim_id: str, section: str, guess: str) -> RationaleQuestion | None:
92
+ """Only queue a question for a descriptive, load-bearing, unresolved claim."""
93
+ if claim.kind != "descriptive" or section not in ASKABLE_SECTIONS:
94
+ return None
95
+ if claim.confidence == Confidence.CONFIRMED:
96
+ return None
97
+ return RationaleQuestion(
98
+ claim_id=claim_id,
99
+ question=f"Why: {claim.text}?",
100
+ options=[
101
+ QuestionOption(label=guess, becomes_text=claim.text),
102
+ QuestionOption(label="No deeper reason", becomes_text=f"{claim.text} (no deliberate reason given)"),
103
+ ],
104
+ )
@@ -0,0 +1,284 @@
1
+ Metadata-Version: 2.4
2
+ Name: graphitect
3
+ Version: 0.2.0
4
+ Summary: One self-contained codebase explainer: bundled Graphify analysis, bundled Archify diagrams, and optional LLM rationale.
5
+ License-Expression: MIT
6
+ Project-URL: Homepage, https://github.com/Naman6019/graphitect
7
+ Project-URL: Repository, https://github.com/Naman6019/graphitect
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ License-File: LICENSE
11
+ License-File: LICENSE-GRAPHIFY-APACHE-2.0
12
+ License-File: LICENSE-GRAPHIFY-MIT
13
+ License-File: NOTICE-GRAPHIFY
14
+ License-File: LICENSE-ARCHIFY-MIT
15
+ License-File: NOTICE-ARCHIFY-THIRD-PARTY.md
16
+ Requires-Dist: pydantic>=2.0
17
+ Requires-Dist: networkx>=3.4
18
+ Requires-Dist: numpy>=1.21
19
+ Requires-Dist: rapidfuzz>=3.0
20
+ Requires-Dist: tomli>=2.0.1; python_version < "3.11"
21
+ Requires-Dist: tree-sitter<0.26,>=0.23.0
22
+ Requires-Dist: tree-sitter-python<0.26,>=0.23
23
+ Requires-Dist: tree-sitter-javascript<0.26,>=0.23
24
+ Requires-Dist: tree-sitter-typescript<0.25,>=0.23
25
+ Requires-Dist: tree-sitter-go<0.26,>=0.23
26
+ Requires-Dist: tree-sitter-rust<0.25,>=0.23
27
+ Requires-Dist: tree-sitter-java<0.25,>=0.23
28
+ Requires-Dist: tree-sitter-groovy<0.3,>=0.1
29
+ Requires-Dist: tree-sitter-c<0.25,>=0.23
30
+ Requires-Dist: tree-sitter-cpp<0.25,>=0.23
31
+ Requires-Dist: tree-sitter-ruby<0.25,>=0.23
32
+ Requires-Dist: tree-sitter-c-sharp<0.25,>=0.23
33
+ Requires-Dist: tree-sitter-kotlin<2.0,>=1.0
34
+ Requires-Dist: tree-sitter-scala<0.27,>=0.23
35
+ Requires-Dist: tree-sitter-php<0.25,>=0.23
36
+ Requires-Dist: tree-sitter-swift<0.9,>=0.7
37
+ Requires-Dist: tree-sitter-lua<0.6,>=0.2
38
+ Requires-Dist: tree-sitter-zig<2.0,>=1.0
39
+ Requires-Dist: tree-sitter-powershell<0.28,>=0.26
40
+ Requires-Dist: tree-sitter-elixir<0.5,>=0.3
41
+ Requires-Dist: tree-sitter-objc<4.0,>=3.0
42
+ Requires-Dist: tree-sitter-julia<0.25,>=0.23
43
+ Requires-Dist: tree-sitter-verilog<2.0,>=1.0
44
+ Requires-Dist: tree-sitter-fortran<0.8,>=0.6
45
+ Requires-Dist: tree-sitter-bash<0.27,>=0.23
46
+ Requires-Dist: tree-sitter-json<0.26,>=0.23
47
+ Requires-Dist: tree-sitter-sql
48
+ Requires-Dist: openai
49
+ Requires-Dist: anthropic
50
+ Requires-Dist: tiktoken
51
+ Dynamic: license-file
52
+
53
+ # Graphitect
54
+
55
+ **Turn an existing codebase into one interactive architecture report.**
56
+
57
+ Graphitect produces a self-contained HTML artifact for project walkthroughs,
58
+ technical interviews, handoffs, and architectural review. It has two sections:
59
+
60
+ 1. **Interactive system diagram** — generated locally from the repository.
61
+ 2. **Project explanation** — technical choices, mechanisms, trade-offs, and
62
+ pros/cons, with every statement marked confirmed or inferred.
63
+
64
+ ![Graphitect report with a responsive embedded interactive viewer](docs/images/embedded-viewer-demo.png)
65
+
66
+ *A live Graphitect report with a responsive embedded Archify viewer. Theme,
67
+ view, presentation, and export controls remain interactive inside the report.*
68
+
69
+ ## Why Graphitect
70
+
71
+ Reading a repository is slow when a graph tells you only **what** connects and
72
+ a prose summary tells you only **why**. Graphitect combines both without
73
+ letting a model invent the diagram:
74
+
75
+ | Layer | What it does |
76
+ | --- | --- |
77
+ | **Graphify** | Extracts code structure, direct relationships, and communities locally. It is the deterministic source for diagram nodes and edges. |
78
+ | **Archify** | Renders the bundled, interactive architecture, workflow, and sequence views. |
79
+ | **Graphitect** | Turns that evidence into one portable HTML report and verifies any file-backed explanation citation before publishing it. |
80
+
81
+ The diagram does not require an LLM or API key. The explanation can come from
82
+ a configured standalone provider or the model already running inside Codex,
83
+ Claude Code, or another Agent Skills-compatible host.
84
+
85
+ ## Requirements
86
+
87
+ - Python 3.10+
88
+ - Node.js 18+ for the bundled Archify renderer
89
+
90
+ Graphify and Archify are bundled with Graphitect. You do **not** install
91
+ Graphify separately or run `npm install` for Archify.
92
+
93
+ ## Install
94
+
95
+ ### From this repository
96
+
97
+ ```bash
98
+ git clone https://github.com/Naman6019/graphitect.git
99
+ cd graphitect
100
+ python -m pip install .
101
+ ```
102
+
103
+ ### From PyPI
104
+
105
+ ```bash
106
+ python -m pip install graphitect
107
+ ```
108
+
109
+ Confirm that the renderer runtime is available:
110
+
111
+ ```bash
112
+ node --version
113
+ graphitect --help
114
+ ```
115
+
116
+ ## Quick start
117
+
118
+ ```bash
119
+ graphitect build /path/to/project -o project-architecture.html
120
+ ```
121
+
122
+ This produces one standalone HTML file. Open it in any modern browser; it does
123
+ not need a server or an internet connection after generation.
124
+
125
+ Without an LLM key, Graphitect still produces Section 1 and plainly marks
126
+ Section 2 as unavailable. To explicitly omit the diagram:
127
+
128
+ ```bash
129
+ graphitect build /path/to/project --no-diagram -o project-explanation.html
130
+ ```
131
+
132
+ ## Add the detailed explanation
133
+
134
+ ### Standalone CLI
135
+
136
+ Configure one supported provider, then use the same command:
137
+
138
+ ```bash
139
+ # PowerShell example — do not commit keys to your repository.
140
+ $env:GEMINI_API_KEY = "..."
141
+ graphitect build /path/to/project -o project-architecture.html
142
+ ```
143
+
144
+ Supported environment variables are `GEMINI_API_KEY` (or `GOOGLE_API_KEY`),
145
+ `ANTHROPIC_API_KEY`, and `OLLAMA_API_KEY`. A local Ollama server is also
146
+ supported through `--ollama-local`.
147
+
148
+ ## Install as an agent skill — no separate API key
149
+
150
+ Install the `graphitect` CLI once, then export a **project skill** inside each
151
+ repository you want an agent to explain. The agent host's selected model writes
152
+ Section 2; Graphitect makes no LLM network call in this path.
153
+
154
+ ### Codex
155
+
156
+ ```bash
157
+ # One time, if Graphitect is not installed already.
158
+ python -m pip install graphitect
159
+
160
+ # In the project you want to document.
161
+ cd /path/to/project
162
+ graphitect skill export --host codex
163
+ # Writes .agents/skills/graphitect/SKILL.md
164
+ ```
165
+
166
+ Open that project in Codex, then say: `Use the Graphitect skill to explain this
167
+ codebase and write project-architecture.html.`
168
+
169
+ ### Claude Code
170
+
171
+ ```bash
172
+ # One time, if Graphitect is not installed already.
173
+ python -m pip install graphitect
174
+
175
+ # In the project you want to document.
176
+ cd /path/to/project
177
+ graphitect skill export --host claude-code
178
+ # Writes .claude/skills/graphitect/SKILL.md
179
+ ```
180
+
181
+ Start or reopen Claude Code in that project, then say: `Use the Graphitect
182
+ skill to explain this codebase and write project-architecture.html.`
183
+
184
+ ### Other Agent Skills-compatible hosts
185
+
186
+ Export the portable bundle to the host's project-skill directory. Point
187
+ `--output` at the directory that host discovers:
188
+
189
+ ```bash
190
+ cd /path/to/project
191
+ graphitect skill export --output ./skills/graphitect
192
+ # Writes SKILL.md and agents/openai.yaml.
193
+ ```
194
+
195
+ ### What the skill does
196
+
197
+ The agent follows this evidence-first handoff:
198
+
199
+ ```bash
200
+ graphitect agent prepare /path/to/project -o .graphitect-agent
201
+ # The host model writes .graphitect-agent/narrative.json from grounded evidence.
202
+ graphitect agent compile .graphitect-agent --title "Project name" -o project-architecture.html
203
+ ```
204
+
205
+ Graphitect owns the diagram in this mode. The host model can write only the
206
+ narrative; it cannot add, remove, or rename Graphify nodes and edges. A
207
+ confirmed code claim must cite a repository-relative file and a full phrase
208
+ from that file. Unverified, uncited, externally referenced, or fabricated
209
+ user-backed claims are downgraded to **inferred**.
210
+
211
+ ## What the report contains
212
+
213
+ ### Section 1 — Interactive system diagram
214
+
215
+ - Overview: a readable structural subset of the Graphify graph.
216
+ - Full architecture rollup: every retained relationship in a scrollable view.
217
+ - Guided workflow story: a paced, direct path through observed relationships.
218
+ - Sequence trace: shown only for consecutive direct `calls` or `invokes`
219
+ relationships; it is static source evidence, never runtime telemetry.
220
+ - Per-block hover details: source path, symbol, relationship context, and
221
+ legacy naming context where a function name might be mistaken for a provider.
222
+
223
+ ![Graphitect evidence-gated static call sequence](docs/images/static-call-sequence-demo.png)
224
+
225
+ *A source-derived sequence view. Every arrow represents a direct Graphify
226
+ `calls` or `invokes` relationship; no runtime request, timing, or trace data
227
+ is implied.*
228
+
229
+ ### Section 2 — Project explanation
230
+
231
+ - Architecture and component walkthrough.
232
+ - Technology choices and the evidence behind them.
233
+ - Trade-offs, alternatives, strengths, and costs.
234
+ - Related diagram components for each section.
235
+ - Confirmed versus inferred labels, plus optional author questions for
236
+ unresolved, load-bearing rationale.
237
+
238
+ ## Evidence rules
239
+
240
+ Graphitect keeps the distinction between repository fact and analysis visible:
241
+
242
+ - **Confirmed**: backed by code, README, docs, Git history, or a direct author
243
+ answer. File-backed citations must contain the cited phrase.
244
+ - **Inferred**: a reasoned interpretation or recommendation. It is useful, but
245
+ never presented as a proven historical decision.
246
+ - Diagram relationships: direct Graphify extraction, not an LLM reconstruction.
247
+
248
+ If `synthesize --non-interactive` or the agent workflow writes
249
+ `graphitect-questions.json`, fill its `answer` fields and rerun with
250
+ `--answers`. Unanswered items remain inferred.
251
+
252
+ ## Advanced commands
253
+
254
+ ```bash
255
+ # Write reusable Graphify-grounded data.
256
+ graphitect ground /path/to/project -o grounding.json
257
+
258
+ # Synthesize from that data with a configured standalone provider.
259
+ graphitect synthesize grounding.json --repo /path/to/project -o understanding.json
260
+
261
+ # Render a saved understanding file.
262
+ graphitect deliver understanding.json --title "Project name" -o project-architecture
263
+ ```
264
+
265
+ For normal use, prefer `graphitect build` or the installed-agent workflow.
266
+
267
+ ## Privacy and network behavior
268
+
269
+ - Graphify extraction and diagram generation run locally.
270
+ - The self-contained report embeds its interactive viewer; viewing it requires
271
+ no hosted Graphitect service.
272
+ - Standalone explanation generation sends the bounded evidence context to the
273
+ provider you configure.
274
+ - Installed-agent mode uses the host agent's own model and permissions instead
275
+ of a Graphitect API key.
276
+
277
+ Review your repository and provider policy before sending source context to an
278
+ external model.
279
+
280
+ ## Bundled projects and notices
281
+
282
+ Graphitect bundles Graphify under its Apache-2.0 distribution terms and
283
+ Archify under MIT plus its third-party notices. See `licenses/graphify/`,
284
+ `licenses/archify/`, and the root notice files for details.