sourcecode 1.73.0__tar.gz → 2.0.1__tar.gz

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 (136) hide show
  1. {sourcecode-1.73.0 → sourcecode-2.0.1}/.gitignore +4 -0
  2. {sourcecode-1.73.0 → sourcecode-2.0.1}/CHANGELOG.md +26 -0
  3. {sourcecode-1.73.0 → sourcecode-2.0.1}/PKG-INFO +22 -10
  4. {sourcecode-1.73.0 → sourcecode-2.0.1}/README.md +21 -9
  5. {sourcecode-1.73.0 → sourcecode-2.0.1}/pyproject.toml +1 -1
  6. sourcecode-2.0.1/scripts/compare_integration_engines.py +62 -0
  7. sourcecode-2.0.1/scripts/customer_smoke_test.sh +87 -0
  8. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/__init__.py +1 -1
  9. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/architecture_analyzer.py +63 -55
  10. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/ast_extractor.py +130 -135
  11. sourcecode-2.0.1/src/sourcecode/call_surface.py +112 -0
  12. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/canonical_ir.py +62 -1
  13. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/cli.py +41 -16
  14. sourcecode-2.0.1/src/sourcecode/context_graph.py +1022 -0
  15. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/contract_pipeline.py +16 -1
  16. sourcecode-2.0.1/src/sourcecode/dynamic_argument_surface.py +54 -0
  17. sourcecode-2.0.1/src/sourcecode/endpoint_literals.py +71 -0
  18. sourcecode-2.0.1/src/sourcecode/evidence_provider.py +90 -0
  19. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/explain.py +28 -13
  20. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/graph_analyzer.py +82 -8
  21. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/license.py +12 -1
  22. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/migrate_check.py +64 -0
  23. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/pr_comment_renderer.py +7 -6
  24. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/prepare_context.py +10 -13
  25. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/repository_ir.py +881 -140
  26. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/semantic_analyzer.py +146 -9
  27. sourcecode-2.0.1/src/sourcecode/semantic_impact_engine.py +403 -0
  28. sourcecode-2.0.1/src/sourcecode/semantic_integration_engine.py +299 -0
  29. sourcecode-2.0.1/src/sourcecode/semantic_services.py +229 -0
  30. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/serializer.py +23 -31
  31. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/summarizer.py +33 -2
  32. sourcecode-2.0.1/src/sourcecode/type_usage_surface.py +101 -0
  33. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/validation_surface.py +173 -205
  34. sourcecode-2.0.1/supabase/sql/licensing_schema.sql +108 -0
  35. sourcecode-1.73.0/src/sourcecode/flow_analyzer.py +0 -671
  36. sourcecode-1.73.0/src/sourcecode/integration_detector.py +0 -435
  37. {sourcecode-1.73.0 → sourcecode-2.0.1}/.github/workflows/build-windows.yml +0 -0
  38. {sourcecode-1.73.0 → sourcecode-2.0.1}/.ruff.toml +0 -0
  39. {sourcecode-1.73.0 → sourcecode-2.0.1}/CONTRIBUTING.md +0 -0
  40. {sourcecode-1.73.0 → sourcecode-2.0.1}/LICENSE +0 -0
  41. {sourcecode-1.73.0 → sourcecode-2.0.1}/SECURITY.md +0 -0
  42. {sourcecode-1.73.0 → sourcecode-2.0.1}/raw +0 -0
  43. {sourcecode-1.73.0 → sourcecode-2.0.1}/scripts/generate_jdk_exports.py +0 -0
  44. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/adaptive_scanner.py +0 -0
  45. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/architecture_summary.py +0 -0
  46. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/cache.py +0 -0
  47. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/cir_graphs.py +0 -0
  48. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/classifier.py +0 -0
  49. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/code_notes_analyzer.py +0 -0
  50. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/confidence_analyzer.py +0 -0
  51. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/context_scorer.py +0 -0
  52. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/context_summarizer.py +0 -0
  53. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/contract_model.py +0 -0
  54. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/coverage_parser.py +0 -0
  55. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/dependency_analyzer.py +0 -0
  56. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/__init__.py +0 -0
  57. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/base.py +0 -0
  58. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/csproj_parser.py +0 -0
  59. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/dart.py +0 -0
  60. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/dotnet.py +0 -0
  61. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/elixir.py +0 -0
  62. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/go.py +0 -0
  63. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/heuristic.py +0 -0
  64. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/hybrid.py +0 -0
  65. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/java.py +0 -0
  66. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/jvm_ext.py +0 -0
  67. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/nodejs.py +0 -0
  68. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/parsers.py +0 -0
  69. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/php.py +0 -0
  70. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/project.py +0 -0
  71. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/python.py +0 -0
  72. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/ruby.py +0 -0
  73. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/rust.py +0 -0
  74. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/systems.py +0 -0
  75. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/terraform.py +0 -0
  76. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/detectors/tooling.py +0 -0
  77. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/doc_analyzer.py +0 -0
  78. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/entrypoint_classifier.py +0 -0
  79. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/env_analyzer.py +0 -0
  80. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/error_schema.py +0 -0
  81. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/file_chunker.py +0 -0
  82. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/file_classifier.py +0 -0
  83. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/format_contract.py +0 -0
  84. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/fqn_utils.py +0 -0
  85. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/git_analyzer.py +0 -0
  86. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/hibernate_strat.py +0 -0
  87. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/jdk_exports.py +0 -0
  88. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/mcp/__init__.py +0 -0
  89. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/mcp/onboarding/__init__.py +0 -0
  90. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/mcp/onboarding/applier.py +0 -0
  91. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/mcp/onboarding/backup.py +0 -0
  92. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/mcp/onboarding/detector.py +0 -0
  93. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/mcp/onboarding/planner.py +0 -0
  94. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/mcp/orchestrator.py +0 -0
  95. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/mcp/registry.py +0 -0
  96. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/mcp/runner.py +0 -0
  97. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/mcp/server.py +0 -0
  98. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/mcp_nudge.py +0 -0
  99. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/metrics_analyzer.py +0 -0
  100. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/openapi_surface.py +0 -0
  101. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/output_budget.py +0 -0
  102. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/path_filters.py +0 -0
  103. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/pr_impact.py +0 -0
  104. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/progress.py +0 -0
  105. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/ranking_engine.py +0 -0
  106. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/redactor.py +0 -0
  107. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/relevance_scorer.py +0 -0
  108. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/rename_refactor.py +0 -0
  109. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/repo_classifier.py +0 -0
  110. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/ris.py +0 -0
  111. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/runtime_classifier.py +0 -0
  112. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/scanner.py +0 -0
  113. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/schema.py +0 -0
  114. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/security_config.py +0 -0
  115. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/spring_event_topology.py +0 -0
  116. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/spring_findings.py +0 -0
  117. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/spring_impact.py +0 -0
  118. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/spring_model.py +0 -0
  119. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/spring_security_audit.py +0 -0
  120. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/spring_semantic.py +0 -0
  121. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/spring_tx_analyzer.py +0 -0
  122. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/telemetry/__init__.py +0 -0
  123. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/telemetry/config.py +0 -0
  124. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/telemetry/consent.py +0 -0
  125. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/telemetry/events.py +0 -0
  126. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/telemetry/filters.py +0 -0
  127. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/telemetry/transport.py +0 -0
  128. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/tree_utils.py +0 -0
  129. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/version_check.py +0 -0
  130. {sourcecode-1.73.0 → sourcecode-2.0.1}/src/sourcecode/workspace.py +0 -0
  131. {sourcecode-1.73.0 → sourcecode-2.0.1}/supabase/functions/README.md +0 -0
  132. {sourcecode-1.73.0 → sourcecode-2.0.1}/supabase/functions/get-license/index.ts +0 -0
  133. {sourcecode-1.73.0 → sourcecode-2.0.1}/supabase/functions/lemonsqueezy-webhook/index.ts +0 -0
  134. {sourcecode-1.73.0 → sourcecode-2.0.1}/supabase/functions/telemetry/index.ts +0 -0
  135. {sourcecode-1.73.0 → sourcecode-2.0.1}/supabase/sql/license_event_ordering.sql +0 -0
  136. {sourcecode-1.73.0 → sourcecode-2.0.1}/supabase/sql/telemetry_events.sql +0 -0
@@ -35,6 +35,10 @@ Thumbs.db
35
35
 
36
36
  .planning
37
37
 
38
+ # ContextGraph baseline — full canonical outputs (multi-MB, from external repos).
39
+ # The light oracle (index.json) and convergence.json ARE committed.
40
+ tests/contextgraph_baseline/baseline/artifacts/
41
+
38
42
  # Supabase local temp
39
43
  supabase/.temp/
40
44
  # Internal agent playbook (not distributed)
@@ -1,5 +1,31 @@
1
1
  # Changelog
2
2
 
3
+ ## [2.0.1] — 2026-07-10
4
+
5
+ ### Added
6
+
7
+ - **`migrate-check --compact` — bounded decision summary.** A new opt-in view of the
8
+ migration report that keeps every decision field (readiness scores, headline blocker,
9
+ blocking count, estimated effort, executive summary, Hibernate classification, aggregate
10
+ counts) and caps each large collection to its top items with a `+N more` count. On real
11
+ monoliths the report shrinks ~97% (BroadleafCommerce 1.2 MB → 32 KB, OpenMRS 908 KB →
12
+ 23 KB) — an agent or reviewer gets the verdict without the leaf-level firehose. The full
13
+ report remains the default output.
14
+
15
+ ### Fixed
16
+
17
+ - **`onboard` project summary no longer leaks raw README markup.** READMEs that open with
18
+ a logo block (`<img>` / `<p>` HTML) or use inline markdown links (`[label](url)`) had
19
+ produced summaries like `API: <img src="…"/>`. The summary is now rendered to clean prose
20
+ — HTML/badge blocks are skipped and inline links are unwrapped to their label.
21
+ - **More complete blast radius in `impact` and `review-pr`.** Change-impact analysis now
22
+ surfaces every affected entry point for a changed repository/service, reducing missed
23
+ callers on interface- and DI-wired code.
24
+
25
+ ### Notes
26
+
27
+ - No breaking changes. Default command outputs are unchanged except where explicitly noted.
28
+
3
29
  ## [1.73.0] — 2026-07-01
4
30
 
5
31
  ### Fixed — audit reliability on a large non-Boot Spring monolith (Alfresco Community Repo field test)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 1.73.0
3
+ Version: 2.0.1
4
4
  Summary: Persistent structural context and ultra-fast repeated analysis for AI coding agents
5
5
  License-File: LICENSE
6
6
  Keywords: agents,ai,codebase,context,developer-tools,llm
@@ -36,22 +36,26 @@ Provides-Extra: mcp
36
36
  Requires-Dist: mcp>=1.0.0; extra == 'mcp'
37
37
  Description-Content-Type: text/markdown
38
38
 
39
- # sourcecode
39
+ # ASK Engine
40
40
 
41
41
  **Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
42
42
 
43
- ![Version](https://img.shields.io/badge/version-1.59.0-blue)
43
+ ![Version](https://img.shields.io/badge/version-2.0.1-blue)
44
44
  ![Python](https://img.shields.io/badge/python-3.9%2B-green)
45
45
 
46
+ > **ASK Engine** is the product. The CLI you install and run is **`sourcecode`** (the
47
+ > executable and Python package). The authoritative version is whatever
48
+ > `sourcecode version` reports.
49
+
46
50
  ---
47
51
 
48
52
  ## The problem
49
53
 
50
54
  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.
51
55
 
52
- `sourcecode` 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.
56
+ 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.
53
57
 
54
- **The cache is not a performance optimization. It is what makes sourcecode usable as infrastructure rather than a one-off tool.**
58
+ **The cache is not a performance optimization. It is what makes ASK Engine usable as infrastructure rather than a one-off tool.**
55
59
 
56
60
  ---
57
61
 
@@ -114,7 +118,7 @@ pipx install sourcecode
114
118
 
115
119
  ```bash
116
120
  sourcecode version
117
- # sourcecode 1.59.0
121
+ # sourcecode 2.0.1
118
122
  ```
119
123
 
120
124
  ---
@@ -168,7 +172,7 @@ sourcecode fix-bug /path/to/repo --symptom "NullPointerException in checkout"
168
172
 
169
173
  ## Cache system
170
174
 
171
- sourcecode maintains a persistent cache at `.sourcecode-cache/` inside each repository. Two layers:
175
+ ASK Engine maintains a persistent cache at `.sourcecode-cache/` inside each repository. Two layers:
172
176
 
173
177
  - **L1 (core):** analysis result keyed by `(git_sha, analysis_flags)`. Survives format changes — you can regenerate `--compact` vs `--agent` views from the same core.
174
178
  - **L2 (view):** rendered output keyed by `(core_hash, view_flags)`. Exact output match — no recomputation.
@@ -269,9 +273,9 @@ if [ "$DECISION" != "analysis_success" ]; then echo "Review failed: $DECISION";
269
273
 
270
274
  ---
271
275
 
272
- ## What sourcecode does (and doesn't)
276
+ ## What ASK Engine does (and doesn't)
273
277
 
274
- **sourcecode reduces exploration cost.** It accelerates context acquisition and minimizes repeated repo parsing. It does not replace reading code — it reduces how often an agent needs to.
278
+ **ASK Engine reduces exploration cost.** It accelerates context acquisition and minimizes repeated repo parsing. It does not replace reading code — it reduces how often an agent needs to.
275
279
 
276
280
  Specifically:
277
281
 
@@ -317,7 +321,7 @@ when the work gets bigger or automated.
317
321
  | Offline, no data egress, no account | ✅ | ✅ |
318
322
 
319
323
  **Non-Java repos are free at any size** — the size limit counts Java source
320
- files only, by design. sourcecode monetises enterprise Java monoliths.
324
+ files only, by design. ASK Engine monetises enterprise Java monoliths.
321
325
 
322
326
  ```bash
323
327
  sourcecode activate <key> # activate a license key
@@ -595,11 +599,19 @@ High-coupling nodes (high fan-in = risky to change), dead zone candidates (isola
595
599
 
596
600
  ```bash
597
601
  sourcecode migrate-check /path/to/repo
602
+ sourcecode migrate-check . --compact # bounded decision summary (score, blocker, effort, top items)
598
603
  sourcecode migrate-check . --min-severity high
599
604
  sourcecode migrate-check . --format text
600
605
  sourcecode migrate-check . --output migration.json
601
606
  ```
602
607
 
608
+ `--compact` returns the decision-grade envelope — readiness scores, headline blocker,
609
+ blocking count, estimated effort, executive summary, Hibernate classification and
610
+ aggregate counts — with each large collection capped to its top items (`+N more`). On
611
+ large monoliths this is ~97% smaller than the full report (e.g. 1.2 MB → 32 KB), so an
612
+ agent or reviewer gets the verdict without the full leaf-level firehose. The complete
613
+ report remains the default.
614
+
603
615
  Detects migration blockers across Java source files, Spring XML config files, and Maven/Gradle build files. 27 rules organized by target:
604
616
 
605
617
  **Jakarta namespace (MIG-001..009) — javax→jakarta**
@@ -1,19 +1,23 @@
1
- # sourcecode
1
+ # ASK Engine
2
2
 
3
3
  **Persistent structural context and ultra-fast repeated analysis for AI coding agents.**
4
4
 
5
- ![Version](https://img.shields.io/badge/version-1.59.0-blue)
5
+ ![Version](https://img.shields.io/badge/version-2.0.1-blue)
6
6
  ![Python](https://img.shields.io/badge/python-3.9%2B-green)
7
7
 
8
+ > **ASK Engine** is the product. The CLI you install and run is **`sourcecode`** (the
9
+ > executable and Python package). The authoritative version is whatever
10
+ > `sourcecode version` reports.
11
+
8
12
  ---
9
13
 
10
14
  ## The problem
11
15
 
12
16
  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.
13
17
 
14
- `sourcecode` 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.
18
+ 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.
15
19
 
16
- **The cache is not a performance optimization. It is what makes sourcecode usable as infrastructure rather than a one-off tool.**
20
+ **The cache is not a performance optimization. It is what makes ASK Engine usable as infrastructure rather than a one-off tool.**
17
21
 
18
22
  ---
19
23
 
@@ -76,7 +80,7 @@ pipx install sourcecode
76
80
 
77
81
  ```bash
78
82
  sourcecode version
79
- # sourcecode 1.59.0
83
+ # sourcecode 2.0.1
80
84
  ```
81
85
 
82
86
  ---
@@ -130,7 +134,7 @@ sourcecode fix-bug /path/to/repo --symptom "NullPointerException in checkout"
130
134
 
131
135
  ## Cache system
132
136
 
133
- sourcecode maintains a persistent cache at `.sourcecode-cache/` inside each repository. Two layers:
137
+ ASK Engine maintains a persistent cache at `.sourcecode-cache/` inside each repository. Two layers:
134
138
 
135
139
  - **L1 (core):** analysis result keyed by `(git_sha, analysis_flags)`. Survives format changes — you can regenerate `--compact` vs `--agent` views from the same core.
136
140
  - **L2 (view):** rendered output keyed by `(core_hash, view_flags)`. Exact output match — no recomputation.
@@ -231,9 +235,9 @@ if [ "$DECISION" != "analysis_success" ]; then echo "Review failed: $DECISION";
231
235
 
232
236
  ---
233
237
 
234
- ## What sourcecode does (and doesn't)
238
+ ## What ASK Engine does (and doesn't)
235
239
 
236
- **sourcecode reduces exploration cost.** It accelerates context acquisition and minimizes repeated repo parsing. It does not replace reading code — it reduces how often an agent needs to.
240
+ **ASK Engine reduces exploration cost.** It accelerates context acquisition and minimizes repeated repo parsing. It does not replace reading code — it reduces how often an agent needs to.
237
241
 
238
242
  Specifically:
239
243
 
@@ -279,7 +283,7 @@ when the work gets bigger or automated.
279
283
  | Offline, no data egress, no account | ✅ | ✅ |
280
284
 
281
285
  **Non-Java repos are free at any size** — the size limit counts Java source
282
- files only, by design. sourcecode monetises enterprise Java monoliths.
286
+ files only, by design. ASK Engine monetises enterprise Java monoliths.
283
287
 
284
288
  ```bash
285
289
  sourcecode activate <key> # activate a license key
@@ -557,11 +561,19 @@ High-coupling nodes (high fan-in = risky to change), dead zone candidates (isola
557
561
 
558
562
  ```bash
559
563
  sourcecode migrate-check /path/to/repo
564
+ sourcecode migrate-check . --compact # bounded decision summary (score, blocker, effort, top items)
560
565
  sourcecode migrate-check . --min-severity high
561
566
  sourcecode migrate-check . --format text
562
567
  sourcecode migrate-check . --output migration.json
563
568
  ```
564
569
 
570
+ `--compact` returns the decision-grade envelope — readiness scores, headline blocker,
571
+ blocking count, estimated effort, executive summary, Hibernate classification and
572
+ aggregate counts — with each large collection capped to its top items (`+N more`). On
573
+ large monoliths this is ~97% smaller than the full report (e.g. 1.2 MB → 32 KB), so an
574
+ agent or reviewer gets the verdict without the full leaf-level firehose. The complete
575
+ report remains the default.
576
+
565
577
  Detects migration blockers across Java source files, Spring XML config files, and Maven/Gradle build files. 27 rules organized by target:
566
578
 
567
579
  **Jakarta namespace (MIG-001..009) — javax→jakarta**
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "sourcecode"
7
- version = "1.73.0"
7
+ version = "2.0.1"
8
8
  description = "Persistent structural context and ultra-fast repeated analysis for AI coding agents"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -0,0 +1,62 @@
1
+ #!/usr/bin/env python3
2
+ """Reproducible comparison harness for Increment #6 (Semantic Integration Engine spike).
3
+
4
+ Runs the legacy heuristic `integration_detector` and the new read-only
5
+ `SemanticIntegrationEngine` over one or more reference repos and prints the metrics
6
+ used in docs/architecture/REPORT-p6-integration-detector-comparison.md.
7
+
8
+ Usage:
9
+ python3 scripts/compare_integration_engines.py <repo_path> [<repo_path> ...]
10
+
11
+ Neither engine is wired into the product; evidence-gathering only.
12
+ """
13
+ from __future__ import annotations
14
+
15
+ import sys
16
+ import time
17
+ from collections import Counter
18
+ from pathlib import Path
19
+
20
+ from sourcecode.context_graph import ContextGraph
21
+ from sourcecode.integration_detector import detect_integrations
22
+ from sourcecode.semantic_integration_engine import SemanticIntegrationEngine
23
+
24
+
25
+ def run(repo: str) -> None:
26
+ root = Path(repo)
27
+ files = [str(p.relative_to(root)) for p in sorted(root.rglob("*.java"))
28
+ if "/test/" not in str(p)]
29
+
30
+ t0 = time.time()
31
+ det = detect_integrations(files, root)
32
+ det_s = time.time() - t0
33
+
34
+ t1 = time.time()
35
+ g = ContextGraph.build(files, root)
36
+ build_s = time.time() - t1
37
+ t2 = time.time()
38
+ sem = SemanticIntegrationEngine(g).as_report()
39
+ sem_s = time.time() - t2
40
+
41
+ det_kinds = Counter(r["kind"] for r in det["integrations"])
42
+ sem_kinds = Counter(r["kind"] for r in sem["integrations"])
43
+ det_targets = sum(1 for r in det["integrations"] if r.get("target"))
44
+ sem_targets = sum(1 for r in sem["integrations"] if r.get("target"))
45
+
46
+ print(f"### {root.name} (files={len(files)})")
47
+ print(f" detector : count={det['count']:>3} targets={det_targets:>3} "
48
+ f"kinds={dict(sorted(det_kinds.items()))} time={det_s:.2f}s source_read=yes")
49
+ print(f" semantic : count={sem['count']:>3} targets={sem_targets:>3} "
50
+ f"kinds={dict(sorted(sem_kinds.items()))} build={build_s:.2f}s detect={sem_s:.2f}s source_read=no")
51
+ only_sem = sorted(set(sem_kinds) - set(det_kinds))
52
+ only_det = sorted(set(det_kinds) - set(sem_kinds))
53
+ print(f" kinds only-semantic={only_sem} only-detector={only_det}")
54
+ print()
55
+
56
+
57
+ if __name__ == "__main__":
58
+ if len(sys.argv) < 2:
59
+ print(__doc__)
60
+ sys.exit(1)
61
+ for repo in sys.argv[1:]:
62
+ run(repo)
@@ -0,0 +1,87 @@
1
+ #!/usr/bin/env bash
2
+ # Customer Smoke Test — verify in < 5 minutes that a real customer can install,
3
+ # activate, and use ASK Engine (Free + Pro), and that revocation removes access.
4
+ #
5
+ # Reproducible. Sections A/B/C escalate in what they require:
6
+ # A. Offline, no license — always runnable (also covered by tests/test_customer_smoke.py)
7
+ # B. Real activation — needs a real Pro key + network (set SC_SMOKE_KEY)
8
+ # C. Revocation — needs the key to be cancelled/expired server-side first
9
+ #
10
+ # Env:
11
+ # SC_CLI command to invoke the CLI (default: "sourcecode")
12
+ # SC_SMOKE_SMALL path to a small repo (<=500 Java) (default: the repo fixture)
13
+ # SC_SMOKE_LARGE path to a large repo (>500 Java) (optional; enables Pro gate check)
14
+ # SC_SMOKE_KEY a real Pro license key (optional; enables section B/C)
15
+ #
16
+ # Exit non-zero on the first failed check in a runnable section.
17
+ set -u
18
+
19
+ CLI="${SC_CLI:-sourcecode}"
20
+ HERE="$(cd "$(dirname "$0")/.." && pwd)"
21
+ SMALL="${SC_SMOKE_SMALL:-$HERE/tests/fixtures/spring_boot_minimal}"
22
+ pass() { printf ' \033[32mPASS\033[0m %s\n' "$1"; }
23
+ fail() { printf ' \033[31mFAIL\033[0m %s\n' "$1"; exit 1; }
24
+ skip() { printf ' \033[33mSKIP\033[0m %s\n' "$1"; }
25
+
26
+ echo "== A. Install + Free (offline, no license) =="
27
+
28
+ $CLI version >/dev/null 2>&1 || fail "install: 'version' did not run"
29
+ pass "installed — 'version' runs"
30
+
31
+ $CLI config >/dev/null 2>&1 || fail "'config' did not run"
32
+ pass "'config' runs"
33
+
34
+ # A Free command on a small repo must succeed (exit 0) with no license.
35
+ if $CLI endpoints "$SMALL" >/dev/null 2>&1; then
36
+ pass "Free command 'endpoints' runs on a small repo"
37
+ else
38
+ fail "Free command 'endpoints' failed on a small repo"
39
+ fi
40
+
41
+ # auth status with no license → unauthenticated.
42
+ if $CLI auth status 2>/dev/null | grep -q '"status": "unauthenticated"\|"status":"unauthenticated"'; then
43
+ pass "'auth status' reports unauthenticated with no license"
44
+ else
45
+ skip "'auth status' not unauthenticated (a license may already be present)"
46
+ fi
47
+
48
+ echo "== B. Activation (needs SC_SMOKE_KEY + network) =="
49
+ if [ -z "${SC_SMOKE_KEY:-}" ]; then
50
+ skip "SC_SMOKE_KEY not set — skipping real activation"
51
+ else
52
+ if $CLI activate "$SC_SMOKE_KEY" 2>/dev/null | grep -q '"status": *"activated"\|"status":"activated"'; then
53
+ pass "activation succeeded"
54
+ else
55
+ fail "activation did NOT return status=activated (check key + Edge Function deploy)"
56
+ fi
57
+ if $CLI auth status 2>/dev/null | grep -q '"pro": *true'; then
58
+ pass "'auth status' reports pro=true after activation"
59
+ else
60
+ fail "'auth status' does not report pro=true after activation"
61
+ fi
62
+ if [ -n "${SC_SMOKE_LARGE:-}" ]; then
63
+ if $CLI impact --help >/dev/null 2>&1 && $CLI modernize "$SC_SMOKE_LARGE" >/dev/null 2>&1; then
64
+ pass "Pro command 'modernize' runs on a large repo while activated"
65
+ else
66
+ fail "Pro command failed on a large repo while activated"
67
+ fi
68
+ else
69
+ skip "SC_SMOKE_LARGE not set — skipping Pro-on-large-repo check"
70
+ fi
71
+ fi
72
+
73
+ echo "== C. Revocation (cancel/expire the subscription server-side FIRST) =="
74
+ if [ -z "${SC_SMOKE_KEY:-}" ] || [ -z "${SC_SMOKE_LARGE:-}" ]; then
75
+ skip "needs SC_SMOKE_KEY + SC_SMOKE_LARGE (and a revoked key) — skipping"
76
+ else
77
+ echo " (assumes the key is now cancelled/expired and the revalidation TTL has elapsed;"
78
+ echo " set SOURCECODE_SUPABASE_URL to force a fresh check or wait out the 30-min TTL)"
79
+ out="$($CLI modernize "$SC_SMOKE_LARGE" 2>&1)"; code=$?
80
+ if [ "$code" = "2" ]; then
81
+ pass "revoked license → Pro command on large repo exits 2 (access removed)"
82
+ else
83
+ fail "revoked license did NOT gate (exit=$code) — revocation path is broken"
84
+ fi
85
+ fi
86
+
87
+ echo "Smoke test complete."
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Deterministic codebase context maps for AI coding agents."""
2
2
 
3
- __version__ = "1.73.0"
3
+ __version__ = "2.0.1"
@@ -960,75 +960,83 @@ class ArchitectureAnalyzer:
960
960
  # Java annotation / base-class secondary scanner (C4)
961
961
  # ------------------------------------------------------------------
962
962
 
963
- _ANNOTATION_DECL_RE = re.compile(
964
- r"@interface\s+([A-Z][A-Za-z0-9_]+)", re.MULTILINE
965
- )
966
- _ANNOTATION_USAGE_RE = re.compile(
967
- r"@([A-Z][A-Za-z0-9_]+)(?:\s*\(([^)]*)\))?", re.MULTILINE
968
- )
969
- _EXTENDS_RE = re.compile(
970
- r"class\s+\w+\s+extends\s+([A-Z][A-Za-z0-9_]+)", re.MULTILINE
971
- )
972
- _ANNOTATION_PARAM_RE = re.compile(r"(\w+)\s*=\s*[\"']?([^,\"')]+)[\"']?")
973
- _MAX_JAVA_SCAN = 2000 # cap files to avoid runaway scanning
963
+ # Annotations never reported as "custom": Java/Spring/JPA/bean-validation
964
+ # built-ins. (Kept from the historical text scan; the counts now come from
965
+ # the ContextGraph's node-grounded `annotated_with` relations.)
966
+ _BUILTIN_ANNOTATIONS: frozenset = frozenset({
967
+ "Override", "SuppressWarnings", "Deprecated", "FunctionalInterface",
968
+ "SafeVarargs", "Retention", "Target", "Documented", "Inherited",
969
+ "RestController", "Service", "Repository", "Component", "Controller",
970
+ "Autowired", "Value", "Bean", "Configuration", "SpringBootApplication",
971
+ "EnableAutoConfiguration", "Transactional", "RequestMapping",
972
+ "GetMapping", "PostMapping", "PutMapping", "DeleteMapping",
973
+ "PathVariable", "RequestBody", "RequestParam", "ResponseBody",
974
+ "NotNull", "NotBlank", "Size", "Min", "Max", "Valid",
975
+ })
976
+ # @interface member return types the historical scan reported as parameters.
977
+ _ANNOTATION_PARAM_TYPES: frozenset = frozenset({"String", "int", "boolean", "Class"})
974
978
 
975
979
  def _scan_java_patterns(
976
980
  self,
977
981
  root: Path,
978
982
  java_paths: list[str],
979
983
  ) -> tuple[list[dict], list[dict]]:
980
- """Scan Java source files for custom @interface annotations and common base classes.
984
+ """Derive custom @interface annotations and common base classes from the
985
+ ContextGraph (the single structural access layer — no source parsing).
986
+
987
+ Counts are node-grounded: an annotation usage is an `annotated_with`
988
+ relation on a real symbol, and a subclass is an `extends` relation —
989
+ occurrences inside string literals, comments, or dead text never count
990
+ (the historical text scan could).
981
991
 
982
992
  Returns (custom_annotations, base_classes) lists for ArchitectureAnalysis.
983
993
  """
984
- from sourcecode.tree_utils import safe_read_text
994
+ from sourcecode.context_graph import ContextGraph
995
+
996
+ graph = ContextGraph.build(java_paths, root)
985
997
 
986
- annotation_decls: dict[str, dict] = {} # name → {params, files, usage_count}
987
- annotation_usage: dict[str, int] = {} # name → total usages across codebase
998
+ annotation_decls: dict[str, dict] = {} # name → {params, usage_count, purpose}
999
+ annotation_usage: dict[str, int] = {} # name → node-grounded usages
988
1000
  extends_counts: dict[str, int] = {} # base_class_name → subclass count
989
1001
 
990
- for path_str in java_paths[:self._MAX_JAVA_SCAN]:
991
- abs_path = root / path_str
992
- try:
993
- content = safe_read_text(abs_path)
994
- except OSError:
1002
+ # Custom annotation declarations: @interface nodes + their members with
1003
+ # the historically-reported parameter return types.
1004
+ for ann in graph.annotation_types():
1005
+ ann_name = ann.fqn.rsplit(".", 1)[-1]
1006
+ if ann_name in annotation_decls:
995
1007
  continue
1008
+ prefix = ann.fqn + "#"
1009
+ # Member return type from the node signature "(params)->ReturnType".
1010
+ params = [
1011
+ m.fqn[len(prefix):]
1012
+ for m in graph.symbols(kind="method")
1013
+ if m.fqn.startswith(prefix)
1014
+ and (m.signature or "").rsplit("->", 1)[-1] in self._ANNOTATION_PARAM_TYPES
1015
+ ]
1016
+ annotation_decls[ann_name] = {
1017
+ "name": ann_name,
1018
+ "parameters": params,
1019
+ "usage_count": 0,
1020
+ "purpose": "",
1021
+ }
996
1022
 
997
- # Custom annotation declarations: `public @interface FooAnnotation`
998
- for m in self._ANNOTATION_DECL_RE.finditer(content):
999
- ann_name = m.group(1)
1000
- if ann_name not in annotation_decls:
1001
- # Extract parameters from the annotation body (next ~500 chars)
1002
- body_start = m.end()
1003
- body = content[body_start:body_start + 500]
1004
- params = re.findall(r"(?:String|int|boolean|Class)\s+(\w+)\s*\(\)", body)
1005
- annotation_decls[ann_name] = {
1006
- "name": ann_name,
1007
- "parameters": params,
1008
- "usage_count": 0,
1009
- "purpose": "",
1010
- }
1011
-
1012
- # Annotation usage frequency
1013
- for m in self._ANNOTATION_USAGE_RE.finditer(content):
1014
- ann_name = m.group(1)
1015
- # Skip common Java built-ins
1016
- if ann_name in {
1017
- "Override", "SuppressWarnings", "Deprecated", "FunctionalInterface",
1018
- "SafeVarargs", "Retention", "Target", "Documented", "Inherited",
1019
- "RestController", "Service", "Repository", "Component", "Controller",
1020
- "Autowired", "Value", "Bean", "Configuration", "SpringBootApplication",
1021
- "EnableAutoConfiguration", "Transactional", "RequestMapping",
1022
- "GetMapping", "PostMapping", "PutMapping", "DeleteMapping",
1023
- "PathVariable", "RequestBody", "RequestParam", "ResponseBody",
1024
- "NotNull", "NotBlank", "Size", "Min", "Max", "Valid",
1025
- }:
1026
- continue
1027
- annotation_usage[ann_name] = annotation_usage.get(ann_name, 0) + 1
1028
-
1029
- # extends BaseClass — count subclasses per base
1030
- for m in self._EXTENDS_RE.finditer(content):
1031
- base = m.group(1)
1023
+ # Annotation usage frequency one count per annotated_with relation.
1024
+ for rel in graph.relations(kind="annotated_with"):
1025
+ ann_name = rel.target.lstrip("@").rsplit(".", 1)[-1]
1026
+ if not ann_name or not ann_name[0].isupper():
1027
+ continue
1028
+ if ann_name in self._BUILTIN_ANNOTATIONS:
1029
+ continue
1030
+ annotation_usage[ann_name] = annotation_usage.get(ann_name, 0) + 1
1031
+
1032
+ # extends BaseClass — count subclasses per base (class extends only,
1033
+ # matching the historical `class X extends Y` scan).
1034
+ class_fqns = {s.fqn for s in graph.symbols(kind="class")}
1035
+ for rel in graph.relations(kind="extends"):
1036
+ if rel.source not in class_fqns:
1037
+ continue # interface-extends-interface is not a subclass count
1038
+ base = re.sub(r"<.*", "", rel.target).rsplit(".", 1)[-1].strip()
1039
+ if base and base[0].isupper():
1032
1040
  extends_counts[base] = extends_counts.get(base, 0) + 1
1033
1041
 
1034
1042
  # Merge usage counts into annotation_decls