code-atlas-forge 1.1.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 (181) hide show
  1. code_atlas/__init__.py +6 -0
  2. code_atlas/__main__.py +6 -0
  3. code_atlas/api/__init__.py +5 -0
  4. code_atlas/api/handlers.py +1514 -0
  5. code_atlas/api/server.py +394 -0
  6. code_atlas/cli.py +2084 -0
  7. code_atlas/config.py +149 -0
  8. code_atlas/context/__init__.py +66 -0
  9. code_atlas/context/contracts.py +277 -0
  10. code_atlas/context/domain.py +760 -0
  11. code_atlas/context/engine.py +442 -0
  12. code_atlas/context/expand.py +133 -0
  13. code_atlas/context/renderers.py +71 -0
  14. code_atlas/context/tokenizer.py +37 -0
  15. code_atlas/contracts/__init__.py +38 -0
  16. code_atlas/contracts/enums.py +67 -0
  17. code_atlas/contracts/models.py +175 -0
  18. code_atlas/contracts/ports.py +70 -0
  19. code_atlas/discovery/__init__.py +25 -0
  20. code_atlas/discovery/gitignore.py +119 -0
  21. code_atlas/discovery/identity.py +52 -0
  22. code_atlas/discovery/models.py +204 -0
  23. code_atlas/discovery/scanner.py +495 -0
  24. code_atlas/domains/__init__.py +107 -0
  25. code_atlas/domains/api/__init__.py +28 -0
  26. code_atlas/domains/api/adapter.py +405 -0
  27. code_atlas/domains/api/common.py +308 -0
  28. code_atlas/domains/api/consumers.py +224 -0
  29. code_atlas/domains/api/emit.py +231 -0
  30. code_atlas/domains/api/openapi.py +398 -0
  31. code_atlas/domains/api/routes.py +582 -0
  32. code_atlas/domains/api/yaml_subset.py +683 -0
  33. code_atlas/domains/aws/__init__.py +28 -0
  34. code_atlas/domains/aws/adapter.py +382 -0
  35. code_atlas/domains/aws/common.py +261 -0
  36. code_atlas/domains/aws/configs.py +1164 -0
  37. code_atlas/domains/aws/emit.py +411 -0
  38. code_atlas/domains/aws/pyfiles.py +2220 -0
  39. code_atlas/domains/aws/sqlbridge.py +448 -0
  40. code_atlas/domains/cicd/__init__.py +50 -0
  41. code_atlas/domains/cicd/adapter.py +762 -0
  42. code_atlas/domains/cicd/classify.py +136 -0
  43. code_atlas/domains/cicd/workflow.py +504 -0
  44. code_atlas/domains/cicd/yamlmini.py +946 -0
  45. code_atlas/domains/cli_surface.py +739 -0
  46. code_atlas/domains/contracts.py +274 -0
  47. code_atlas/domains/crossresolve.py +1208 -0
  48. code_atlas/domains/evidence.py +57 -0
  49. code_atlas/domains/identity.py +149 -0
  50. code_atlas/domains/model.py +202 -0
  51. code_atlas/domains/registry.py +250 -0
  52. code_atlas/domains/resolution.py +65 -0
  53. code_atlas/domains/spark/__init__.py +31 -0
  54. code_atlas/domains/spark/adapter.py +146 -0
  55. code_atlas/domains/spark/emit.py +344 -0
  56. code_atlas/domains/spark/extract.py +108 -0
  57. code_atlas/domains/spark/sqllit.py +192 -0
  58. code_atlas/domains/spark/walk.py +1943 -0
  59. code_atlas/domains/sql/__init__.py +30 -0
  60. code_atlas/domains/sql/adapter.py +382 -0
  61. code_atlas/domains/sql/extract.py +1271 -0
  62. code_atlas/domains/sql/nodes.py +264 -0
  63. code_atlas/domains/sql/parser.py +1510 -0
  64. code_atlas/domains/sql/tokens.py +318 -0
  65. code_atlas/domains/summarize.py +126 -0
  66. code_atlas/domains/terraform/__init__.py +20 -0
  67. code_atlas/domains/terraform/adapter.py +127 -0
  68. code_atlas/domains/terraform/catalog.py +43 -0
  69. code_atlas/domains/terraform/extract.py +1277 -0
  70. code_atlas/domains/terraform/hcl.py +538 -0
  71. code_atlas/domains/terraform/refs.py +181 -0
  72. code_atlas/domains/terraform/tfjson.py +163 -0
  73. code_atlas/domains/terraform/tfvars.py +98 -0
  74. code_atlas/domains/validation.py +120 -0
  75. code_atlas/env_override.py +27 -0
  76. code_atlas/eval/__init__.py +26 -0
  77. code_atlas/eval/domain_eval.py +681 -0
  78. code_atlas/eval/ground_truth.py +688 -0
  79. code_atlas/eval/impact_truth.py +758 -0
  80. code_atlas/eval/metrics.py +178 -0
  81. code_atlas/eval/pack_value.py +610 -0
  82. code_atlas/federation/__init__.py +72 -0
  83. code_atlas/federation/federation.py +676 -0
  84. code_atlas/federation/model.py +355 -0
  85. code_atlas/federation/store.py +194 -0
  86. code_atlas/gitbin.py +48 -0
  87. code_atlas/graph/__init__.py +20 -0
  88. code_atlas/graph/builder.py +378 -0
  89. code_atlas/graph/identity.py +54 -0
  90. code_atlas/graph/queries.py +47 -0
  91. code_atlas/graph/validation.py +92 -0
  92. code_atlas/impact/__init__.py +37 -0
  93. code_atlas/impact/blast_radius.py +171 -0
  94. code_atlas/impact/contract_analyzer.py +104 -0
  95. code_atlas/impact/contracts.py +315 -0
  96. code_atlas/impact/diff_reader.py +142 -0
  97. code_atlas/impact/domain_traversal.py +768 -0
  98. code_atlas/impact/engine.py +392 -0
  99. code_atlas/impact/packs.py +128 -0
  100. code_atlas/impact/report.py +136 -0
  101. code_atlas/impact/revision_binding.py +220 -0
  102. code_atlas/impact/risk_classifier.py +83 -0
  103. code_atlas/impact/snapshot_resolver.py +136 -0
  104. code_atlas/impact/symbol_diff.py +210 -0
  105. code_atlas/impact/test_selector.py +166 -0
  106. code_atlas/indexing/__init__.py +5 -0
  107. code_atlas/indexing/cache.py +396 -0
  108. code_atlas/indexing/change_detector.py +278 -0
  109. code_atlas/indexing/coordinator.py +1032 -0
  110. code_atlas/indexing/dependency_planner.py +268 -0
  111. code_atlas/indexing/persist.py +436 -0
  112. code_atlas/indexing/reuse.py +85 -0
  113. code_atlas/install/__init__.py +7 -0
  114. code_atlas/install/cli.py +168 -0
  115. code_atlas/install/lifecycle.py +286 -0
  116. code_atlas/mcp.py +674 -0
  117. code_atlas/observability/__init__.py +19 -0
  118. code_atlas/observability/logging.py +199 -0
  119. code_atlas/parsers/__init__.py +43 -0
  120. code_atlas/parsers/contracts.py +209 -0
  121. code_atlas/parsers/engine.py +125 -0
  122. code_atlas/parsers/languages/__init__.py +1 -0
  123. code_atlas/parsers/languages/python.py +391 -0
  124. code_atlas/parsers/languages/tsx.py +28 -0
  125. code_atlas/parsers/languages/typescript.py +909 -0
  126. code_atlas/parsers/registry.py +51 -0
  127. code_atlas/parsers/tree_sitter_runtime.py +118 -0
  128. code_atlas/resolvers/__init__.py +33 -0
  129. code_atlas/resolvers/bootstrap.py +246 -0
  130. code_atlas/resolvers/capabilities.py +33 -0
  131. code_atlas/resolvers/contracts.py +133 -0
  132. code_atlas/resolvers/coordinator.py +96 -0
  133. code_atlas/resolvers/enrichment.py +318 -0
  134. code_atlas/resolvers/lsp/__init__.py +26 -0
  135. code_atlas/resolvers/lsp/adapter.py +256 -0
  136. code_atlas/resolvers/lsp/lifecycle.py +251 -0
  137. code_atlas/resolvers/lsp/spawn.py +92 -0
  138. code_atlas/resolvers/lsp/transport.py +240 -0
  139. code_atlas/resolvers/registry.py +33 -0
  140. code_atlas/resolvers/scip/__init__.py +49 -0
  141. code_atlas/resolvers/scip/decoder.py +216 -0
  142. code_atlas/resolvers/scip/importer.py +248 -0
  143. code_atlas/resolvers/scip/loader.py +90 -0
  144. code_atlas/resolvers/scip/mapper.py +271 -0
  145. code_atlas/resolvers/scip/provenance.py +211 -0
  146. code_atlas/resolvers/scip/resolver.py +289 -0
  147. code_atlas/resolvers/scip/validation.py +101 -0
  148. code_atlas/resolvers/syntactic/__init__.py +8 -0
  149. code_atlas/resolvers/syntactic/resolver.py +254 -0
  150. code_atlas/search/__init__.py +32 -0
  151. code_atlas/search/contracts.py +176 -0
  152. code_atlas/search/domain.py +561 -0
  153. code_atlas/search/engine.py +530 -0
  154. code_atlas/search/federated.py +276 -0
  155. code_atlas/search/graph.py +282 -0
  156. code_atlas/search/hybrid.py +151 -0
  157. code_atlas/search/intent.py +48 -0
  158. code_atlas/search/lexical.py +176 -0
  159. code_atlas/search/refs.py +359 -0
  160. code_atlas/search/structural.py +505 -0
  161. code_atlas/search/tokenizer.py +43 -0
  162. code_atlas/search/vector/__init__.py +35 -0
  163. code_atlas/search/vector/contracts.py +76 -0
  164. code_atlas/search/vector/embeddings.py +96 -0
  165. code_atlas/search/vector/index.py +169 -0
  166. code_atlas/search/vector/provider.py +50 -0
  167. code_atlas/storage/__init__.py +14 -0
  168. code_atlas/storage/db.py +47 -0
  169. code_atlas/storage/migrations.py +552 -0
  170. code_atlas/storage/store.py +1045 -0
  171. code_atlas/ui/__init__.py +29 -0
  172. code_atlas/ui/i18n.py +143 -0
  173. code_atlas/ui/kit.py +689 -0
  174. code_atlas/ui/wizard.py +317 -0
  175. code_atlas_forge-1.1.0.dist-info/METADATA +246 -0
  176. code_atlas_forge-1.1.0.dist-info/RECORD +181 -0
  177. code_atlas_forge-1.1.0.dist-info/WHEEL +5 -0
  178. code_atlas_forge-1.1.0.dist-info/entry_points.txt +2 -0
  179. code_atlas_forge-1.1.0.dist-info/licenses/LICENSE +202 -0
  180. code_atlas_forge-1.1.0.dist-info/licenses/NOTICE +17 -0
  181. code_atlas_forge-1.1.0.dist-info/top_level.txt +1 -0
@@ -0,0 +1,142 @@
1
+ """Leitura segura de diffs Git (Phase 2).
2
+
3
+ Regras: somente plumbing read-only (`diff`, `rev-parse`, `status`),
4
+ argv sem shell via ``gitbin``, ``-z`` para nomes com bytes/space,
5
+ nenhuma escrita no workspace/index/checkout.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ from pathlib import Path
11
+
12
+ from code_atlas import gitbin
13
+ from code_atlas.impact.contracts import (
14
+ ChangeKind,
15
+ ChangeSet,
16
+ FileChange,
17
+ ImpactDiagnostic,
18
+ )
19
+
20
+ _KIND_BY_STATUS = {
21
+ "A": ChangeKind.ADDED, "M": ChangeKind.MODIFIED,
22
+ "D": ChangeKind.DELETED, "R": ChangeKind.RENAMED,
23
+ "C": ChangeKind.COPIED, "T": ChangeKind.TYPE_CHANGED,
24
+ "B": ChangeKind.BINARY, "X": ChangeKind.UNAVAILABLE,
25
+ }
26
+
27
+
28
+ def resolve_revision(root: Path, expr: str) -> str | None:
29
+ """Resolve HEAD~N/branch/sha → sha completo; None se inválido."""
30
+ res = gitbin.run_git(["rev-parse", "--verify", "--quiet",
31
+ f"{expr}^{{commit}}"], cwd=root)
32
+ if res.returncode != 0:
33
+ return None
34
+ return res.stdout.decode("utf-8", "replace").strip() or None
35
+
36
+
37
+ def dirty_worktree(root: Path) -> bool:
38
+ res = gitbin.run_git(["status", "--porcelain", "-z"], cwd=root)
39
+ return res.returncode == 0 and bool(res.stdout.strip(b"\0"))
40
+
41
+
42
+ def _parse_name_status_z(raw: bytes) -> list[FileChange]:
43
+ """Parse `git diff --name-status -M -z` → FileChange[]."""
44
+ fields = [f for f in raw.split(b"\0") if f]
45
+ out: list[FileChange] = []
46
+ i = 0
47
+ while i < len(fields):
48
+ status = fields[i].decode("ascii", "replace")
49
+ i += 1
50
+ letter = status[0]
51
+ sim = int(status[1:]) if letter in "RC" and status[1:] else None
52
+ kind = _KIND_BY_STATUS.get(letter, ChangeKind.UNAVAILABLE)
53
+ old_p: str | None
54
+ new_p: str | None
55
+ if letter in "RC":
56
+ old_p = fields[i].decode("utf-8", "replace")
57
+ new_p = fields[i + 1].decode("utf-8", "replace")
58
+ i += 2
59
+ else:
60
+ old_p = fields[i].decode("utf-8", "replace")
61
+ new_p = old_p if letter != "D" else None
62
+ if letter == "A":
63
+ old_p, new_p = None, fields[i].decode("utf-8", "replace")
64
+ i += 1
65
+ out.append(FileChange(kind=kind, old_path=old_p,
66
+ new_path=new_p, similarity=sim,
67
+ binary=letter == "B"))
68
+ return out
69
+
70
+
71
+ def _binary_paths(root: Path, args: list[str]) -> set[str]:
72
+ """`--numstat -z`: linhas '-'/'-' marcam binários (name-status
73
+ não distingue — Phase 2 exige kind BINARY real)."""
74
+ res = gitbin.run_git([args[0], "--numstat", "-z", *args[1:]],
75
+ cwd=root)
76
+ if res.returncode != 0:
77
+ return set()
78
+ fields = [f for f in res.stdout.split(b"\0") if f]
79
+ out: set[str] = set()
80
+ i = 0
81
+ while i < len(fields):
82
+ parts = fields[i].split(b"\t")
83
+ i += 1
84
+ if len(parts) < 2:
85
+ continue
86
+ adds, dels = parts[0].strip(), parts[1].strip()
87
+ binary = adds == b"-" and dels == b"-"
88
+ # rename no numstat emite dois caminhos; o novo é o último.
89
+ renamed = len(parts) > 2 and parts[2].strip() == b""
90
+ n = 2 if renamed else 1
91
+ if binary and i + n - 1 < len(fields):
92
+ out.add(fields[i + n - 1].decode("utf-8", "replace"))
93
+ i += n
94
+ return out
95
+
96
+
97
+ def read_diff(
98
+ root: Path, base: str, target: str | None, *,
99
+ staged: bool = False,
100
+ ) -> tuple[ChangeSet | None, tuple[ImpactDiagnostic, ...]]:
101
+ """ChangeSet para base e target; target=None → worktree (dirty)."""
102
+ diags: list[ImpactDiagnostic] = []
103
+ base_sha = resolve_revision(root, base)
104
+ if base_sha is None:
105
+ return None, (ImpactDiagnostic(
106
+ "invalid-revision", f"revisão base inválida: {base!r}"),)
107
+ target_sha = (
108
+ resolve_revision(root, target) if target else None)
109
+ if target and target_sha is None:
110
+ return None, (ImpactDiagnostic(
111
+ "invalid-revision", f"revisão alvo inválida: {target!r}"),)
112
+
113
+ args = ["diff", "--name-status", "-M", "-z"]
114
+ if staged:
115
+ args.append("--cached")
116
+ if target_sha:
117
+ args += [base_sha, target_sha]
118
+ else:
119
+ args.append(base_sha)
120
+ res = gitbin.run_git(args, cwd=root)
121
+ if res.returncode != 0:
122
+ return None, (ImpactDiagnostic(
123
+ "git-error",
124
+ res.stderr.decode("utf-8", "replace")[:200]),)
125
+ binary = _binary_paths(root, args)
126
+ files = tuple(
127
+ FileChange(
128
+ kind=(ChangeKind.BINARY
129
+ if (f.new_path or f.old_path) in binary else f.kind),
130
+ old_path=f.old_path, new_path=f.new_path,
131
+ old_hash=f.old_hash, new_hash=f.new_hash,
132
+ similarity=f.similarity,
133
+ binary=True) if (f.new_path or f.old_path) in binary else f
134
+ for f in _parse_name_status_z(res.stdout))
135
+ return ChangeSet(
136
+ base_revision=base_sha,
137
+ target_revision=target_sha or "worktree",
138
+ files=files,
139
+ source="git",
140
+ dirty_worktree=dirty_worktree(root),
141
+ commit_ids=(base_sha, *( (target_sha,) if target_sha else ())),
142
+ ), tuple(diags)