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.
- code_atlas/__init__.py +6 -0
- code_atlas/__main__.py +6 -0
- code_atlas/api/__init__.py +5 -0
- code_atlas/api/handlers.py +1514 -0
- code_atlas/api/server.py +394 -0
- code_atlas/cli.py +2084 -0
- code_atlas/config.py +149 -0
- code_atlas/context/__init__.py +66 -0
- code_atlas/context/contracts.py +277 -0
- code_atlas/context/domain.py +760 -0
- code_atlas/context/engine.py +442 -0
- code_atlas/context/expand.py +133 -0
- code_atlas/context/renderers.py +71 -0
- code_atlas/context/tokenizer.py +37 -0
- code_atlas/contracts/__init__.py +38 -0
- code_atlas/contracts/enums.py +67 -0
- code_atlas/contracts/models.py +175 -0
- code_atlas/contracts/ports.py +70 -0
- code_atlas/discovery/__init__.py +25 -0
- code_atlas/discovery/gitignore.py +119 -0
- code_atlas/discovery/identity.py +52 -0
- code_atlas/discovery/models.py +204 -0
- code_atlas/discovery/scanner.py +495 -0
- code_atlas/domains/__init__.py +107 -0
- code_atlas/domains/api/__init__.py +28 -0
- code_atlas/domains/api/adapter.py +405 -0
- code_atlas/domains/api/common.py +308 -0
- code_atlas/domains/api/consumers.py +224 -0
- code_atlas/domains/api/emit.py +231 -0
- code_atlas/domains/api/openapi.py +398 -0
- code_atlas/domains/api/routes.py +582 -0
- code_atlas/domains/api/yaml_subset.py +683 -0
- code_atlas/domains/aws/__init__.py +28 -0
- code_atlas/domains/aws/adapter.py +382 -0
- code_atlas/domains/aws/common.py +261 -0
- code_atlas/domains/aws/configs.py +1164 -0
- code_atlas/domains/aws/emit.py +411 -0
- code_atlas/domains/aws/pyfiles.py +2220 -0
- code_atlas/domains/aws/sqlbridge.py +448 -0
- code_atlas/domains/cicd/__init__.py +50 -0
- code_atlas/domains/cicd/adapter.py +762 -0
- code_atlas/domains/cicd/classify.py +136 -0
- code_atlas/domains/cicd/workflow.py +504 -0
- code_atlas/domains/cicd/yamlmini.py +946 -0
- code_atlas/domains/cli_surface.py +739 -0
- code_atlas/domains/contracts.py +274 -0
- code_atlas/domains/crossresolve.py +1208 -0
- code_atlas/domains/evidence.py +57 -0
- code_atlas/domains/identity.py +149 -0
- code_atlas/domains/model.py +202 -0
- code_atlas/domains/registry.py +250 -0
- code_atlas/domains/resolution.py +65 -0
- code_atlas/domains/spark/__init__.py +31 -0
- code_atlas/domains/spark/adapter.py +146 -0
- code_atlas/domains/spark/emit.py +344 -0
- code_atlas/domains/spark/extract.py +108 -0
- code_atlas/domains/spark/sqllit.py +192 -0
- code_atlas/domains/spark/walk.py +1943 -0
- code_atlas/domains/sql/__init__.py +30 -0
- code_atlas/domains/sql/adapter.py +382 -0
- code_atlas/domains/sql/extract.py +1271 -0
- code_atlas/domains/sql/nodes.py +264 -0
- code_atlas/domains/sql/parser.py +1510 -0
- code_atlas/domains/sql/tokens.py +318 -0
- code_atlas/domains/summarize.py +126 -0
- code_atlas/domains/terraform/__init__.py +20 -0
- code_atlas/domains/terraform/adapter.py +127 -0
- code_atlas/domains/terraform/catalog.py +43 -0
- code_atlas/domains/terraform/extract.py +1277 -0
- code_atlas/domains/terraform/hcl.py +538 -0
- code_atlas/domains/terraform/refs.py +181 -0
- code_atlas/domains/terraform/tfjson.py +163 -0
- code_atlas/domains/terraform/tfvars.py +98 -0
- code_atlas/domains/validation.py +120 -0
- code_atlas/env_override.py +27 -0
- code_atlas/eval/__init__.py +26 -0
- code_atlas/eval/domain_eval.py +681 -0
- code_atlas/eval/ground_truth.py +688 -0
- code_atlas/eval/impact_truth.py +758 -0
- code_atlas/eval/metrics.py +178 -0
- code_atlas/eval/pack_value.py +610 -0
- code_atlas/federation/__init__.py +72 -0
- code_atlas/federation/federation.py +676 -0
- code_atlas/federation/model.py +355 -0
- code_atlas/federation/store.py +194 -0
- code_atlas/gitbin.py +48 -0
- code_atlas/graph/__init__.py +20 -0
- code_atlas/graph/builder.py +378 -0
- code_atlas/graph/identity.py +54 -0
- code_atlas/graph/queries.py +47 -0
- code_atlas/graph/validation.py +92 -0
- code_atlas/impact/__init__.py +37 -0
- code_atlas/impact/blast_radius.py +171 -0
- code_atlas/impact/contract_analyzer.py +104 -0
- code_atlas/impact/contracts.py +315 -0
- code_atlas/impact/diff_reader.py +142 -0
- code_atlas/impact/domain_traversal.py +768 -0
- code_atlas/impact/engine.py +392 -0
- code_atlas/impact/packs.py +128 -0
- code_atlas/impact/report.py +136 -0
- code_atlas/impact/revision_binding.py +220 -0
- code_atlas/impact/risk_classifier.py +83 -0
- code_atlas/impact/snapshot_resolver.py +136 -0
- code_atlas/impact/symbol_diff.py +210 -0
- code_atlas/impact/test_selector.py +166 -0
- code_atlas/indexing/__init__.py +5 -0
- code_atlas/indexing/cache.py +396 -0
- code_atlas/indexing/change_detector.py +278 -0
- code_atlas/indexing/coordinator.py +1032 -0
- code_atlas/indexing/dependency_planner.py +268 -0
- code_atlas/indexing/persist.py +436 -0
- code_atlas/indexing/reuse.py +85 -0
- code_atlas/install/__init__.py +7 -0
- code_atlas/install/cli.py +168 -0
- code_atlas/install/lifecycle.py +286 -0
- code_atlas/mcp.py +674 -0
- code_atlas/observability/__init__.py +19 -0
- code_atlas/observability/logging.py +199 -0
- code_atlas/parsers/__init__.py +43 -0
- code_atlas/parsers/contracts.py +209 -0
- code_atlas/parsers/engine.py +125 -0
- code_atlas/parsers/languages/__init__.py +1 -0
- code_atlas/parsers/languages/python.py +391 -0
- code_atlas/parsers/languages/tsx.py +28 -0
- code_atlas/parsers/languages/typescript.py +909 -0
- code_atlas/parsers/registry.py +51 -0
- code_atlas/parsers/tree_sitter_runtime.py +118 -0
- code_atlas/resolvers/__init__.py +33 -0
- code_atlas/resolvers/bootstrap.py +246 -0
- code_atlas/resolvers/capabilities.py +33 -0
- code_atlas/resolvers/contracts.py +133 -0
- code_atlas/resolvers/coordinator.py +96 -0
- code_atlas/resolvers/enrichment.py +318 -0
- code_atlas/resolvers/lsp/__init__.py +26 -0
- code_atlas/resolvers/lsp/adapter.py +256 -0
- code_atlas/resolvers/lsp/lifecycle.py +251 -0
- code_atlas/resolvers/lsp/spawn.py +92 -0
- code_atlas/resolvers/lsp/transport.py +240 -0
- code_atlas/resolvers/registry.py +33 -0
- code_atlas/resolvers/scip/__init__.py +49 -0
- code_atlas/resolvers/scip/decoder.py +216 -0
- code_atlas/resolvers/scip/importer.py +248 -0
- code_atlas/resolvers/scip/loader.py +90 -0
- code_atlas/resolvers/scip/mapper.py +271 -0
- code_atlas/resolvers/scip/provenance.py +211 -0
- code_atlas/resolvers/scip/resolver.py +289 -0
- code_atlas/resolvers/scip/validation.py +101 -0
- code_atlas/resolvers/syntactic/__init__.py +8 -0
- code_atlas/resolvers/syntactic/resolver.py +254 -0
- code_atlas/search/__init__.py +32 -0
- code_atlas/search/contracts.py +176 -0
- code_atlas/search/domain.py +561 -0
- code_atlas/search/engine.py +530 -0
- code_atlas/search/federated.py +276 -0
- code_atlas/search/graph.py +282 -0
- code_atlas/search/hybrid.py +151 -0
- code_atlas/search/intent.py +48 -0
- code_atlas/search/lexical.py +176 -0
- code_atlas/search/refs.py +359 -0
- code_atlas/search/structural.py +505 -0
- code_atlas/search/tokenizer.py +43 -0
- code_atlas/search/vector/__init__.py +35 -0
- code_atlas/search/vector/contracts.py +76 -0
- code_atlas/search/vector/embeddings.py +96 -0
- code_atlas/search/vector/index.py +169 -0
- code_atlas/search/vector/provider.py +50 -0
- code_atlas/storage/__init__.py +14 -0
- code_atlas/storage/db.py +47 -0
- code_atlas/storage/migrations.py +552 -0
- code_atlas/storage/store.py +1045 -0
- code_atlas/ui/__init__.py +29 -0
- code_atlas/ui/i18n.py +143 -0
- code_atlas/ui/kit.py +689 -0
- code_atlas/ui/wizard.py +317 -0
- code_atlas_forge-1.1.0.dist-info/METADATA +246 -0
- code_atlas_forge-1.1.0.dist-info/RECORD +181 -0
- code_atlas_forge-1.1.0.dist-info/WHEEL +5 -0
- code_atlas_forge-1.1.0.dist-info/entry_points.txt +2 -0
- code_atlas_forge-1.1.0.dist-info/licenses/LICENSE +202 -0
- code_atlas_forge-1.1.0.dist-info/licenses/NOTICE +17 -0
- code_atlas_forge-1.1.0.dist-info/top_level.txt +1 -0
code_atlas/ui/wizard.py
ADDED
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
"""Guided installation wizard (Experience Program §1.2).
|
|
2
|
+
|
|
3
|
+
Drives the *real* install service — every choice maps to an actual
|
|
4
|
+
``install()`` call; the review step is the real dry-run plan, and the
|
|
5
|
+
apply step is the governed ``--yes`` path. Nothing here writes files
|
|
6
|
+
itself.
|
|
7
|
+
|
|
8
|
+
Flow: env check → scope → profile → execution profile → components →
|
|
9
|
+
hosts → review (dry-run plan) → confirm → apply → doctor → finish.
|
|
10
|
+
Each optional step is skipped when its vocabulary is empty (a forge
|
|
11
|
+
with no gateway profiles, components or hosts simply never asks).
|
|
12
|
+
Any step can be cancelled (Esc/q) without side effects; nothing is
|
|
13
|
+
written before the explicit confirm.
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import shutil
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
from typing import Any
|
|
23
|
+
|
|
24
|
+
from code_atlas.ui import i18n
|
|
25
|
+
from code_atlas.ui.kit import (
|
|
26
|
+
NonInteractive,
|
|
27
|
+
UIContext,
|
|
28
|
+
_c,
|
|
29
|
+
confirm,
|
|
30
|
+
dashboard,
|
|
31
|
+
multi_select,
|
|
32
|
+
select,
|
|
33
|
+
)
|
|
34
|
+
|
|
35
|
+
_ALL_HOSTS = ("claude", "devin", "codex", "copilot")
|
|
36
|
+
_SCOPES = ("project", "workspace", "user")
|
|
37
|
+
_SCOPE_LABELS = {
|
|
38
|
+
"project": "Current repository",
|
|
39
|
+
"workspace": "Entire workspace",
|
|
40
|
+
"user": "My user account",
|
|
41
|
+
}
|
|
42
|
+
_PROFILES = ("recommended", "minimal", "full") # service contract names
|
|
43
|
+
_PROFILE_LABELS = {
|
|
44
|
+
"recommended": "Balanced (Recommended)",
|
|
45
|
+
"minimal": "Economy",
|
|
46
|
+
"full": "Full",
|
|
47
|
+
}
|
|
48
|
+
_GATEWAY_PROFILES = ("economy", "balanced", "deep")
|
|
49
|
+
_GATEWAY_LABELS = {
|
|
50
|
+
"economy": "Economy (lowest context cost)",
|
|
51
|
+
"balanced": "Balanced (recommended)",
|
|
52
|
+
"deep": "Deep (maximum verification)",
|
|
53
|
+
}
|
|
54
|
+
_COMPONENTS = ("skills", "agents", "mcp", "tui", "graph-studio")
|
|
55
|
+
_COMPONENT_LABELS = {
|
|
56
|
+
"skills": "Skills",
|
|
57
|
+
"agents": "Agents",
|
|
58
|
+
"mcp": "MCP",
|
|
59
|
+
"tui": "TUI",
|
|
60
|
+
"graph-studio": "Graph Studio",
|
|
61
|
+
}
|
|
62
|
+
_COMPONENT_DETAILS = {
|
|
63
|
+
"skills": "host-discoverable skills and progressive capability guidance",
|
|
64
|
+
"agents": "specialists, subagents and declared agent instructions",
|
|
65
|
+
"mcp": "MCP configuration and governed server entry points",
|
|
66
|
+
"tui": "the terminal command center runtime",
|
|
67
|
+
"graph-studio": "federated graph views and local graph navigation",
|
|
68
|
+
}
|
|
69
|
+
_COMPONENT_DEFAULTS = {
|
|
70
|
+
"minimal": {"mcp", "tui"},
|
|
71
|
+
"recommended": {"skills", "agents", "mcp", "tui", "graph-studio"},
|
|
72
|
+
"full": {"skills", "agents", "mcp", "tui", "graph-studio"},
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _has_vcs_root(start: Path) -> bool:
|
|
77
|
+
"""Return whether current directory belongs to a supported project root."""
|
|
78
|
+
return any(
|
|
79
|
+
(candidate / ".git").exists()
|
|
80
|
+
or ((candidate / "forge.json").is_file() and (candidate / "pyproject.toml").is_file())
|
|
81
|
+
for candidate in (start, *start.parents)
|
|
82
|
+
)
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def _scope_choices(ctx: UIContext, scopes: tuple[str, ...]) -> list[str]:
|
|
86
|
+
"""Hide repository-only choice when operator is in a fresh directory."""
|
|
87
|
+
choices = list(scopes)
|
|
88
|
+
if "project" in choices and not _has_vcs_root(Path.cwd()):
|
|
89
|
+
print(
|
|
90
|
+
_c(
|
|
91
|
+
ctx,
|
|
92
|
+
"33",
|
|
93
|
+
" No repository detected; choose Entire workspace to use this directory "
|
|
94
|
+
"or My user account.",
|
|
95
|
+
)
|
|
96
|
+
)
|
|
97
|
+
choices.remove("project")
|
|
98
|
+
return choices
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def env_check_rows(hosts: tuple[str, ...] = _ALL_HOSTS) -> list[tuple[str, str]]:
|
|
102
|
+
"""Real environment evidence: python, git, host binaries."""
|
|
103
|
+
v = sys.version_info
|
|
104
|
+
rows = [("Python", f"{v.major}.{v.minor}.{v.micro}")]
|
|
105
|
+
rows.append(("Git", "OK" if shutil.which("git") else "missing"))
|
|
106
|
+
for h in hosts:
|
|
107
|
+
rows.append((h.capitalize(), "Available" if shutil.which(h) else "not detected"))
|
|
108
|
+
return rows
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _banner(ctx: UIContext, forge_name: str) -> None:
|
|
112
|
+
print()
|
|
113
|
+
print(_c(ctx, "1", f" {forge_name.upper()}"))
|
|
114
|
+
print(_c(ctx, "2", " " + ("─" if ctx.unicode else "-") * max(10, len(forge_name) + 4)))
|
|
115
|
+
print(f" {i18n.t('env_detected', ctx.language)}:")
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def run_wizard(
|
|
119
|
+
*,
|
|
120
|
+
forge_name: str,
|
|
121
|
+
install_fn: Any,
|
|
122
|
+
doctor_fn: Any | None = None,
|
|
123
|
+
hosts_available: tuple[str, ...] = _ALL_HOSTS,
|
|
124
|
+
gateway_profiles: tuple[str, ...] = _GATEWAY_PROFILES,
|
|
125
|
+
scopes: tuple[str, ...] = _SCOPES,
|
|
126
|
+
component_order: tuple[str, ...] | None = _COMPONENTS,
|
|
127
|
+
component_labels: dict[str, str] | None = None,
|
|
128
|
+
component_details: dict[str, str] | None = None,
|
|
129
|
+
component_defaults: dict[str, set[str]] | None = None,
|
|
130
|
+
env_hosts: tuple[str, ...] | None = None,
|
|
131
|
+
next_hint: str | None = None,
|
|
132
|
+
ctx: UIContext | None = None,
|
|
133
|
+
out_json: bool = False,
|
|
134
|
+
family: bool = False,
|
|
135
|
+
) -> int:
|
|
136
|
+
"""Interactive install wizard. Returns exit code.
|
|
137
|
+
|
|
138
|
+
``install_fn(**kwargs)`` is the forge's real install service
|
|
139
|
+
(``host``/``scope``/``profile``/``gateway_profile``/``components``/
|
|
140
|
+
``dry_run``/``yes`` kwargs). ``doctor_fn`` optionally verifies
|
|
141
|
+
post-install. Empty vocabularies skip their step: no hosts, no
|
|
142
|
+
gateway profile or no components means the question is never asked.
|
|
143
|
+
"""
|
|
144
|
+
ctx = ctx or UIContext.detect()
|
|
145
|
+
if not ctx.interactive:
|
|
146
|
+
raise NonInteractive("install wizard requires a TTY")
|
|
147
|
+
|
|
148
|
+
comp_order = component_order or ()
|
|
149
|
+
comp_labels = component_labels or _COMPONENT_LABELS
|
|
150
|
+
comp_details = component_details or _COMPONENT_DETAILS
|
|
151
|
+
comp_defaults = component_defaults or _COMPONENT_DEFAULTS
|
|
152
|
+
|
|
153
|
+
_banner(ctx, forge_name)
|
|
154
|
+
env_rows = env_check_rows(hosts_available if env_hosts is None else env_hosts)
|
|
155
|
+
for label, val in env_rows:
|
|
156
|
+
print(f" {label:<14} {_c(ctx, '32' if val in ('OK', 'Available') else '2', val)}")
|
|
157
|
+
print()
|
|
158
|
+
if family:
|
|
159
|
+
print(_c(ctx, "36", " family mode: The Forge + registered specialist forges"))
|
|
160
|
+
print(_c(ctx, "2", " one real plan, one approval, each forge keeps its own installer"))
|
|
161
|
+
print()
|
|
162
|
+
|
|
163
|
+
# --- scope ---------------------------------------------------------------
|
|
164
|
+
scope_choices = _scope_choices(ctx, scopes)
|
|
165
|
+
idx = select(
|
|
166
|
+
i18n.t("install_scope_q", ctx.language),
|
|
167
|
+
[_SCOPE_LABELS.get(s, s) for s in scope_choices],
|
|
168
|
+
ctx=ctx,
|
|
169
|
+
)
|
|
170
|
+
if idx is None:
|
|
171
|
+
return 130
|
|
172
|
+
scope = scope_choices[idx]
|
|
173
|
+
|
|
174
|
+
# --- profile -------------------------------------------------------------
|
|
175
|
+
idx = select(
|
|
176
|
+
i18n.t("install_profile_q", ctx.language),
|
|
177
|
+
[_PROFILE_LABELS[p] for p in _PROFILES],
|
|
178
|
+
ctx=ctx,
|
|
179
|
+
)
|
|
180
|
+
if idx is None:
|
|
181
|
+
return 130
|
|
182
|
+
profile = _PROFILES[idx]
|
|
183
|
+
|
|
184
|
+
# --- execution profile ----------------------------------------------------
|
|
185
|
+
gateway_profile: str | None = None
|
|
186
|
+
if gateway_profiles:
|
|
187
|
+
idx = select(
|
|
188
|
+
i18n.t("install_gateway_profile_q", ctx.language),
|
|
189
|
+
[_GATEWAY_LABELS.get(p, p) for p in gateway_profiles],
|
|
190
|
+
ctx=ctx,
|
|
191
|
+
)
|
|
192
|
+
if idx is None:
|
|
193
|
+
return 130
|
|
194
|
+
gateway_profile = gateway_profiles[idx]
|
|
195
|
+
|
|
196
|
+
# --- optional components ---------------------------------------------------
|
|
197
|
+
components: tuple[str, ...] | None = None
|
|
198
|
+
if comp_order:
|
|
199
|
+
comp_checked = comp_defaults.get(profile, set())
|
|
200
|
+
print(_c(ctx, "2", " component choices (economy loads optional knowledge/evals on demand):"))
|
|
201
|
+
for component in comp_order:
|
|
202
|
+
detail = comp_details.get(component)
|
|
203
|
+
print(f" {component:<13} {detail}" if detail else f" {component}")
|
|
204
|
+
comp_chosen = multi_select(
|
|
205
|
+
i18n.t("install_components_q", ctx.language),
|
|
206
|
+
[comp_labels.get(c, c) for c in comp_order],
|
|
207
|
+
checked={i for i, c in enumerate(comp_order) if c in comp_checked},
|
|
208
|
+
ctx=ctx,
|
|
209
|
+
)
|
|
210
|
+
if comp_chosen is None:
|
|
211
|
+
return 130
|
|
212
|
+
components = tuple(sorted(comp_order[i] for i in comp_chosen))
|
|
213
|
+
|
|
214
|
+
# --- hosts ---------------------------------------------------------------
|
|
215
|
+
host_arg: str | None = None
|
|
216
|
+
if hosts_available:
|
|
217
|
+
detected = {
|
|
218
|
+
i
|
|
219
|
+
for i, h in enumerate(hosts_available)
|
|
220
|
+
if any(h in r[0].lower() and r[1] == "Available" for r in env_rows)
|
|
221
|
+
}
|
|
222
|
+
chosen = multi_select(
|
|
223
|
+
i18n.t("install_hosts_q", ctx.language),
|
|
224
|
+
list(hosts_available),
|
|
225
|
+
checked=detected,
|
|
226
|
+
ctx=ctx,
|
|
227
|
+
)
|
|
228
|
+
if chosen is None:
|
|
229
|
+
return 130
|
|
230
|
+
if not chosen:
|
|
231
|
+
host_arg = None
|
|
232
|
+
elif len(chosen) == len(hosts_available):
|
|
233
|
+
host_arg = "all"
|
|
234
|
+
else:
|
|
235
|
+
host_arg = ",".join(hosts_available[i] for i in sorted(chosen))
|
|
236
|
+
|
|
237
|
+
# --- review (real dry-run plan) ------------------------------------------
|
|
238
|
+
# Contrato explícito: install_fn aceita `components` — wrappers **kw
|
|
239
|
+
# encaminham sem inspeção de assinatura (GAP-002).
|
|
240
|
+
kwargs: dict[str, Any] = {
|
|
241
|
+
"scope": scope,
|
|
242
|
+
"profile": profile,
|
|
243
|
+
"gateway_profile": gateway_profile,
|
|
244
|
+
"components": components,
|
|
245
|
+
}
|
|
246
|
+
try:
|
|
247
|
+
# `none` é opt-out explícito — zero hosts escolhidos nunca vira
|
|
248
|
+
# "all" (GAP-003); csv de subconjunto é contrato válido (GAP-003b).
|
|
249
|
+
plan = install_fn(host=host_arg or "none", dry_run=True, **kwargs)
|
|
250
|
+
except Exception as exc:
|
|
251
|
+
print(_c(ctx, "31", f"plan failed: {exc}"))
|
|
252
|
+
return 1
|
|
253
|
+
|
|
254
|
+
planned = (
|
|
255
|
+
plan.get("planned")
|
|
256
|
+
or plan.get("planned_files")
|
|
257
|
+
or plan.get("writes")
|
|
258
|
+
or plan.get("plan")
|
|
259
|
+
or []
|
|
260
|
+
)
|
|
261
|
+
review_rows = [
|
|
262
|
+
("scope", scope),
|
|
263
|
+
("profile", profile),
|
|
264
|
+
]
|
|
265
|
+
if gateway_profile is not None:
|
|
266
|
+
review_rows.append(("execution", gateway_profile))
|
|
267
|
+
review_rows.append(("mode", "family" if family else "this forge"))
|
|
268
|
+
if comp_order:
|
|
269
|
+
review_rows.append(("components", ", ".join(components or ()) or "none"))
|
|
270
|
+
if hosts_available:
|
|
271
|
+
review_rows.append(("hosts", host_arg or "none"))
|
|
272
|
+
review_rows.append(
|
|
273
|
+
("managed writes", str(len(planned) if isinstance(planned, list) else planned))
|
|
274
|
+
)
|
|
275
|
+
dashboard(
|
|
276
|
+
f"{forge_name} — {i18n.t('review', ctx.language)}",
|
|
277
|
+
[("Plan", review_rows)],
|
|
278
|
+
ctx=ctx,
|
|
279
|
+
)
|
|
280
|
+
if isinstance(planned, list):
|
|
281
|
+
for entry in planned[:10]:
|
|
282
|
+
if isinstance(entry, dict):
|
|
283
|
+
path = str(entry.get("path") or entry.get("target") or "")
|
|
284
|
+
else:
|
|
285
|
+
path = str(entry)
|
|
286
|
+
print(f" {_c(ctx, '2', path)}")
|
|
287
|
+
if len(planned) > 10:
|
|
288
|
+
print(f" … {len(planned) - 10} more")
|
|
289
|
+
|
|
290
|
+
if not confirm(i18n.t("confirm_q", ctx.language), default=True, ctx=ctx):
|
|
291
|
+
return 130
|
|
292
|
+
|
|
293
|
+
# --- apply (real governed install) ---------------------------------------
|
|
294
|
+
receipt = install_fn(host=host_arg or "none", yes=True, dry_run=False, **kwargs)
|
|
295
|
+
status = receipt.get("status", "unknown")
|
|
296
|
+
if out_json:
|
|
297
|
+
print(json.dumps(receipt, indent=2, ensure_ascii=False))
|
|
298
|
+
color = "32" if status == "completed" else "31"
|
|
299
|
+
print(f"\n {i18n.t('status', ctx.language)}: {_c(ctx, color, status)}")
|
|
300
|
+
|
|
301
|
+
# --- verify --------------------------------------------------------------
|
|
302
|
+
if doctor_fn is not None and status == "completed":
|
|
303
|
+
health = doctor_fn(scope=scope)
|
|
304
|
+
raw_health = health.get("status") or health.get("health") or "unknown"
|
|
305
|
+
hstatus = raw_health
|
|
306
|
+
if isinstance(raw_health, dict):
|
|
307
|
+
hstatus = raw_health.get("status", "unknown")
|
|
308
|
+
color = "32" if hstatus in ("healthy", "PASS") else "33"
|
|
309
|
+
print(f" {i18n.t('health', ctx.language)}: {_c(ctx, color, str(hstatus))}")
|
|
310
|
+
|
|
311
|
+
if status == "completed" and next_hint:
|
|
312
|
+
print(f" next: {next_hint}")
|
|
313
|
+
|
|
314
|
+
return 0 if status == "completed" else 1
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
__all__ = ["run_wizard", "env_check_rows", "NonInteractive", "UIContext"]
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: code-atlas-forge
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Plataforma local-first de inteligência estrutural de código — Cycle 4.1: retrieval, context packs, MCP e evidência canônica
|
|
5
|
+
Author: Edgar Socrates
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Keywords: code-intelligence,code-graph,static-analysis,local-first
|
|
8
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
9
|
+
Classifier: Environment :: Console
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
14
|
+
Classifier: Topic :: Software Development
|
|
15
|
+
Requires-Python: >=3.12
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
License-File: LICENSE
|
|
18
|
+
License-File: NOTICE
|
|
19
|
+
Requires-Dist: tree-sitter==0.25.2
|
|
20
|
+
Requires-Dist: tree-sitter-python==0.23.6
|
|
21
|
+
Requires-Dist: tree-sitter-typescript==0.23.2
|
|
22
|
+
Provides-Extra: dev
|
|
23
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
24
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
25
|
+
Requires-Dist: ruff>=0.6.0; extra == "dev"
|
|
26
|
+
Requires-Dist: mypy>=1.11.0; extra == "dev"
|
|
27
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
28
|
+
Requires-Dist: mcp>=2.3.0; extra == "dev"
|
|
29
|
+
Provides-Extra: vector
|
|
30
|
+
Requires-Dist: sqlite-vec>=0.1.6; extra == "vector"
|
|
31
|
+
Dynamic: license-file
|
|
32
|
+
|
|
33
|
+
# Code Atlas Forge
|
|
34
|
+
|
|
35
|
+
<p align="center">
|
|
36
|
+
<img src="docs/assets/logo.png" alt="Code Atlas logo" width="420" />
|
|
37
|
+
</p>
|
|
38
|
+
|
|
39
|
+
Plataforma **open source, local-first e offline-first** de inteligência estrutural,
|
|
40
|
+
semântica e arquitetural de software — grafos tipados, proveniência verificável,
|
|
41
|
+
indexação incremental e contexto econômico para agentes.
|
|
42
|
+
|
|
43
|
+
> **Status atual: Cycle 8 — production quality em andamento
|
|
44
|
+
> (branch `cycle-8-production-quality`); Cycle 7 (inteligência de
|
|
45
|
+
> domínio + federação) entregue e arquivado.**
|
|
46
|
+
> O Code Atlas indexa repositórios com parsing real (Tree-sitter), extrai
|
|
47
|
+
> símbolos e constrói um grafo tipado persistido em SQLite, consultável via
|
|
48
|
+
> CLI, servidor MCP stdio, **API HTTP loopback `/api/v1`** e o
|
|
49
|
+
> **Explorer** — workbench visual Angular servido por
|
|
50
|
+
> `code-atlas explorer`. Análise de impacto entre revisões
|
|
51
|
+
> (`code-atlas impact` / `POST /impact/analyze`) está disponível com
|
|
52
|
+
> estados de binding honestos. O Cycle 7 adiciona **seis adapters de
|
|
53
|
+
> domínio opt-in** (SQL, PySpark, AWS data, API contracts, Terraform,
|
|
54
|
+
> CI/CD) com grafo de domínio snapshot-scoped (schema v9), **resolução
|
|
55
|
+
> cross-domain** por identidade qualificada, **federação multi-repo**
|
|
56
|
+
> read-only com pins por snapshot (schema v10), faceta de domínio e
|
|
57
|
+
> busca federada no SearchEngine, blast radius misto no Impact Engine,
|
|
58
|
+
> context packs de domínio, cinco telas novas no Explorer e layout FA2
|
|
59
|
+
> assíncrono em Web Worker (RAF gap máx. 83 ms, era ~2300 ms). Veja
|
|
60
|
+
> `docs/cycle-7-architecture.md`, `docs/cycle-7-homologation.md`,
|
|
61
|
+
> `docs/cycle-7-limitations.md` e `docs/roadmap.md`.
|
|
62
|
+
|
|
63
|
+
## Princípios
|
|
64
|
+
|
|
65
|
+
- **Local-first:** nenhum serviço externo, Docker, conta SaaS ou API key é
|
|
66
|
+
necessário para operar. Nenhum dado sai da máquina por padrão.
|
|
67
|
+
- **Sem telemetria:** não existe código de telemetria no projeto — a coleta
|
|
68
|
+
não é apenas desligada por padrão, ela está ausente.
|
|
69
|
+
- **O repositório indexado é não confiável:** nada do código varrido é
|
|
70
|
+
executado, importado ou instalado.
|
|
71
|
+
- **Evidência primeiro:** toda relação carrega origem, extrator, versão,
|
|
72
|
+
confiança e status de resolução.
|
|
73
|
+
- **Determinismo:** a mesma árvore produz o mesmo grafo e os mesmos ids.
|
|
74
|
+
|
|
75
|
+
A especificação arquitetural normativa está em `docs/BLUEPRINT-v1.md`.
|
|
76
|
+
|
|
77
|
+
## Instalação
|
|
78
|
+
|
|
79
|
+
Requer Python 3.12+. Dependências de runtime pinned: `tree-sitter` +
|
|
80
|
+
gramáticas (ADR-015) — instaladas automaticamente via wheels.
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
git clone https://github.com/EdgarSocrates98/code-atlas-forge.git
|
|
84
|
+
cd code-atlas-forge
|
|
85
|
+
python3 -m venv .venv
|
|
86
|
+
source .venv/bin/activate # Windows: .venv\Scripts\activate
|
|
87
|
+
pip install .
|
|
88
|
+
code-atlas --version
|
|
89
|
+
code-atlas doctor
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Para desenvolvimento: `pip install -e .[dev]`.
|
|
93
|
+
Detalhes em `docs/install.md`.
|
|
94
|
+
|
|
95
|
+
## Uso rápido
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
code-atlas init --root /caminho/do/repo # configura o workspace (XDG/APPDATA)
|
|
99
|
+
code-atlas doctor # verifica ambiente e config
|
|
100
|
+
code-atlas index /caminho/do/repo # incremental é o default; --full força rebuild
|
|
101
|
+
code-atlas index . --scip idx.scip # binding SCIP ad-hoc (proveniência mantida)
|
|
102
|
+
code-atlas index . --resolver scip # restringe resolvers ativos (erro tipado se desconhecido)
|
|
103
|
+
code-atlas index-plan /caminho/do/repo # dry-run do IndexPlan, sem tocar no DB
|
|
104
|
+
code-atlas status --json # estado do índice, git, migrações
|
|
105
|
+
code-atlas semantic-status . --json # resolvers: installed/enabled/used + coverage do READY
|
|
106
|
+
|
|
107
|
+
# consultas sobre o snapshot READY (envelope v2)
|
|
108
|
+
code-atlas search "Engine" --json # substring em qualified_name
|
|
109
|
+
code-atlas symbol pkg.core.Engine # detalhe + ocorrências
|
|
110
|
+
code-atlas references pkg.util.helper # incoming, com status/confiança
|
|
111
|
+
code-atlas definitions pkg.util.helper # defs + declarações do símbolo
|
|
112
|
+
code-atlas dependencies pkg.app.main --depth 2 # BFS outgoing, cycle-safe
|
|
113
|
+
code-atlas graph --json # dump de nós+arestas do snapshot
|
|
114
|
+
|
|
115
|
+
# retrieval híbrido + contexto (Cycle 4)
|
|
116
|
+
code-atlas search "q" --strategy lexical|structural|graph|hybrid --limit 10 --json
|
|
117
|
+
code-atlas context "create_order" --level focus --budget 4000 --format markdown
|
|
118
|
+
code-atlas context-expand --symbol pkg.orders.validate_total --snapshot 1 --delivered sym:pkg.orders
|
|
119
|
+
code-atlas mcp # servidor MCP stdio (JSON-RPC ndjson)
|
|
120
|
+
code-atlas mcp doctor --json # executável, DB, schema, sqlite_vec
|
|
121
|
+
code-atlas mcp manifest --host claude-code # emite config (nunca escreve)
|
|
122
|
+
|
|
123
|
+
# impacto entre revisões/snapshots (Cycle 5)
|
|
124
|
+
code-atlas impact report --base HEAD~1 --target HEAD --format json
|
|
125
|
+
|
|
126
|
+
# API local + Explorer visual (Cycle 6)
|
|
127
|
+
code-atlas api --port 8741 # só /api/v1, loopback, read-only
|
|
128
|
+
code-atlas explorer --open # API + UI Angular compilada
|
|
129
|
+
|
|
130
|
+
# domínios + federação (Cycle 7 — opt-in, default OFF)
|
|
131
|
+
code-atlas index . --domains sql,pyspark --cross-domain # extrai domínio no snapshot
|
|
132
|
+
code-atlas domains list # adapters registrados (listar não extrai)
|
|
133
|
+
code-atlas domains entities --domain sql # entidades do snapshot READY
|
|
134
|
+
code-atlas domains entity 'sql:mart.daily_revenue'
|
|
135
|
+
code-atlas domains lineage staging.orders --direction up --depth 3
|
|
136
|
+
code-atlas federation create minha-view # workspace multi-repo
|
|
137
|
+
code-atlas federation add minha-view /repo/a
|
|
138
|
+
code-atlas federation add minha-view /repo/b
|
|
139
|
+
code-atlas federation pin minha-view a 42 # pin de snapshot (latest desfaz)
|
|
140
|
+
code-atlas federation view minha-view --json
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
Filtros: `--language`, `--kind`, `--path`, `--snapshot`,
|
|
144
|
+
`--relation-type`, `--limit`/`--offset`. Saída JSON estável com
|
|
145
|
+
`schema_version`. Detalhes: `docs/structural-search.md`, `docs/hybrid-search.md`,
|
|
146
|
+
`docs/graph-retrieval.md`, `docs/context-engineering.md`,
|
|
147
|
+
`docs/mcp-server.md`.
|
|
148
|
+
|
|
149
|
+
Configuração: `--root` > variáveis `CODE_ATLAS_*` > `XDG_CONFIG_HOME` /
|
|
150
|
+
`%APPDATA%`. Nada é escrito dentro do repositório indexado.
|
|
151
|
+
|
|
152
|
+
## Linguagens suportadas (Cycle 2)
|
|
153
|
+
|
|
154
|
+
Python (`.py`/`.pyi`), TypeScript (`.ts` variantes), TSX (`.tsx`) —
|
|
155
|
+
matriz completa com versões de gramática pinned em
|
|
156
|
+
`docs/parser-adapters.md`. Arquivos de outras linguagens entram no
|
|
157
|
+
inventário como nós `file`, sem extração.
|
|
158
|
+
|
|
159
|
+
## Limitações
|
|
160
|
+
|
|
161
|
+
Implementado hoje: parsing sintático real, símbolos/referências/
|
|
162
|
+
relações com proveniência por aresta, resolução syntactic **integrada**
|
|
163
|
+
a resolvers opt-in SCIP/LSP, indexação incremental com cache
|
|
164
|
+
versionado, snapshots atômicos, busca estrutural SQL, **FTS5
|
|
165
|
+
snapshot-scoped**, **retrieval híbrido (RRF k=60 + rerank task-aware
|
|
166
|
+
opt-in)**, graph retrieval multi-seed com direção por intent,
|
|
167
|
+
**context packs** com budget do payload serializado e expansão
|
|
168
|
+
progressiva, **servidor MCP stdio** (read-only, homologado com o SDK
|
|
169
|
+
oficial `mcp`), **impact engine** entre revisões/snapshots com binding
|
|
170
|
+
honesto (`partial`/`mismatch`/`unavailable` nunca viram resultado
|
|
171
|
+
verificado), **API HTTP loopback `/api/v1`** stdlib read-only
|
|
172
|
+
(ADR-0009), o **Explorer Angular** (`code-atlas explorer`) — grafo
|
|
173
|
+
bounded, busca, impacto, revisões, context packs e diagnósticos — e,
|
|
174
|
+
no Cycle 7, **adapters de domínio opt-in** (SQL/PySpark/AWS data/
|
|
175
|
+
API/Terraform/CI-CD, stdlib-only, zero deps novas), **grafo de
|
|
176
|
+
domínio** persistido com estados `resolved`/`ambiguous`/`unresolved`/
|
|
177
|
+
`external`, **resolução cross-domain** por identidade qualificada,
|
|
178
|
+
**federação multi-repo** com snapshot pinado por membro, **busca
|
|
179
|
+
federada** e faceta `domain`, **blast misto** símbolo+domínio,
|
|
180
|
+
**context packs de domínio** e **views de domínio no Explorer**
|
|
181
|
+
(`/lineage`, `/contracts`, `/infra`, `/pipelines`, `/federation`).
|
|
182
|
+
|
|
183
|
+
**Não implementado** (por design): type-checking real (resolver
|
|
184
|
+
sintático não infere receiver — `d.speak()` fica `unresolved`),
|
|
185
|
+
embeddings semânticos reais (provider naïve documentado, ADR-0022),
|
|
186
|
+
structural sharing de grafo entre snapshots (rejeitado, ADR-0020),
|
|
187
|
+
escrita via API/UI (read-only por contrato), telemetria (ausente por
|
|
188
|
+
spec), conexão com cloud real (adapters de domínio leem só artefatos
|
|
189
|
+
declarados no repo — zero rede, zero execução), mutação de federação
|
|
190
|
+
por HTTP (CLI-only), FTS de entidades de domínio (`q=` é `LIKE`).
|
|
191
|
+
|
|
192
|
+
**Homologado com ressalvas** (Cycle 6.1 — `docs/cycle-6.1-limitations.md`):
|
|
193
|
+
sem benchmark de 10k nós (máximo medido: 2000 renderizados; API trunca a
|
|
194
|
+
2000, canvas capa a 500/2000), arestas do overview sem custo medido
|
|
195
|
+
(0 edges por design), Firefox/Safari/Windows interativo **NOT RUN**,
|
|
196
|
+
sem passeio com screen reader, ~55 vulnerabilidades `npm audit` (0
|
|
197
|
+
critical em produção — `docs/cycle-6.1-dependency-audit.md`).
|
|
198
|
+
**Verificado no 6.1:** 102 testes Playwright (flows, security, a11y,
|
|
199
|
+
scale, screenshots) verdes no job `explorer-e2e`; auditoria axe WCAG 2.2
|
|
200
|
+
AA em 24 scans com **0 violações**; rate limit 300/min + timeout 30 s +
|
|
201
|
+
CSP/XFO na API; drill-down via `GET /files/{id}/occurrences`;
|
|
202
|
+
`evidence_ids` emitidos nas arestas; canvas congelado pós-relayout
|
|
203
|
+
corrigido e medido; `npm ci` reproduzível.
|
|
204
|
+
Estados `unresolved`/`ambiguous`/`external` são preservados como
|
|
205
|
+
fatos — nunca preenchidos por heurística. Detalhes:
|
|
206
|
+
`docs/semantic-resolution.md`, `docs/cycle-3.1-evidence.md`,
|
|
207
|
+
`docs/semantic-accuracy-report.md`, `docs/cycle-6-homologation.md`,
|
|
208
|
+
`docs/cycle-6.1-evidence.md`, `docs/cycle-7-homologation.md`,
|
|
209
|
+
`docs/cycle-7-limitations.md`.
|
|
210
|
+
|
|
211
|
+
## Estrutura do repositório
|
|
212
|
+
|
|
213
|
+
```
|
|
214
|
+
src/code_atlas/ pacote (layout src; deps pinned = tree-sitter)
|
|
215
|
+
contracts/ modelos de domínio, enums, ports (hexagonal)
|
|
216
|
+
discovery/ scanner Git-aware + manifest de inventário
|
|
217
|
+
parsers/ engine + registry + adaptadores tree-sitter
|
|
218
|
+
graph/ builder canônico, identidades, validação
|
|
219
|
+
resolvers/ syntactic + SCIP/LSP opt-in, enrichment, bootstrap
|
|
220
|
+
storage/ SQLite: migrações v1-v10, snapshots, FTS5
|
|
221
|
+
indexing/ coordinator + persistência do grafo
|
|
222
|
+
search/ StructuralSearch (SQL parametrizado)
|
|
223
|
+
impact/ ImpactEngine: diff, binding, blast misto, risco, testes
|
|
224
|
+
domains/ adapters de domínio opt-in (sql, pyspark, aws, api,
|
|
225
|
+
terraform, cicd) + registry, identity, crossresolve
|
|
226
|
+
federation/ workspaces multi-repo, pins, views (schema v10)
|
|
227
|
+
eval/ suporte do runner de avaliação por goldens
|
|
228
|
+
api/ HTTP loopback read-only /api/v1 (stdlib, ADR-0009)
|
|
229
|
+
observability/ logging estruturado + redaction (sem telemetria)
|
|
230
|
+
explorer/ workbench Angular 18 (SPA servida por `code-atlas explorer`)
|
|
231
|
+
tests/ suíte pytest (unitários, golden corpus, e2e, bench)
|
|
232
|
+
docs/ blueprint, ADRs, roadmap, schemas, evidências
|
|
233
|
+
factory/ specs Loop Factory (inbox/active/archive + audit)
|
|
234
|
+
benchmarks/ bench_scan (cycle-1) + bench_index (cycle-2)
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
## Desenvolvimento
|
|
238
|
+
|
|
239
|
+
```bash
|
|
240
|
+
pip install -e .[dev]
|
|
241
|
+
ruff check src tests && mypy src && pytest -q
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Licença
|
|
245
|
+
|
|
246
|
+
Apache-2.0 — veja `LICENSE` e `NOTICE`.
|