sourcecode 2.0.0__py3-none-any.whl → 2.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.
sourcecode/summarizer.py CHANGED
@@ -259,7 +259,15 @@ class ProjectSummarizer:
259
259
  paragraphs.append(" ".join(current_lines).strip())
260
260
  current_lines = []
261
261
  continue
262
- if _BADGE_RE.match(line) or _LINK_ONLY_RE.match(line):
262
+ # Treat HTML-markup-only lines (<img .../>, <p align=...>, <a><img></a>)
263
+ # as paragraph breaks, like badges/links — otherwise a README whose first
264
+ # block is a centered logo leaks raw markup into the project summary.
265
+ _html_stripped = _re.sub(r"<[^>]+>", "", line).strip()
266
+ if (
267
+ _BADGE_RE.match(line)
268
+ or _LINK_ONLY_RE.match(line)
269
+ or ("<" in line and ">" in line and not _html_stripped)
270
+ ):
263
271
  if current_lines:
264
272
  paragraphs.append(" ".join(current_lines).strip())
265
273
  current_lines = []
@@ -290,9 +298,32 @@ class ProjectSummarizer:
290
298
  _link_count = len(_MD_LINK_RE.findall(paragraph))
291
299
  if _link_count > 2 and _link_count * 30 > len(paragraph):
292
300
  continue
293
- return paragraph
301
+ # Render to clean prose: unwrap inline markdown links, drop images,
302
+ # strip residual inline HTML tags and emphasis markers. A paragraph that
303
+ # collapses below the usefulness floor after cleaning is skipped.
304
+ cleaned = self._clean_inline_markup(paragraph)
305
+ if len(cleaned) < 30:
306
+ continue
307
+ return cleaned
294
308
  return None
295
309
 
310
+ @staticmethod
311
+ def _clean_inline_markup(text: str) -> str:
312
+ """Reduce README markdown/HTML to plain prose (presentation-only).
313
+
314
+ Removes inline images, unwraps `[label](url)` to `label`, strips residual
315
+ inline HTML tags and bold/italic/code markers, and collapses whitespace.
316
+ Does not touch underscores (they occur inside identifiers)."""
317
+ import re as _re
318
+ text = _re.sub(r"!\[[^\]]*\]\([^)]*\)", "", text) # ![alt](url) images
319
+ text = _re.sub(r"\[([^\]]+)\]\([^)]*\)", r"\1", text) # [label](url) -> label
320
+ text = _re.sub(r"<[^>]+>", "", text) # residual inline HTML
321
+ text = _re.sub(r"\*\*(.+?)\*\*", r"\1", text) # **bold** -> bold
322
+ text = _re.sub(r"\*(.+?)\*", r"\1", text) # *italic* -> italic
323
+ text = text.replace("`", "") # `code` -> code
324
+ text = _re.sub(r"\s+", " ", text).strip()
325
+ return text
326
+
296
327
  _TYPE_LABELS: dict[str, str] = {
297
328
  "cli": "CLI",
298
329
  "api": "API",
@@ -0,0 +1,249 @@
1
+ """validation_inference.py — Knowledge-Layer validation-pattern inference (P1-C).
2
+
3
+ Answers the field-audit failure "the report goes quiet or says 0 validated" by
4
+ classifying a repo's request-body validation *pattern* instead of counting zeros
5
+ (DESIGN-robustness-security-inference.md §2 / §5). The true finding on the field
6
+ repo was **909 `@RequestBody` and 0 `@Valid`** — "a bean-validation framework is
7
+ on the classpath but unused", not "nothing to report".
8
+
9
+ This is the Knowledge Layer: it reads facts that already exist — endpoint verbs,
10
+ `@Valid`/`@Validated` markers captured on handler nodes, and
11
+ `jakarta/javax.validation` import edges — and emits an Evidence-Confidence
12
+ `Verdict` per the shared §0 envelope. It adds no IR atom.
13
+
14
+ VAI note (§0.5): the only names this module keys on are **published open
15
+ standards** (`jakarta.validation`, `javax.validation`, the bean-validation
16
+ annotation vocabulary). No client / convention name enters a predicate.
17
+
18
+ ARCHITECTURAL DIVERGENCE — manual_validation (design §2, deferred)
19
+ -----------------------------------------------------------------
20
+ Design §2 also asks for a `manual_validation` class detected by the **β
21
+ GuardClause** shape on handler bodies (a guard that throws on a bad parameter).
22
+ That path assumes guard atoms cover handlers. They do NOT: `_extract_guard_facts`
23
+ (repository_ir.py) scans only `symbol_kind in {"method", "constructor"}`, and a
24
+ mapped handler surfaces as kind `"endpoint"` — so `guard_facts` is empty for
25
+ every handler body. Design §8 classifies §2 as *Incremental / no new primitive*;
26
+ against the current architecture that is false. Rather than smuggle in an IR
27
+ change under an "Incremental" increment, `manual_validation` is deferred and the
28
+ conflict is documented in the design doc (EPV / INV-2: build the endpoint-scoped
29
+ guard capability only when this consumer is ratified). The three classes that ARE
30
+ reuse-only (active / available-unused / none) ship here and already resolve the
31
+ field defect.
32
+ """
33
+ from __future__ import annotations
34
+
35
+ from dataclasses import dataclass, field
36
+ from typing import TYPE_CHECKING
37
+
38
+ from sourcecode.security_posture import (
39
+ Confidence,
40
+ Evidence,
41
+ Verdict,
42
+ _annotation_simple,
43
+ _graph_nodes,
44
+ )
45
+
46
+ if TYPE_CHECKING:
47
+ from sourcecode.canonical_ir import CanonicalRepositoryIR
48
+
49
+
50
+ # Published bean-validation import namespaces — DATA, subtract/match only (§0.5).
51
+ _VALIDATION_NAMESPACES: tuple[str, ...] = (
52
+ "jakarta.validation", "javax.validation",
53
+ )
54
+
55
+ # Published bean-validation constraint / marker simple-names (open standard).
56
+ _BEAN_VALIDATION_VOCAB: frozenset[str] = frozenset({
57
+ "Valid", "Validated", "NotNull", "NotEmpty", "NotBlank", "Null", "AssertTrue",
58
+ "AssertFalse", "Min", "Max", "DecimalMin", "DecimalMax", "Digits", "Positive",
59
+ "PositiveOrZero", "Negative", "NegativeOrZero", "Size", "Pattern", "Email",
60
+ "Past", "PastOrPresent", "Future", "FutureOrPresent",
61
+ })
62
+ _VALIDATE_MARKERS: frozenset[str] = frozenset({"Valid", "Validated"})
63
+
64
+ # Body-carrying HTTP verbs — the surface where request-body validation applies.
65
+ _BODY_VERBS: frozenset[str] = frozenset({"POST", "PUT", "PATCH"})
66
+
67
+
68
+ def _validation_available(cir: "CanonicalRepositoryIR") -> tuple[bool, list[str]]:
69
+ """True when a published bean-validation framework is on the classpath,
70
+ proven structurally by an import edge into a validation namespace (or, as a
71
+ fallback, any bean-validation annotation present anywhere). Returns
72
+ (available, evidence_imports)."""
73
+ imports_seen: list[str] = []
74
+ for edge in getattr(cir, "dependencies", None) or []:
75
+ if not isinstance(edge, dict) or edge.get("type") != "imports":
76
+ continue
77
+ target = str(edge.get("to") or "")
78
+ if any(target.startswith(ns + ".") or target == ns for ns in _VALIDATION_NAMESPACES):
79
+ imports_seen.append(target)
80
+ if imports_seen:
81
+ return True, sorted(set(imports_seen))
82
+
83
+ # Fallback: a bean-validation annotation used anywhere also proves the
84
+ # framework is on the classpath (even without a visible import edge).
85
+ for node in _graph_nodes(cir):
86
+ if not isinstance(node, dict):
87
+ continue
88
+ for tok in node.get("annotations") or []:
89
+ if _annotation_simple(str(tok)) in _BEAN_VALIDATION_VOCAB:
90
+ return True, []
91
+ return False, []
92
+
93
+
94
+ def _validated_handler_symbols(cir: "CanonicalRepositoryIR") -> set[str]:
95
+ """handler_symbols whose node carries a captured `_validated_params` value
96
+ naming a @Valid/@Validated parameter (reuses the existing Phase-3.2 capture)."""
97
+ out: set[str] = set()
98
+ for node in _graph_nodes(cir):
99
+ if not isinstance(node, dict):
100
+ continue
101
+ av = node.get("annotation_values") or {}
102
+ params = av.get("_validated_params", "") if isinstance(av, dict) else ""
103
+ if not params:
104
+ continue
105
+ # the capture only fires when a marker is present, but re-check by shape
106
+ if "@Valid" in params or "@Validated" in params:
107
+ out.add(node.get("fqn") or "")
108
+ out.discard("")
109
+ return out
110
+
111
+
112
+ @dataclass
113
+ class ValidationInferenceResult:
114
+ """Repo + per-endpoint request-body validation-pattern projection (P1-C)."""
115
+
116
+ pattern: str # repo-level class (see classify)
117
+ verdict: Verdict
118
+ endpoint_findings: list[dict] = field(default_factory=list)
119
+ rollup: dict = field(default_factory=dict)
120
+
121
+ def to_dict(self) -> dict:
122
+ return {
123
+ "provenance": "validation_inference projection (P1-C)",
124
+ "pattern": self.pattern,
125
+ "verdict": self.verdict.to_dict(),
126
+ "endpoints": self.endpoint_findings,
127
+ "rollup": self.rollup,
128
+ }
129
+
130
+
131
+ def infer_validation_pattern(cir: "CanonicalRepositoryIR") -> ValidationInferenceResult:
132
+ """Classify the repo's request-body validation pattern (P1-C).
133
+
134
+ Repo-level class (DESIGN §2):
135
+ - ``bean_validation_active`` — @Valid present on body handlers.
136
+ - ``bean_validation_available_unused`` — validation on the classpath and
137
+ body endpoints exist, but no handler validates → risk (Likely).
138
+ - ``no_validation_detected`` — none of the above (Unknown).
139
+ (``manual_validation`` deferred — see module docstring.)
140
+
141
+ Never raises.
142
+ """
143
+ available, import_evidence = _validation_available(cir)
144
+ validated_handlers = _validated_handler_symbols(cir)
145
+
146
+ body_eps = [ep for ep in cir.endpoints if str(ep.method).upper() in _BODY_VERBS]
147
+ body_total = len(body_eps)
148
+ validated_body = sum(1 for ep in body_eps if ep.handler_symbol in validated_handlers)
149
+ unvalidated_body = body_total - validated_body
150
+
151
+ # per-endpoint findings (only the body surface — where a request body applies)
152
+ endpoint_findings: list[dict] = []
153
+ rollup = {"validated": 0, "available_unused": 0, "unvalidated_unknown": 0}
154
+ for ep in body_eps:
155
+ if ep.handler_symbol in validated_handlers:
156
+ klass, conf = "validated", Confidence.VERIFIED
157
+ elif available:
158
+ # @RequestBody-shaped verb, framework available, no @Valid → gap
159
+ klass, conf = "available_unused", Confidence.LIKELY
160
+ else:
161
+ klass, conf = "unvalidated_unknown", Confidence.UNKNOWN
162
+ rollup[klass] += 1
163
+ endpoint_findings.append({
164
+ "endpoint_id": ep.id,
165
+ "method": ep.method,
166
+ "path": ep.path,
167
+ "handler_symbol": ep.handler_symbol,
168
+ "finding": klass,
169
+ "confidence": conf.value,
170
+ })
171
+
172
+ # repo-level classification + envelope
173
+ common_limitation = (
174
+ "Body surface is approximated by body-carrying verbs (POST/PUT/PATCH); "
175
+ "the exact @RequestBody parameter count is not captured (no IR atom "
176
+ "exposes it), so 'available_unused' may include body-less handlers."
177
+ )
178
+ manual_fn = (
179
+ "manual_validation (a handler that throws on a bad parameter) is not "
180
+ "detected — guard-clause atoms do not cover endpoint-kind handlers "
181
+ "(architectural defer, see module docstring); such a repo classifies "
182
+ "no_validation_detected here."
183
+ )
184
+
185
+ if validated_body > 0:
186
+ pattern = "bean_validation_active"
187
+ verdict = Verdict(
188
+ claim="Bean validation active on request bodies",
189
+ confidence=Confidence.VERIFIED,
190
+ evidence=Evidence(
191
+ atoms_used=[f"@Valid handler: {h}" for h in sorted(validated_handlers)][:50],
192
+ details={
193
+ "validated_body_endpoints": validated_body,
194
+ "body_endpoints": body_total,
195
+ "unvalidated_body_endpoints": unvalidated_body,
196
+ "validation_available": available,
197
+ },
198
+ ),
199
+ limitations=[common_limitation],
200
+ fn_causes=[manual_fn],
201
+ )
202
+ elif available and body_total > 0:
203
+ pattern = "bean_validation_available_unused"
204
+ verdict = Verdict(
205
+ claim="Validation framework present but unused on request bodies",
206
+ confidence=Confidence.LIKELY,
207
+ evidence=Evidence(
208
+ relations_used=[f"imports {i}" for i in import_evidence][:20],
209
+ details={
210
+ "body_endpoints": body_total,
211
+ "validated_body_endpoints": 0,
212
+ "validation_available": True,
213
+ "validation_imports": import_evidence[:20],
214
+ },
215
+ ),
216
+ limitations=[
217
+ common_limitation,
218
+ "Validation could live in a shared interceptor / filter this "
219
+ "projection does not traverse.",
220
+ ],
221
+ fp_causes=[
222
+ "A body endpoint may be intentionally unvalidated (idempotent "
223
+ "echo, internal-only), or validated manually.",
224
+ ],
225
+ fn_causes=[manual_fn],
226
+ )
227
+ else:
228
+ pattern = "no_validation_detected"
229
+ verdict = Verdict(
230
+ claim="No request-body validation pattern detected",
231
+ confidence=Confidence.UNKNOWN,
232
+ evidence=Evidence(details={
233
+ "body_endpoints": body_total,
234
+ "validation_available": available,
235
+ }),
236
+ limitations=[
237
+ "Absence of evidence is not evidence of absence — no @Valid, no "
238
+ "validation import, and no body endpoints, or validation is done "
239
+ "by a mechanism this projection does not model.",
240
+ ],
241
+ fn_causes=[manual_fn],
242
+ )
243
+
244
+ return ValidationInferenceResult(
245
+ pattern=pattern,
246
+ verdict=verdict,
247
+ endpoint_findings=endpoint_findings,
248
+ rollup=rollup,
249
+ )
@@ -0,0 +1,241 @@
1
+ Metadata-Version: 2.4
2
+ Name: sourcecode
3
+ Version: 2.1.0
4
+ Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
5
+ License-File: LICENSE
6
+ Keywords: agents,ai,codebase,context,developer-tools,llm
7
+ Classifier: Development Status :: 4 - Beta
8
+ Classifier: Environment :: Console
9
+ Classifier: Intended Audience :: Developers
10
+ Classifier: License :: OSI Approved :: Apache Software License
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.9
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
18
+ Classifier: Topic :: Utilities
19
+ Requires-Python: >=3.9
20
+ Requires-Dist: defusedxml>=0.7
21
+ Requires-Dist: mcp>=1.0.0
22
+ Requires-Dist: pathspec>=1.0
23
+ Requires-Dist: ruamel-yaml>=0.18
24
+ Requires-Dist: tomli>=2.0; python_version < '3.11'
25
+ Requires-Dist: typer>=0.24
26
+ Provides-Extra: ast
27
+ Requires-Dist: tree-sitter-javascript>=0.21; extra == 'ast'
28
+ Requires-Dist: tree-sitter-typescript>=0.21; extra == 'ast'
29
+ Requires-Dist: tree-sitter>=0.21; extra == 'ast'
30
+ Provides-Extra: dev
31
+ Requires-Dist: mcp>=1.0.0; extra == 'dev'
32
+ Requires-Dist: mypy>=1.10; extra == 'dev'
33
+ Requires-Dist: pytest>=8; extra == 'dev'
34
+ Requires-Dist: ruff>=0.15; extra == 'dev'
35
+ Provides-Extra: mcp
36
+ Requires-Dist: mcp>=1.0.0; extra == 'mcp'
37
+ Description-Content-Type: text/markdown
38
+
39
+ # ASK Engine
40
+
41
+ > **Persistent structural intelligence for AI coding agents.**
42
+
43
+ **Context · Impact · Migration · Architecture · Review — everything from one structural model.**
44
+
45
+ ![Version](https://img.shields.io/badge/version-2.1.0-blue)
46
+ ![Python](https://img.shields.io/badge/python-3.9%2B-green)
47
+
48
+ > **ASK Engine** is the product. The CLI command is **`ask`**. The legacy **`sourcecode`**
49
+ > command still works as a deprecated alias (it prints a one-line notice and forwards to
50
+ > `ask`) and remains the Python/PyPI package name for now. The authoritative version is
51
+ > whatever `ask version` reports. See [docs/PRODUCT_IDENTITY.md](docs/PRODUCT_IDENTITY.md).
52
+
53
+ ---
54
+
55
+ ## The problem
56
+
57
+ Every time an AI coding agent starts a new session, it has to re-parse the repository from scratch. For a large Java or TypeScript monolith, that means 5–15 seconds per invocation. Multiply by dozens of agent turns per hour, and repo context acquisition becomes a real bottleneck — not just latency, but tokens, compute, and iteration velocity.
58
+
59
+ ASK Engine solves this with a persistent structural cache keyed on file content hashes. After the first scan, every subsequent invocation returns pre-built context in milliseconds. The repo doesn't change? The cache doesn't expire.
60
+
61
+ **The cache is not a performance optimization. It is what makes ASK Engine usable as infrastructure rather than a one-off tool.**
62
+
63
+ ---
64
+
65
+ ## Proof — measured on real repos
66
+
67
+ | Repo | Size | Cold scan | Cache hit | Speedup |
68
+ |------|------|-----------|-----------|---------|
69
+ | Keycloak | 7,885 Java files | 10.5s | 0.6s | **~17x** |
70
+ | BroadleafCommerce | 2,985 Java files | 2.7s | 0.3s | **~9x** |
71
+
72
+ Cache keyed on content hashes — invalidated only when source changes. On repeated agent sessions against the same codebase, nearly every invocation is a cache hit.
73
+
74
+ At 0.3s per call, ASK Engine becomes **constant infrastructure** inside agent loops — call it before every edit, every PR review, every test run, without batching or caching manually.
75
+
76
+ ---
77
+
78
+ ## Install
79
+
80
+ ```bash
81
+ # Homebrew (macOS / Linux)
82
+ brew tap haroundominique/sourcecode && brew install sourcecode
83
+
84
+ # pip / pipx
85
+ pipx install sourcecode # or: pip install sourcecode
86
+
87
+ ask version # ask 2.1.0
88
+ ```
89
+
90
+ > **Package vs. command.** The install package is named `sourcecode` this release
91
+ > (renaming the distribution is a separate, breaking change). Installing it gives you the
92
+ > canonical **`ask`** command plus the deprecated **`sourcecode`** alias.
93
+
94
+ ---
95
+
96
+ ## Quickstart
97
+
98
+ ```bash
99
+ # High-signal structural summary — warm cache ~0.3s, cold 2–10s
100
+ ask --compact
101
+
102
+ # Blast radius: what breaks if this class changes?
103
+ ask impact OrderService /path/to/repo
104
+
105
+ # Spring Boot 2→3 migration readiness (bounded decision summary)
106
+ ask migrate-check /path/to/repo --compact
107
+
108
+ # Spring semantic audit: TX anomalies + security surface
109
+ ask spring-audit /path/to/repo
110
+
111
+ # Onboard to an unfamiliar codebase
112
+ ask onboard /path/to/repo
113
+
114
+ # PR review: risk, test gaps, changed modules
115
+ ask review-pr /path/to/repo --since main
116
+ ```
117
+
118
+ Full command reference: **[docs/USER_GUIDE.md](docs/USER_GUIDE.md)**.
119
+
120
+ ---
121
+
122
+ ## Capabilities
123
+
124
+ Everything is computed from one cached structural model. Seven groups:
125
+
126
+ ### 1 · Structural Context
127
+ Bounded, noise-free repo context designed to drop straight into an agent's context window.
128
+ `ask --compact` · `ask --agent` · `ask onboard` · `ask cold-start`
129
+ → [reference](docs/USER_GUIDE.md#core-commands)
130
+
131
+ ### 2 · Impact Analysis
132
+ Blast radius from a class or interface — reverse dependencies, through Spring DI, to the HTTP endpoints a change reaches.
133
+ `ask impact` · `ask impact-chain` (TX/SEC-enriched) · `ask pr-impact`
134
+ → [reference](docs/USER_GUIDE.md#core-commands)
135
+
136
+ ### 3 · Architecture Intelligence
137
+ The system map: module graph, dependency views, REST surface, per-class summaries, and a symbol-level IR for downstream tooling.
138
+ `ask export` · `ask repo-ir` · `ask endpoints` · `ask explain`
139
+ → [reference](docs/USER_GUIDE.md#core-commands)
140
+
141
+ ### 4 · Migration & Modernization
142
+ Is this codebase ready to upgrade? Per-dimension readiness (Jakarta / Spring Boot / JDK / Hibernate), located blockers, and an effort estimate.
143
+ `ask migrate-check` · `ask modernize`
144
+ → [migrate-check reference](docs/migrate-check.md) · [MODERNIZATION.md](docs/MODERNIZATION.md)
145
+
146
+ ### 5 · Spring Analysis
147
+ Deterministic Spring semantics: transactional anomalies (e.g. `@Transactional` on a private method = silent CGLIB no-op), security surface, request-body validation.
148
+ `ask spring-audit` · `ask validation`
149
+ → [reference](docs/USER_GUIDE.md#core-commands)
150
+
151
+ ### 6 · Developer Workflows
152
+ The everyday loop: diff-based PR review, symptom-driven bug triage, and delta context for continuous agent runs.
153
+ `ask review-pr` · `ask fix-bug` · `ask prepare-context`
154
+ → [reference](docs/USER_GUIDE.md#typical-workflows)
155
+
156
+ ### 7 · Utilities
157
+ `ask rename-class` (word-boundary Java rename) · `ask chunk-file` (split large files for agents) · `ask cache` (status / warm / clear / freshness)
158
+ → [reference](docs/USER_GUIDE.md)
159
+
160
+ ---
161
+
162
+ ## What it does — and doesn't
163
+
164
+ **ASK Engine reduces exploration cost.** It accelerates context acquisition and computes
165
+ blast radius; it does not replace reading code — it reduces how often an agent needs to.
166
+ All signals are **static and deterministic** (annotations, import graph, file structure) —
167
+ no runtime analysis, no LLM guessing.
168
+
169
+ Honest limits worth knowing before you rely on it:
170
+
171
+ - `impact` on an **implementation** class (`OrderServiceImpl`) returns 0 callers in Spring Boot — callers inject the interface. **Always target the interface.**
172
+ - `no_security_signal` on an endpoint means *no recognized method-level annotation*, **not** "unsecured" — Spring Security filter chains and custom authorization annotations show as `no_security_signal` unless taught via config (below).
173
+ - `spring-audit` / `impact-chain` are **Java/Spring only**; non-Java repos return `spring_detected: false`.
174
+ - Event topology (`--type events`) resolves Spring `ApplicationEvent` / `@EventListener` chains only — **not** Kafka/RabbitMQ/Redis routes.
175
+ - Architecture classification is tuned for Spring MVC layered apps; SPI/plugin models (e.g. Quarkus extensions) may be misclassified. JAX-RS subresource-locator endpoint recall is ~65%.
176
+ - Self-invocation `@Transactional` bypass (same-class call skipping the proxy) is not detected.
177
+
178
+ ---
179
+
180
+ ## Pricing
181
+
182
+ > **🎉 Early-adoption: Pro is currently unlocked for everyone.** Every install runs with
183
+ > full Pro entitlements — no size gate, no key. The tiers below describe the model the
184
+ > paywall will return to later.
185
+
186
+ **Gating is by repo size and automation — never by command.** Every command runs at full
187
+ power on Free for small and mid-size repos; you upgrade when the work gets bigger or automated.
188
+
189
+ | | **Free** — €0 | **Pro** — €19/mo · €190/yr per dev |
190
+ |---|---|---|
191
+ | Repo size | ≤ 500 Java source files | **> 500 Java files** (enterprise monoliths) |
192
+ | Commands | All of them, full output | Same commands, unlocked at scale |
193
+ | `impact` / `fix-bug` / `review-pr` / `modernize` | ✅ full on small repos | ✅ full on large repos (Free gets a capped preview) |
194
+ | `prepare-context delta` | 30 free runs/repo | unlimited — CI/CD automation |
195
+ | MCP local server, offline, no data egress | ✅ | ✅ |
196
+
197
+ **Non-Java repos are free at any size** — the size limit counts Java source files only.
198
+ ASK Engine monetises enterprise Java monoliths. Activate with `ask activate <key>`.
199
+ Full breakdown: [docs/PRODUCT_TIERS.md](docs/PRODUCT_TIERS.md).
200
+
201
+ ---
202
+
203
+ ## Configuration & privacy
204
+
205
+ ```bash
206
+ ask config # version, config file path, telemetry status
207
+ ask telemetry disable # anonymous telemetry is on by default (opt-out)
208
+ ```
209
+
210
+ Telemetry collects version, OS, commands, flags, duration, repo-size range, and errors —
211
+ **no source code, paths, secrets, or output**. Disable any time with
212
+ `export SOURCECODE_TELEMETRY=0` (or `DO_NOT_TRACK=1`).
213
+
214
+ **Custom security annotations.** Teach `endpoints`, `spring-audit`, and `explain` about
215
+ project-specific authorization annotations via an optional `sourcecode.config.json` at the
216
+ repo root (otherwise they report `policy: "none_detected"`):
217
+
218
+ ```json
219
+ {
220
+ "customSecurityAnnotations": [
221
+ { "fullyQualifiedName": "com.example.security.M3FiltroSeguridad", "shortName": "M3FiltroSeguridad" }
222
+ ]
223
+ }
224
+ ```
225
+
226
+ Matching endpoints report `policy: "custom"` and drop out of the `no_security_signal` count.
227
+
228
+ ---
229
+
230
+ ## Documentation
231
+
232
+ | Doc | What it covers |
233
+ |-----|----------------|
234
+ | [USER_GUIDE.md](docs/USER_GUIDE.md) | Full command reference, flags, output schema, workflows |
235
+ | [migrate-check.md](docs/migrate-check.md) | Migration rule catalogue (MIG-001..043) + Hibernate stratification |
236
+ | [MODERNIZATION.md](docs/MODERNIZATION.md) | The modernization product: assess → understand → plan → execute |
237
+ | [PRODUCT_TIERS.md](docs/PRODUCT_TIERS.md) | Free vs Pro, pricing model |
238
+ | [DEMO-5MIN.md](docs/DEMO-5MIN.md) | A reproducible 5-minute demo |
239
+ | [MANUAL-USUARIO.md](docs/MANUAL-USUARIO.md) | Guía de usuario en español |
240
+ | [PRODUCT_IDENTITY.md](docs/PRODUCT_IDENTITY.md) | `ask` (command) vs `sourcecode` (package/alias) |
241
+ | [privacy.md](docs/privacy.md) | Telemetry and data-handling policy |
@@ -1,4 +1,4 @@
1
- sourcecode/__init__.py,sha256=KXxsvCNAyt9RcCa2C-lchlVriTeFPszgzdkd0371mYU,308
1
+ sourcecode/__init__.py,sha256=6IP6ug5fLqU_NKOIzfMwRs-jSKYDBb21TLg1tIzLWAk,308
2
2
  sourcecode/adaptive_scanner.py,sha256=XffluXKzJUXrMtjEiAOnSNPZnztdIcts17T9ouHeID0,10521
3
3
  sourcecode/architecture_analyzer.py,sha256=6_wC4TYuBYxaqckZS0I1vBSMRPeH2vzlDB48gXwOOd4,46627
4
4
  sourcecode/architecture_summary.py,sha256=UaxmUU77oTlWxttMU3c6PxHrU-Q6q5N31TeU_NWWTdU,24297
@@ -8,7 +8,7 @@ sourcecode/call_surface.py,sha256=fiqYfHooxN1fX9oQoysq1LS3LoZcobhyUNjAGEZKpwk,41
8
8
  sourcecode/canonical_ir.py,sha256=U69m8Vkr0p407xF1q5y1KguHXWEAubYw8NFHR9hDNJk,29178
9
9
  sourcecode/cir_graphs.py,sha256=9G0HHj1kw2325IDyzo2OpX73BNswEckecf4MZUXB4JM,12078
10
10
  sourcecode/classifier.py,sha256=YTTCoRdcLEFRVcql9Ow1dE7eYQj0jq2rgx32bRDnb1k,13852
11
- sourcecode/cli.py,sha256=5ykrowoQdp7i9CzPPVv1jrwE6jkNs1Myd6eY-wnZ8Kk,288344
11
+ sourcecode/cli.py,sha256=PksGF5DiVcPU0SUHw0vKwQA-9w934ZMKCix1K4WX8Rs,289590
12
12
  sourcecode/code_notes_analyzer.py,sha256=EJemNCNc9Dn-1RZYu-aNbK0ELzmsyC4s6FdHi3XyNEI,9392
13
13
  sourcecode/confidence_analyzer.py,sha256=_jckZSxksV-OU38vbkxfVNBnWCtlCq8Vwfg23x1uspA,19054
14
14
  sourcecode/context_graph.py,sha256=8MVDu06bPhvRDTgUqWEvRME2fOw34bFLf4OUILZd13I,43527
@@ -37,7 +37,7 @@ sourcecode/jdk_exports.py,sha256=fCrlwNAXUT9gge_joq6kMnY3zJxYB2pxqy-0w3o3MJI,874
37
37
  sourcecode/license.py,sha256=keFuwNxdAtvK2Ds91Wl79GMYxuxWYnN5Wbw1qBpaoUI,24896
38
38
  sourcecode/mcp_nudge.py,sha256=yjwclsBVsDg1BtocGdEi6GYV4vzPo-SBlC9pIcNmlDM,2910
39
39
  sourcecode/metrics_analyzer.py,sha256=m0ENgtqKeBL17kUIK3fmGkgo7UfXBNHxCMj0H_Y5K7c,22750
40
- sourcecode/migrate_check.py,sha256=Fz12IrMTQWjvbT7melwJGf1aNqQDtzbyRPd_bDA7hdw,105025
40
+ sourcecode/migrate_check.py,sha256=HJhvHuvXDGTJSRQU9_AMx0L94h2TImC1DCGs3m3RTu0,108199
41
41
  sourcecode/openapi_surface.py,sha256=BTt0K-woZbkbWTN77IkqeBm_Okag9owR0848fmot8sk,16207
42
42
  sourcecode/output_budget.py,sha256=Js9yUlfQtPhqBl9R6wn_9UHVjjJc3GtLcqyfjf5t50Q,9869
43
43
  sourcecode/path_filters.py,sha256=VnaD9jxZVNzluackNSTCdIddwzAqIseuSCs_A-gpCDM,6898
@@ -56,21 +56,23 @@ sourcecode/runtime_classifier.py,sha256=uTAD6BDCiBLUZEDRfqk718kM4RTT_vAbfkcOI2_X
56
56
  sourcecode/scanner.py,sha256=WdOQ78mMzjR1NjmKTlbxdgwinnCTfAhxCVLBEFQiFHU,8899
57
57
  sourcecode/schema.py,sha256=aHNXDf8LGyUC8ZDE_VS9kiskC2-Oswhi_WnpdGy6HDw,24897
58
58
  sourcecode/security_config.py,sha256=_m8oQAy2gP7Ho2I-IIMTFFjFVWbJIGlLEOiAWK2TDjs,3503
59
+ sourcecode/security_posture.py,sha256=CAJw4oJD0aAW2yRewvd_fonkzi9_HpUjBZlIDZle00s,26034
59
60
  sourcecode/semantic_analyzer.py,sha256=bpgdC6m0_ftVtRf3rSdwhbhWjnZnGxRXaZVcfe4BbcQ,95414
60
61
  sourcecode/semantic_impact_engine.py,sha256=HpgjenY6DLPHPomP7Hw850zA-EBokf1S4O2sh8NegdI,20351
61
62
  sourcecode/semantic_integration_engine.py,sha256=cUJFZaBfgzsYpj9hRa9CZjo3U9VSfIQV1k2SQYM5HZw,14524
62
63
  sourcecode/semantic_services.py,sha256=AO_p7PRfjc-AUh6ThvLMfm4iUGi0issL_frCf8sxGlI,10637
63
64
  sourcecode/serializer.py,sha256=_3aLX3iFbHlqVDx_IlmOBQUI6QLPrL_XX2jxF6gL_No,129094
64
65
  sourcecode/spring_event_topology.py,sha256=5_ON_21Le5zbG-1GRc5GLIi5HJfy_QjcXLVPC5WeUGQ,18055
65
- sourcecode/spring_findings.py,sha256=G7Or2lKBUQbcTDqudLvSs9XvNg_YoAa-_lBOG_ULs8E,5457
66
+ sourcecode/spring_findings.py,sha256=EX7kLZLN74CFyR9iZPm3CI115BfFKNd4WRPuErNljZM,5729
66
67
  sourcecode/spring_impact.py,sha256=1Eu1vkdwTVsw92iBEJDe_i_FaSf4uGH9Rb0eSgIAAZc,57542
67
68
  sourcecode/spring_model.py,sha256=zOAgFmrRbG4a6KLm1TJl55aWMyPNsz3OS3FSczqPG6A,16594
68
- sourcecode/spring_security_audit.py,sha256=xrdv3MaR_m2wblA6a8MRWM7attvZeoPXKsoRO_R2lY4,21401
69
+ sourcecode/spring_security_audit.py,sha256=Rk-aSohezdc7YDYbSoJquVnwpkDB8ty1BCD-4Hc4R5A,22832
69
70
  sourcecode/spring_semantic.py,sha256=O1nKSGVzlukuxLHQVuCPxc-XrcrMFxwlHA20_dmEGgM,13307
70
71
  sourcecode/spring_tx_analyzer.py,sha256=iH9mK0Tgvhgy_Ee2r13KA_RMf8HKN-pjCKQGuyCCBjM,33812
71
- sourcecode/summarizer.py,sha256=hV-kPFXXh15GnIvmLZnmVWTEbmr35C-yKVtPiR25U2g,21802
72
+ sourcecode/summarizer.py,sha256=ByfFxIGVRrGrf3paQPuDOg1rVj78LyzXN_EXBkfbCLU,23582
72
73
  sourcecode/tree_utils.py,sha256=8GAkIfQAsvtEudIeW1l4ooH_oRtrWR8cpJQJsEa_Pfw,2093
73
74
  sourcecode/type_usage_surface.py,sha256=51IrKRQoIoRnlsiDjHnqpJBn2rc6E59aRhgS0HTzAF0,4428
75
+ sourcecode/validation_inference.py,sha256=UtmYv-d4TEzT1ihGEipAhtXDrwq0XtVtjMpb3i7NsNc,10815
74
76
  sourcecode/validation_surface.py,sha256=A7h0Z9xHjPrxUPkyoPKlhGb8tCF0w0PI_XT-tOj5sC4,21375
75
77
  sourcecode/version_check.py,sha256=CHp6ZxTIfo8kyHPCBgJA1uFC0xQCoXMuuOfrW8QTL8o,4942
76
78
  sourcecode/workspace.py,sha256=X_6NmNnitvT3_38V-JDChydo_sR68s249hLFlrQskU0,8271
@@ -111,8 +113,8 @@ sourcecode/telemetry/consent.py,sha256=LIAO9ohJZF8OuZwM4u1VWtALlYfTCCKq4wV3Vwc7i
111
113
  sourcecode/telemetry/events.py,sha256=LtzYfaX9Ilckj5PTvAcTpDa9mLqDsYPDUiDkRa58piY,2580
112
114
  sourcecode/telemetry/filters.py,sha256=NHa5T-6DaZduQPFuC34jOqHWQgSizM-Ygq8aZ4j19ng,5834
113
115
  sourcecode/telemetry/transport.py,sha256=4gGHsq0WeY9VywEZXA3vUxykfiYnw9uuqfjAAec7F8o,1681
114
- sourcecode-2.0.0.dist-info/METADATA,sha256=qxaCEW0jVsLWYJeZczHV1L2NPXLpJScDWC3ztFPtL-k,48445
115
- sourcecode-2.0.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
116
- sourcecode-2.0.0.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
117
- sourcecode-2.0.0.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
118
- sourcecode-2.0.0.dist-info/RECORD,,
116
+ sourcecode-2.1.0.dist-info/METADATA,sha256=YcJphSnTG4SGmAeb7R2XWhj4nIp4-1hazejT7QdPo9Y,10798
117
+ sourcecode-2.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
118
+ sourcecode-2.1.0.dist-info/entry_points.txt,sha256=-JEAdChrK5We51kZcb7OaDcyil-dHBjBPL-NhuO-QY8,89
119
+ sourcecode-2.1.0.dist-info/licenses/LICENSE,sha256=7DdHrU9Z_3e7dSvq4ISijZNjnuHo5NIHNiHDouMQ9JU,10491
120
+ sourcecode-2.1.0.dist-info/RECORD,,