sourcecode 0.22.0__py3-none-any.whl → 0.23.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/__init__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  """sourcecode — Genera mapas de contexto estructurado para agentes IA."""
2
2
 
3
- __version__ = "0.22.0"
3
+ __version__ = "0.23.0"
sourcecode/cli.py CHANGED
@@ -623,6 +623,12 @@ def main(
623
623
  for _finding in validate_cross_analyzer_consistency(sm, strict=False):
624
624
  typer.echo(f"[consistency] {_finding}", err=True)
625
625
 
626
+ # Build confidence summary + analysis gaps (always runs, lightweight)
627
+ from sourcecode.confidence_analyzer import ConfidenceAnalyzer
628
+ from dataclasses import replace as _replace
629
+ _conf_summary, _analysis_gaps = ConfidenceAnalyzer().analyze(sm)
630
+ sm = _replace(sm, confidence_summary=_conf_summary, analysis_gaps=_analysis_gaps)
631
+
626
632
  # 4. Serializar
627
633
  if agent:
628
634
  data = agent_view(sm)
@@ -666,13 +672,18 @@ def main(
666
672
  def prepare_context_cmd(
667
673
  task: Optional[str] = typer.Argument(
668
674
  None,
669
- help="Task: explain | fix-bug | refactor | generate-tests",
675
+ help="Task: explain | fix-bug | refactor | generate-tests | onboard | review-pr | delta",
670
676
  ),
671
677
  path: Path = typer.Option(
672
678
  Path("."),
673
679
  "--path", "-p",
674
680
  help="Project directory to analyze (default: current directory)",
675
681
  ),
682
+ since: Optional[str] = typer.Option(
683
+ None,
684
+ "--since",
685
+ help="Git ref for delta task: show files changed since this ref (e.g. HEAD~3, main)",
686
+ ),
676
687
  llm_prompt: bool = typer.Option(
677
688
  False,
678
689
  "--llm-prompt",
@@ -689,13 +700,24 @@ def prepare_context_cmd(
689
700
  help="Show what would be analyzed without running it",
690
701
  ),
691
702
  ) -> None:
692
- """Prepare task-aware context optimized for LLM reasoning.
703
+ """Compile task-aware context for AI coding agents.
704
+
705
+ \b
706
+ Tasks:
707
+ explain Project overview: structure, entry points, dependencies
708
+ fix-bug Risk-ranked files, suspected areas, code annotations
709
+ refactor Structural issues, improvement opportunities
710
+ generate-tests Untested source files, test gap analysis
711
+ onboard Full project context for a new agent or developer
712
+ review-pr PR review context: changed files + architecture
713
+ delta Incremental context: git-changed files only
693
714
 
694
715
  \b
695
- Note: PATH must be provided before the subcommand (default: '.'):
716
+ Examples:
696
717
  sourcecode . prepare-context explain
697
718
  sourcecode . prepare-context fix-bug --path /my/project
698
- sourcecode . prepare-context generate-tests --llm-prompt
719
+ sourcecode . prepare-context delta --since main
720
+ sourcecode . prepare-context onboard --llm-prompt
699
721
  sourcecode . prepare-context --task-help
700
722
  """
701
723
  from sourcecode.prepare_context import TASKS, TaskContextBuilder
@@ -734,22 +756,28 @@ def prepare_context_cmd(
734
756
  typer.echo(f"path: {target}")
735
757
  typer.echo(f"analyzers: dependencies={'yes' if spec.enable_dependencies else 'no'}"
736
758
  f", code_notes={'yes' if spec.enable_code_notes else 'no'}")
759
+ if since:
760
+ typer.echo(f"since: {since}")
737
761
  typer.echo(f"output: {spec.output_hint}")
738
762
  raise typer.Exit()
739
763
 
740
764
  from dataclasses import asdict
741
765
 
742
766
  builder = TaskContextBuilder(target)
743
- output = builder.build(task)
767
+ output = builder.build(task, since=since)
744
768
 
745
769
  out: dict[str, Any] = {
746
770
  "task": output.task,
747
771
  "goal": output.goal,
748
772
  "project_summary": output.project_summary,
749
773
  "architecture_summary": output.architecture_summary,
774
+ "confidence": output.confidence,
750
775
  "relevant_files": [asdict(f) for f in output.relevant_files],
776
+ "why_these_files": output.why_these_files,
751
777
  "key_dependencies": output.key_dependencies,
752
778
  }
779
+ if output.gaps:
780
+ out["gaps"] = output.gaps
753
781
  if output.suspected_areas:
754
782
  out["suspected_areas"] = output.suspected_areas
755
783
  if output.improvement_opportunities:
@@ -758,6 +786,10 @@ def prepare_context_cmd(
758
786
  out["test_gaps"] = output.test_gaps
759
787
  if output.code_notes_summary:
760
788
  out["code_notes_summary"] = output.code_notes_summary
789
+ if output.changed_files:
790
+ out["changed_files"] = output.changed_files
791
+ if output.affected_entry_points:
792
+ out["affected_entry_points"] = output.affected_entry_points
761
793
  if output.limitations:
762
794
  out["limitations"] = output.limitations
763
795
  if llm_prompt:
@@ -0,0 +1,190 @@
1
+ """confidence_analyzer.py — Builds ConfidenceSummary and AnalysisGap list from SourceMap.
2
+
3
+ Analyzes detection quality post-facto:
4
+ - Classifies signals as hard (manifest/lockfile) vs soft (heuristic/extension)
5
+ - Identifies auxiliary paths that were found but correctly ignored
6
+ - Detects anomalies (conflicting signals, low-confidence detections)
7
+ - Produces structured analysis gaps for agent consumption
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ from pathlib import Path
13
+ from typing import TYPE_CHECKING
14
+
15
+ from sourcecode.schema import AnalysisGap, ConfidenceSummary, SourceMap
16
+
17
+ if TYPE_CHECKING:
18
+ pass
19
+
20
+ _AUXILIARY_DIR_PREFIXES = (
21
+ ".claude/", ".cursor/", ".vscode/", ".github/", ".idea/",
22
+ ".devcontainer/", ".husky/",
23
+ )
24
+
25
+ _FIXTURE_DIR_SEGMENTS = {"fixtures", "fixture", "testdata", "test_data", "__fixtures__"}
26
+ _TEST_DIR_SEGMENTS = {"tests", "test", "spec", "specs", "__tests__"}
27
+ _DOC_DIR_SEGMENTS = {"docs", "doc", "documentation", "wiki"}
28
+ _GENERATED_DIR_SEGMENTS = {"dist", "build", "target", "out", "output", ".next", "__pycache__"}
29
+
30
+ _HARD_SOURCES = {"manifest", "lockfile", "pyproject.toml", "package.json", "go.mod",
31
+ "Cargo.toml", "pom.xml", "build.gradle"}
32
+ _SOFT_SOURCES = {"heuristic", "code_signal", "convention"}
33
+
34
+
35
+ class ConfidenceAnalyzer:
36
+ """Analyzes SourceMap quality and produces confidence + gap metadata."""
37
+
38
+ def analyze(self, sm: SourceMap) -> tuple[ConfidenceSummary, list[AnalysisGap]]:
39
+ hard_signals: list[str] = []
40
+ soft_signals: list[str] = []
41
+ ignored_signals: list[str] = []
42
+ anomalies: list[str] = []
43
+ gaps: list[AnalysisGap] = []
44
+
45
+ # ── Stack signals ─────────────────────────────────────────────────────
46
+ for stack in sm.stacks:
47
+ if stack.detection_method == "manifest" and stack.confidence in ("high", "medium"):
48
+ for manifest in stack.manifests:
49
+ sig = f"stack:{stack.stack} via {manifest}"
50
+ if sig not in hard_signals:
51
+ hard_signals.append(sig)
52
+ elif stack.detection_method == "heuristic":
53
+ sig = f"stack:{stack.stack} (heuristic, no manifest)"
54
+ if sig not in soft_signals:
55
+ soft_signals.append(sig)
56
+ elif stack.detection_method == "lockfile":
57
+ sig = f"stack:{stack.stack} via lockfile"
58
+ if sig not in hard_signals:
59
+ hard_signals.append(sig)
60
+
61
+ # ── Entry point signals ───────────────────────────────────────────────
62
+ for ep in sm.entry_points:
63
+ if ep.source in _HARD_SOURCES or ep.reason == "console_script":
64
+ sig = f"entry:{ep.path} ({ep.reason or ep.source})"
65
+ if sig not in hard_signals:
66
+ hard_signals.append(sig)
67
+ else:
68
+ sig = f"entry:{ep.path} ({ep.reason or ep.source})"
69
+ if sig not in soft_signals:
70
+ soft_signals.append(sig)
71
+
72
+ # ── Ignored auxiliary paths ───────────────────────────────────────────
73
+ aux_dirs_found: set[str] = set()
74
+ for path in sm.file_paths:
75
+ norm = path.replace("\\", "/")
76
+ for prefix in _AUXILIARY_DIR_PREFIXES:
77
+ if norm.startswith(prefix):
78
+ top = prefix.rstrip("/")
79
+ aux_dirs_found.add(top)
80
+ break
81
+
82
+ for aux in sorted(aux_dirs_found):
83
+ ignored_signals.append(f"aux_dir:{aux} (tooling, not analyzed as project source)")
84
+
85
+ # ── Anomaly: multiple stacks, ambiguous primary ───────────────────────
86
+ primary_stacks = [s for s in sm.stacks if s.primary]
87
+ heuristic_only = [s for s in sm.stacks if s.detection_method == "heuristic"]
88
+
89
+ if len(primary_stacks) == 0 and sm.stacks:
90
+ anomalies.append("No primary stack marked — multiple stacks detected with equal weight")
91
+ if len(primary_stacks) > 1:
92
+ names = ", ".join(s.stack for s in primary_stacks)
93
+ anomalies.append(f"Multiple stacks marked as primary: {names}")
94
+ if heuristic_only and not any(s.detection_method != "heuristic" for s in sm.stacks):
95
+ anomalies.append("All stacks detected via heuristic only — no manifest found")
96
+
97
+ # ── Anomaly: entry points all low-confidence ──────────────────────────
98
+ if sm.entry_points and all(ep.confidence == "low" for ep in sm.entry_points):
99
+ anomalies.append("All entry points are low-confidence (heuristic/code_signal only)")
100
+
101
+ # ── Gaps ──────────────────────────────────────────────────────────────
102
+ if not sm.entry_points:
103
+ gaps.append(AnalysisGap(
104
+ area="entry_points",
105
+ reason="No entry point detected — project may use non-standard structure or be a library",
106
+ impact="high",
107
+ ))
108
+ elif all(ep.confidence == "low" for ep in sm.entry_points):
109
+ gaps.append(AnalysisGap(
110
+ area="entry_points",
111
+ reason="Entry points inferred from code patterns only, no manifest declaration found",
112
+ impact="medium",
113
+ ))
114
+
115
+ if not sm.stacks:
116
+ gaps.append(AnalysisGap(
117
+ area="stack",
118
+ reason="No stack detected — project may be infrastructure-only or use an unsupported language",
119
+ impact="high",
120
+ ))
121
+ elif all(s.detection_method == "heuristic" for s in sm.stacks):
122
+ gaps.append(AnalysisGap(
123
+ area="stack",
124
+ reason="Stack inferred from file extensions only — no manifest or lockfile found",
125
+ impact="medium",
126
+ ))
127
+
128
+ dep_summary = sm.dependency_summary
129
+ if dep_summary is None or not dep_summary.requested:
130
+ gaps.append(AnalysisGap(
131
+ area="dependencies",
132
+ reason="Dependencies not analyzed — run with --dependencies for full context",
133
+ impact="medium",
134
+ ))
135
+ elif dep_summary.requested and dep_summary.total_count == 0:
136
+ gaps.append(AnalysisGap(
137
+ area="dependencies",
138
+ reason="No dependencies found — project may have no external dependencies or manifest is non-standard",
139
+ impact="low",
140
+ ))
141
+
142
+ env_summary = sm.env_summary
143
+ if env_summary is None or not env_summary.requested:
144
+ gaps.append(AnalysisGap(
145
+ area="env",
146
+ reason="Environment variables not analyzed — run with --env-map for operational context",
147
+ impact="low",
148
+ ))
149
+
150
+ # ── Compute overall confidence ─────────────────────────────────────────
151
+ # Stack: use best manifest-detected stack, fall back to min
152
+ manifest_stacks = [s for s in sm.stacks if s.detection_method != "heuristic"]
153
+ stack_conf = (
154
+ _max_confidence([s.confidence for s in manifest_stacks])
155
+ if manifest_stacks
156
+ else _min_confidence([s.confidence for s in sm.stacks] or ["low"])
157
+ )
158
+ # Entry points: use best available (highest-confidence EP wins)
159
+ ep_conf = _max_confidence([ep.confidence for ep in sm.entry_points] or ["low"])
160
+ overall = _min_confidence([stack_conf, ep_conf])
161
+
162
+ # Downgrade if gaps are severe
163
+ high_impact_gaps = [g for g in gaps if g.impact == "high"]
164
+ if high_impact_gaps:
165
+ overall = "low" if overall != "high" else "medium"
166
+
167
+ summary = ConfidenceSummary(
168
+ overall=overall, # type: ignore[arg-type]
169
+ stack_confidence=stack_conf, # type: ignore[arg-type]
170
+ entry_point_confidence=ep_conf, # type: ignore[arg-type]
171
+ hard_signals=hard_signals,
172
+ soft_signals=soft_signals,
173
+ ignored_signals=ignored_signals,
174
+ anomalies=anomalies,
175
+ )
176
+ return summary, gaps
177
+
178
+
179
+ def _min_confidence(values: list[str]) -> str:
180
+ rank = {"high": 2, "medium": 1, "low": 0}
181
+ if not values:
182
+ return "low"
183
+ return min(values, key=lambda v: rank.get(v, 0))
184
+
185
+
186
+ def _max_confidence(values: list[str]) -> str:
187
+ rank = {"high": 2, "medium": 1, "low": 0}
188
+ if not values:
189
+ return "low"
190
+ return max(values, key=lambda v: rank.get(v, 0))
@@ -32,6 +32,22 @@ _PY_PARSING: frozenset[str] = frozenset({
32
32
  "pathspec", "gitpython", "lxml", "beautifulsoup4", "html5lib",
33
33
  "regex", "pyparsing",
34
34
  })
35
+ _PY_BUILDTOOLS: frozenset[str] = frozenset({
36
+ "setuptools", "hatchling", "flit-core", "flit", "wheel", "build", "twine",
37
+ "hatch", "poetry-core", "maturin", "cython",
38
+ })
39
+ _PY_OBSERVABILITY: frozenset[str] = frozenset({
40
+ "sentry-sdk", "datadog", "prometheus-client", "opentelemetry-api",
41
+ "opentelemetry-sdk", "structlog", "loguru", "elastic-apm", "ddtrace",
42
+ "opentelemetry-instrumentation",
43
+ })
44
+ _PY_INFRA: frozenset[str] = frozenset({
45
+ "boto3", "botocore", "azure-storage-blob", "azure-identity",
46
+ "google-cloud-storage", "google-cloud-bigquery", "kubernetes",
47
+ "paramiko", "fabric", "celery", "dramatiq", "redis", "aioredis",
48
+ "psycopg2", "psycopg2-binary", "asyncpg", "sqlalchemy", "motor",
49
+ "pymongo", "aiomysql", "mysql-connector-python",
50
+ })
35
51
 
36
52
  _NODE_TESTTOOLS: frozenset[str] = frozenset({
37
53
  "jest", "@jest/globals", "mocha", "jasmine", "chai", "karma",
@@ -39,8 +55,22 @@ _NODE_TESTTOOLS: frozenset[str] = frozenset({
39
55
  "sinon", "nock", "supertest",
40
56
  })
41
57
  _NODE_DEVTOOLS: frozenset[str] = frozenset({
42
- "eslint", "prettier", "typescript", "@types/node", "webpack", "rollup",
43
- "parcel", "@babel/core", "husky", "lint-staged", "nodemon", "ts-node", "tsx",
58
+ "eslint", "prettier", "typescript", "@types/node", "husky",
59
+ "lint-staged", "nodemon", "ts-node", "tsx",
60
+ })
61
+ _NODE_BUILDTOOLS: frozenset[str] = frozenset({
62
+ "webpack", "webpack-cli", "rollup", "parcel", "@babel/core", "@babel/preset-env",
63
+ "esbuild", "@swc/core", "vite", "turbopack", "postcss", "autoprefixer",
64
+ "tailwindcss", "sass", "less",
65
+ })
66
+ _NODE_OBSERVABILITY: frozenset[str] = frozenset({
67
+ "@sentry/node", "dd-trace", "pino", "winston", "morgan",
68
+ "@opentelemetry/sdk-node", "pino-http",
69
+ })
70
+ _NODE_INFRA: frozenset[str] = frozenset({
71
+ "aws-sdk", "@aws-sdk/client-s3", "azure-storage", "firebase-admin",
72
+ "bull", "bullmq", "amqplib", "kafkajs", "ioredis", "pg", "mysql2",
73
+ "mongoose", "prisma", "@prisma/client", "typeorm", "sequelize",
44
74
  })
45
75
 
46
76
  _DEV_SCOPES: frozenset[str] = frozenset({"dev", "optional"})
@@ -49,13 +79,17 @@ _ROLE_PRIORITY: dict[str, int] = {
49
79
  "runtime": 0,
50
80
  "parsing": 1,
51
81
  "serialization": 2,
52
- "testtool": 3,
53
- "devtool": 4,
82
+ "observability": 3,
83
+ "infra": 4,
84
+ "buildtool": 5,
85
+ "testtool": 6,
86
+ "devtool": 7,
87
+ "unknown": 8,
54
88
  }
55
89
 
56
90
 
57
91
  def _infer_role(name: str, ecosystem: str, scope: str) -> str:
58
- """Infer dependency role: runtime | parsing | serialization | devtool | testtool."""
92
+ """Infer dependency role: runtime | parsing | serialization | buildtool | observability | infra | devtool | testtool | unknown."""
59
93
  n = name.lower()
60
94
  is_dev = scope in _DEV_SCOPES or scope.startswith(("optional:", "group:"))
61
95
 
@@ -64,8 +98,14 @@ def _infer_role(name: str, ecosystem: str, scope: str) -> str:
64
98
  return "testtool"
65
99
  if n in _PY_DEVTOOLS:
66
100
  return "devtool"
101
+ if n in _PY_BUILDTOOLS:
102
+ return "buildtool"
67
103
  if is_dev:
68
104
  return "devtool"
105
+ if n in _PY_OBSERVABILITY:
106
+ return "observability"
107
+ if n in _PY_INFRA:
108
+ return "infra"
69
109
  if n in _PY_SERIALIZATION:
70
110
  return "serialization"
71
111
  if n in _PY_PARSING:
@@ -77,8 +117,14 @@ def _infer_role(name: str, ecosystem: str, scope: str) -> str:
77
117
  return "testtool"
78
118
  if n in _NODE_DEVTOOLS:
79
119
  return "devtool"
120
+ if n in _NODE_BUILDTOOLS:
121
+ return "buildtool"
80
122
  if is_dev:
81
123
  return "devtool"
124
+ if n in _NODE_OBSERVABILITY:
125
+ return "observability"
126
+ if n in _NODE_INFRA:
127
+ return "infra"
82
128
  return "runtime"
83
129
 
84
130
  return "devtool" if is_dev else "runtime"
@@ -66,6 +66,14 @@ class HeuristicDetector(AbstractDetector):
66
66
  if filename in _ENTRYPOINT_NAMES:
67
67
  stack, kind = _ENTRYPOINT_NAMES[filename]
68
68
  entry_points.append(
69
- EntryPoint(path=path, stack=stack, kind=kind, source="heuristic")
69
+ EntryPoint(
70
+ path=path,
71
+ stack=stack,
72
+ kind=kind,
73
+ source="heuristic",
74
+ confidence="low",
75
+ reason="entry_file_pattern",
76
+ evidence=f"conventional filename: {filename}",
77
+ )
70
78
  )
71
79
  return stacks, entry_points
@@ -162,6 +162,8 @@ class PythonDetector(AbstractDetector):
162
162
  kind=kind,
163
163
  source="pyproject.toml",
164
164
  confidence="high",
165
+ reason="console_script",
166
+ evidence="declared in [project.scripts]",
165
167
  )
166
168
  )
167
169
 
@@ -180,6 +182,8 @@ class PythonDetector(AbstractDetector):
180
182
  kind=kind,
181
183
  source="convention",
182
184
  confidence="medium",
185
+ reason="entry_file_pattern",
186
+ evidence=f"conventional filename: {fname}",
183
187
  )
184
188
  )
185
189
 
@@ -195,6 +199,8 @@ class PythonDetector(AbstractDetector):
195
199
  kind="script",
196
200
  source="code_signal",
197
201
  confidence="low",
202
+ reason="main_guard",
203
+ evidence="if __name__ == '__main__' guard detected",
198
204
  )
199
205
  )
200
206
 
@@ -1,8 +1,19 @@
1
- """prepare_context.py — Task-aware context optimizer for LLM reasoning."""
1
+ """prepare_context.py — Task-aware context compiler for AI coding agents.
2
+
3
+ Each task produces a focused context bundle:
4
+ - goal: what the agent should accomplish
5
+ - project_summary: value-oriented product description
6
+ - architecture_summary: flow description (entry → processing → output)
7
+ - relevant_files: ranked by relevance with why_these_files rationale
8
+ - key_dependencies: runtime-first, role-tagged
9
+ - confidence: detection quality indicator
10
+ - gaps: what's uncertain or not analyzed
11
+ - llm_prompt: ready-to-use prompt (optional, --llm-prompt)
12
+ """
2
13
 
3
14
  from __future__ import annotations
4
15
 
5
- from dataclasses import dataclass
16
+ from dataclasses import dataclass, field
6
17
  from pathlib import Path
7
18
  from typing import Any, Optional
8
19
 
@@ -105,6 +116,77 @@ untested or undertested areas of this codebase.
105
116
  4. Each test must have a clear name that describes exactly what it verifies.
106
117
  """
107
118
 
119
+ _ONBOARD_PROMPT = """\
120
+ You are an expert software engineer onboarding to a new codebase. \
121
+ Your task is to understand and explain this project thoroughly.
122
+
123
+ ## Project
124
+
125
+ {project_summary}
126
+
127
+ {architecture_section}
128
+
129
+ ## Entry Points
130
+
131
+ {relevant_files_section}
132
+
133
+ {dependencies_section}
134
+
135
+ ## Instructions
136
+
137
+ 1. Describe what this project does and the problem it solves.
138
+ 2. Walk through the main execution flow from entry point to output.
139
+ 3. Identify the 3-5 most important files to understand first.
140
+ 4. Note any non-obvious conventions, constraints, or design decisions.
141
+ 5. List what you would need to know to safely modify this codebase.
142
+ """
143
+
144
+ _REVIEW_PR_PROMPT = """\
145
+ You are an expert code reviewer. Your task is to review the changes \
146
+ in this pull request within the context of the full project.
147
+
148
+ ## Project Context
149
+
150
+ {project_summary}
151
+
152
+ {architecture_section}
153
+
154
+ ## Changed Files
155
+
156
+ {relevant_files_section}
157
+
158
+ {suspected_areas_section}
159
+
160
+ ## Instructions
161
+
162
+ 1. Review the changed files for correctness, security, and maintainability.
163
+ 2. Check that changes are consistent with the project's architecture.
164
+ 3. Identify any missing tests, edge cases, or error handling.
165
+ 4. Flag any breaking changes to public APIs or contracts.
166
+ 5. Suggest concrete improvements with specific file and line references.
167
+ """
168
+
169
+ _DELTA_PROMPT = """\
170
+ You are an expert software engineer reviewing incremental changes to a codebase.
171
+
172
+ ## Project Context
173
+
174
+ {project_summary}
175
+
176
+ ## Changed Files
177
+
178
+ {relevant_files_section}
179
+
180
+ {suspected_areas_section}
181
+
182
+ ## Instructions
183
+
184
+ 1. Analyze the changed files and their relationship to the project architecture.
185
+ 2. Identify which entry points and components are affected.
186
+ 3. Assess the risk and impact of these changes.
187
+ 4. Flag any consistency issues with the rest of the codebase.
188
+ """
189
+
108
190
 
109
191
  # ─────────────────────────────────────────────────────────────────────────────
110
192
  # Task registry
@@ -170,6 +252,41 @@ TASKS: dict[str, TaskSpec] = {
170
252
  prompt_template=_GENERATE_TESTS_PROMPT,
171
253
  output_hint="test_gaps, relevant_files (source without tests), key_dependencies",
172
254
  ),
255
+ "onboard": TaskSpec(
256
+ name="onboard",
257
+ goal="Build complete project understanding for a new agent or developer joining the codebase.",
258
+ description="Full structural context: entry points, architecture, key files, dependencies.",
259
+ ranking_boosts=["main", "cli", "app", "core", "index", "schema", "config", "readme"],
260
+ ranking_penalties=[".min.", "__pycache__"],
261
+ enable_code_notes=False,
262
+ enable_dependencies=True,
263
+ prompt_template=_ONBOARD_PROMPT,
264
+ output_hint="project_summary, architecture_summary, relevant_files, key_dependencies, confidence, gaps",
265
+ ),
266
+ "review-pr": TaskSpec(
267
+ name="review-pr",
268
+ goal="Review pull request changes in the context of the full project architecture.",
269
+ description="Surface changed files, potential regressions, and architectural consistency.",
270
+ ranking_boosts=["handler", "service", "middleware", "router", "controller",
271
+ "api", "schema", "model", "validator"],
272
+ ranking_penalties=[".min.", "__pycache__"],
273
+ enable_code_notes=True,
274
+ enable_dependencies=False,
275
+ prompt_template=_REVIEW_PR_PROMPT,
276
+ output_hint="relevant_files, suspected_areas, architecture_summary, code_notes_summary",
277
+ ),
278
+ "delta": TaskSpec(
279
+ name="delta",
280
+ goal="Produce incremental context for changed files — avoids re-reading the full repo.",
281
+ description="Git-aware context: changed files, affected entry points, dependency impact.",
282
+ ranking_boosts=["handler", "service", "middleware", "router", "controller",
283
+ "api", "schema", "model"],
284
+ ranking_penalties=[".min.", "__pycache__"],
285
+ enable_code_notes=True,
286
+ enable_dependencies=False,
287
+ prompt_template=_DELTA_PROMPT,
288
+ output_hint="changed_files, affected_entry_points, dependency_impact, architecture_summary",
289
+ ),
173
290
  }
174
291
 
175
292
 
@@ -183,6 +300,7 @@ class RelevantFile:
183
300
  role: str # entrypoint | source | test
184
301
  score: float
185
302
  reason: str
303
+ why: str = "" # why this file matters for the specific task
186
304
 
187
305
 
188
306
  @dataclass
@@ -198,6 +316,11 @@ class TaskOutput:
198
316
  key_dependencies: list[dict[str, Any]]
199
317
  code_notes_summary: Optional[dict[str, Any]]
200
318
  limitations: list[str]
319
+ confidence: str = "medium" # overall detection confidence
320
+ gaps: list[str] = field(default_factory=list) # analysis gaps
321
+ why_these_files: dict[str, str] = field(default_factory=dict) # path → why relevant
322
+ changed_files: list[str] = field(default_factory=list) # delta task only
323
+ affected_entry_points: list[str] = field(default_factory=list) # delta task only
201
324
 
202
325
 
203
326
  # ─────────────────────────────────────────────────────────────────────────────
@@ -218,7 +341,7 @@ class TaskContextBuilder:
218
341
  def __init__(self, root: Path) -> None:
219
342
  self.root = root
220
343
 
221
- def build(self, task_name: str) -> TaskOutput:
344
+ def build(self, task_name: str, *, since: Optional[str] = None) -> TaskOutput:
222
345
  if task_name not in TASKS:
223
346
  raise ValueError(
224
347
  f"Unknown task '{task_name}'. Available: {', '.join(TASKS)}"
@@ -334,6 +457,42 @@ class TaskContextBuilder:
334
457
  untested.sort(key=lambda p: (len(p.split("/")), p))
335
458
  test_gaps = untested[:15]
336
459
 
460
+ # ── 8. Confidence + gaps ──────────────────────────────────────────────
461
+ from sourcecode.confidence_analyzer import ConfidenceAnalyzer
462
+ from dataclasses import asdict as _asdict
463
+
464
+ sm_for_conf = SourceMap(
465
+ metadata=AnalysisMetadata(analyzed_path=str(self.root)),
466
+ file_tree=file_tree,
467
+ stacks=stacks,
468
+ project_type=project_type,
469
+ entry_points=entry_points,
470
+ )
471
+ sm_for_conf.file_paths = all_paths
472
+ if spec.enable_dependencies and key_dependencies:
473
+ from sourcecode.schema import DependencySummary
474
+ sm_for_conf.dependency_summary = DependencySummary(
475
+ requested=True,
476
+ total_count=len(key_dependencies),
477
+ )
478
+
479
+ conf_summary, analysis_gaps = ConfidenceAnalyzer().analyze(sm_for_conf)
480
+ confidence = conf_summary.overall
481
+ gaps = [g.reason for g in analysis_gaps]
482
+
483
+ # ── 9. why_these_files ────────────────────────────────────────────────
484
+ why_these_files: dict[str, str] = {
485
+ rf.path: rf.reason for rf in relevant_files
486
+ }
487
+
488
+ # ── 10. Delta: git changed files ──────────────────────────────────────
489
+ changed_files: list[str] = []
490
+ affected_entry_points: list[str] = []
491
+ if task_name == "delta":
492
+ changed_files = self._get_git_changed_files(since=since)
493
+ ep_set = {ep.path for ep in entry_points}
494
+ affected_entry_points = [f for f in changed_files if f in ep_set]
495
+
337
496
  return TaskOutput(
338
497
  task=task_name,
339
498
  goal=spec.goal,
@@ -346,6 +505,11 @@ class TaskContextBuilder:
346
505
  key_dependencies=key_dependencies,
347
506
  code_notes_summary=code_notes_summary,
348
507
  limitations=limitations,
508
+ confidence=confidence,
509
+ gaps=gaps,
510
+ why_these_files=why_these_files,
511
+ changed_files=changed_files,
512
+ affected_entry_points=affected_entry_points,
349
513
  )
350
514
 
351
515
  def render_prompt(self, output: TaskOutput) -> str:
@@ -398,16 +562,21 @@ class TaskContextBuilder:
398
562
  "\n".join(f"- `{p}`" for p in output.test_gaps),
399
563
  )
400
564
 
401
- return spec.prompt_template.format(
402
- project_summary=project_summary,
403
- architecture_section=architecture_section,
404
- relevant_files_section=relevant_files_section,
405
- dependencies_section=dependencies_section,
406
- suspected_areas_section=suspected_areas_section,
407
- code_notes_section=code_notes_section,
408
- improvement_opportunities_section=improvement_opportunities_section,
409
- test_gaps_section=test_gaps_section,
410
- ).strip()
565
+ format_kwargs: dict[str, str] = {
566
+ "project_summary": project_summary,
567
+ "architecture_section": architecture_section,
568
+ "relevant_files_section": relevant_files_section,
569
+ "dependencies_section": dependencies_section,
570
+ "suspected_areas_section": suspected_areas_section,
571
+ "code_notes_section": code_notes_section,
572
+ "improvement_opportunities_section": improvement_opportunities_section,
573
+ "test_gaps_section": test_gaps_section,
574
+ }
575
+ # Only pass keys that the template actually uses
576
+ import re as _re
577
+ used_keys = set(_re.findall(r"\{(\w+)\}", spec.prompt_template))
578
+ filtered_kwargs = {k: v for k, v in format_kwargs.items() if k in used_keys}
579
+ return spec.prompt_template.format(**filtered_kwargs).strip()
411
580
 
412
581
  # ── Helpers ───────────────────────────────────────────────────────────────
413
582
 
@@ -484,3 +653,37 @@ class TaskContextBuilder:
484
653
 
485
654
  def _is_source(self, path: str) -> bool:
486
655
  return Path(path).suffix.lower() in _SOURCE_EXTENSIONS
656
+
657
+ def _get_git_changed_files(self, since: Optional[str] = None) -> list[str]:
658
+ """Get files changed since a git ref (default: HEAD~1) relative to root."""
659
+ import subprocess
660
+ ref = since or "HEAD~1"
661
+ try:
662
+ result = subprocess.run(
663
+ ["git", "diff", "--name-only", ref, "HEAD"],
664
+ cwd=str(self.root),
665
+ capture_output=True,
666
+ text=True,
667
+ timeout=10,
668
+ )
669
+ if result.returncode == 0:
670
+ return [
671
+ line.strip() for line in result.stdout.splitlines()
672
+ if line.strip()
673
+ ]
674
+ except (subprocess.TimeoutExpired, FileNotFoundError):
675
+ pass
676
+ # Fallback: uncommitted changes
677
+ try:
678
+ result = subprocess.run(
679
+ ["git", "diff", "--name-only"],
680
+ cwd=str(self.root),
681
+ capture_output=True,
682
+ text=True,
683
+ timeout=10,
684
+ )
685
+ if result.returncode == 0:
686
+ return [line.strip() for line in result.stdout.splitlines() if line.strip()]
687
+ except (subprocess.TimeoutExpired, FileNotFoundError):
688
+ pass
689
+ return []
sourcecode/schema.py CHANGED
@@ -68,6 +68,8 @@ class EntryPoint:
68
68
  kind: str = "entry"
69
69
  source: str = "manifest"
70
70
  confidence: Literal["high", "medium", "low"] = "high"
71
+ reason: Optional[str] = None # console_script | entry_file_pattern | main_guard | typer_app | heuristic | convention
72
+ evidence: Optional[str] = None # brief evidence string
71
73
 
72
74
 
73
75
  @dataclass
@@ -83,7 +85,7 @@ class DependencyRecord:
83
85
  parent: Optional[str] = None
84
86
  manifest_path: Optional[str] = None
85
87
  workspace: Optional[str] = None
86
- role: Optional[str] = None # runtime | parsing | serialization | devtool | testtool
88
+ role: Optional[str] = None # runtime | parsing | serialization | buildtool | observability | infra | devtool | testtool | unknown
87
89
 
88
90
 
89
91
  @dataclass
@@ -409,6 +411,30 @@ class CodeNotesSummary:
409
411
  limitations: list[str] = field(default_factory=list)
410
412
 
411
413
 
414
+ # --- Confidence & Explainability ---
415
+
416
+ @dataclass
417
+ class ConfidenceSummary:
418
+ """Resumen de confianza y calidad del analisis."""
419
+
420
+ overall: Literal["high", "medium", "low"] = "medium"
421
+ stack_confidence: Literal["high", "medium", "low"] = "medium"
422
+ entry_point_confidence: Literal["high", "medium", "low"] = "medium"
423
+ hard_signals: list[str] = field(default_factory=list) # manifest, lockfile, real entrypoint
424
+ soft_signals: list[str] = field(default_factory=list) # heuristic, extension-based
425
+ ignored_signals: list[str] = field(default_factory=list) # tooling dirs, aux manifests
426
+ anomalies: list[str] = field(default_factory=list)
427
+
428
+
429
+ @dataclass
430
+ class AnalysisGap:
431
+ """Gap o incertidumbre detectada en el analisis."""
432
+
433
+ area: str # entry_points | dependencies | stack | architecture | env
434
+ reason: str
435
+ impact: Literal["high", "medium", "low"] = "medium"
436
+
437
+
412
438
  # --- Git Context ---
413
439
 
414
440
  @dataclass
@@ -501,3 +527,6 @@ class SourceMap:
501
527
  code_notes: list[CodeNote] = field(default_factory=list)
502
528
  code_adrs: list[AdrRecord] = field(default_factory=list)
503
529
  code_notes_summary: Optional[CodeNotesSummary] = None
530
+ # Confidence & Explainability (v0.23.0)
531
+ confidence_summary: Optional[ConfidenceSummary] = None
532
+ analysis_gaps: list[AnalysisGap] = field(default_factory=list)
sourcecode/serializer.py CHANGED
@@ -55,30 +55,27 @@ def to_yaml(sm: SourceMap) -> str:
55
55
 
56
56
 
57
57
  def compact_view(sm: SourceMap, *, no_tree: bool = False) -> dict[str, Any]:
58
- """Proyeccion compacta del SourceMap (~500-700 tokens).
58
+ """Context package ready for prompt or handoff (~600-800 tokens).
59
59
 
60
- Incluye: schema_version, project_type, stacks, entry_points,
61
- project_summary (siempre), architecture_summary (siempre),
62
- dependency_summary + key_dependencies (cuando requested=True),
63
- file_tree_depth1 (omitido si no_tree=True).
60
+ Answers: what it is, where it enters, what depends on what,
61
+ what signals matter, and what uncertainty exists.
64
62
 
65
- Excluye: dependencies (lista larga), docs, module_graph.
66
- """
67
- depth1: dict[str, Any] | None = None
68
- if not no_tree:
69
- depth1 = {}
70
- for name, value in sm.file_tree.items():
71
- if isinstance(value, dict):
72
- depth1[name] = {}
73
- else:
74
- depth1[name] = None
63
+ Includes: project_type, project_summary, architecture_summary,
64
+ stacks, entry_points, dependency_summary + key_dependencies (when analyzed),
65
+ env_summary (when analyzed), code_notes_summary (when analyzed),
66
+ confidence_summary, anomalies, analysis_gaps.
75
67
 
68
+ Excludes: file_tree, raw dependency lists, docs, module_graph.
69
+ Empty sections are explained when relevant.
70
+ """
76
71
  dep_summary_dict: Any = None
77
72
  key_deps: Any = None
78
73
  if sm.dependency_summary is not None and sm.dependency_summary.requested:
79
74
  dep_summary_dict = asdict(sm.dependency_summary)
80
75
  dep_summary_dict.pop("dependencies", None)
81
76
  key_deps = [asdict(d) for d in sm.key_dependencies]
77
+ elif sm.dependency_summary is None or not sm.dependency_summary.requested:
78
+ dep_summary_dict = None # "not analyzed" — agent should add --dependencies
82
79
 
83
80
  env_summary_dict: Any = None
84
81
  if sm.env_summary is not None and sm.env_summary.requested:
@@ -88,21 +85,47 @@ def compact_view(sm: SourceMap, *, no_tree: bool = False) -> dict[str, Any]:
88
85
  if sm.code_notes_summary is not None and sm.code_notes_summary.requested:
89
86
  code_notes_summary_dict = asdict(sm.code_notes_summary)
90
87
 
88
+ # Entry points: strip None fields for compactness
89
+ entry_points_compact = [
90
+ {k: v for k, v in asdict(ep).items() if v is not None and v != ""}
91
+ for ep in sm.entry_points
92
+ ]
93
+ if not entry_points_compact:
94
+ entry_points_compact = None # type: ignore[assignment] # signal: not detected
95
+
96
+ # Confidence summary
97
+ conf_dict: Any = None
98
+ anomalies: Any = None
99
+ if sm.confidence_summary is not None:
100
+ conf_dict = asdict(sm.confidence_summary)
101
+ if sm.confidence_summary.anomalies:
102
+ anomalies = sm.confidence_summary.anomalies
103
+
104
+ # Analysis gaps
105
+ gaps_list: Any = None
106
+ if sm.analysis_gaps:
107
+ gaps_list = [asdict(g) for g in sm.analysis_gaps]
108
+
91
109
  result: dict[str, Any] = {
92
110
  "schema_version": sm.metadata.schema_version,
93
111
  "project_type": sm.project_type,
94
112
  "project_summary": sm.project_summary,
95
113
  "architecture_summary": sm.architecture_summary,
96
114
  "stacks": [asdict(stack) for stack in sm.stacks],
97
- "entry_points": [asdict(entry_point) for entry_point in sm.entry_points],
115
+ "entry_points": entry_points_compact,
98
116
  "dependency_summary": dep_summary_dict,
99
117
  "key_dependencies": key_deps,
100
118
  "env_summary": env_summary_dict,
101
119
  "code_notes_summary": code_notes_summary_dict,
120
+ "confidence_summary": conf_dict,
121
+ "anomalies": anomalies,
122
+ "analysis_gaps": gaps_list,
102
123
  }
103
- if not no_tree:
104
- result["file_tree_depth1"] = depth1
105
- return result
124
+ # Strip keys that are fully None and not informative
125
+ return {k: v for k, v in result.items() if v is not None or k in (
126
+ "project_type", "project_summary", "architecture_summary",
127
+ "dependency_summary", "confidence_summary",
128
+ )}
106
129
 
107
130
 
108
131
  def normalize_source_map(sm: SourceMap) -> SourceMap:
@@ -349,15 +372,16 @@ def agent_view(sm: SourceMap) -> dict[str, Any]:
349
372
  """Opinionated output for AI agents — structured, noise-free, gap-aware.
350
373
 
351
374
  Output order:
352
- project what it is and what stack it uses
353
- entry_points → where execution starts
354
- architecture → how it's structured
355
- key_dependencies → what runtime dependencies matter (when analyzed)
356
- signals → compact operational signals (env, notes, tests)
357
- gaps what's uncertain or missing
375
+ 1. project identity: type, summary, primary stack, frameworks
376
+ 2. entry_points → where execution starts (with reason/evidence)
377
+ 3. architecture → how it's structured (flow description)
378
+ 4. key_dependencies → runtime dependencies that matter (when analyzed)
379
+ 5. signals → compact operational context (env, notes, tests)
380
+ 6. confidence_summary detection quality and hard/soft signals
381
+ 7. analysis_gaps → what's uncertain or missing
358
382
 
359
383
  Never includes: file_tree, file_paths, schema internals, empty sections,
360
- null fields, raw dependency lists, or low-signal metadata.
384
+ null fields, raw dependency lists, metrics, docs, or low-signal metadata.
361
385
  """
362
386
  # ── 1. Identity ──────────────────────────────────────────────────────────
363
387
  primary = next((s for s in sm.stacks if s.primary), sm.stacks[0] if sm.stacks else None)
@@ -381,10 +405,11 @@ def agent_view(sm: SourceMap) -> dict[str, Any]:
381
405
 
382
406
  result: dict[str, Any] = {"project": project}
383
407
 
384
- # ── 2. Entry points ───────────────────────────────────────────────────────
408
+ # ── 2. Entry points (with reason/evidence when available) ─────────────────
385
409
  if sm.entry_points:
410
+ _ep_skip = {"workspace"}
386
411
  result["entry_points"] = [
387
- {k: v for k, v in asdict(ep).items() if v is not None and v != ""}
412
+ {k: v for k, v in asdict(ep).items() if v is not None and v != "" and k not in _ep_skip}
388
413
  for ep in sm.entry_points
389
414
  ]
390
415
 
@@ -394,9 +419,9 @@ def agent_view(sm: SourceMap) -> dict[str, Any]:
394
419
 
395
420
  # ── 4. Key dependencies (role-sorted, already computed) ───────────────────
396
421
  if sm.dependency_summary and sm.dependency_summary.requested and sm.key_dependencies:
397
- _skip = {"parent", "manifest_path", "workspace", "source", "ecosystem"}
422
+ _dep_skip = {"parent", "manifest_path", "workspace", "source", "ecosystem"}
398
423
  result["key_dependencies"] = [
399
- {k: v for k, v in asdict(d).items() if v is not None and k not in _skip}
424
+ {k: v for k, v in asdict(d).items() if v is not None and k not in _dep_skip}
400
425
  for d in sm.key_dependencies
401
426
  ]
402
427
 
@@ -428,29 +453,59 @@ def agent_view(sm: SourceMap) -> dict[str, Any]:
428
453
  if signals:
429
454
  result["signals"] = signals
430
455
 
431
- # ── 6. Gaps what's uncertain or missing ────────────────────────────────
432
- gaps: list[str] = []
433
-
434
- if not sm.entry_points:
435
- gaps.append("No entry point detected — project structure may be non-standard")
436
-
437
- if primary and primary.confidence == "low":
438
- gaps.append(f"Low-confidence stack detection for '{primary.stack}' — no manifest found")
439
-
440
- heuristic_stacks = [s for s in sm.stacks if s.detection_method == "heuristic"]
441
- if heuristic_stacks:
442
- gaps.append(
443
- f"Heuristic-only detection (no manifest): {', '.join(s.stack for s in heuristic_stacks)}"
444
- )
445
-
446
- if sm.dependency_summary and sm.dependency_summary.requested:
447
- for limitation in sm.dependency_summary.limitations[:3]:
448
- gaps.append(limitation)
449
- elif not sm.dependency_summary or not sm.dependency_summary.requested:
450
- gaps.append("Dependencies not analyzed add --dependencies for full context")
451
-
452
- if gaps:
453
- result["gaps"] = gaps
456
+ # ── 6. Confidence summary ─────────────────────────────────────────────────
457
+ if sm.confidence_summary is not None:
458
+ cs = sm.confidence_summary
459
+ conf: dict[str, Any] = {
460
+ "overall": cs.overall,
461
+ "stack": cs.stack_confidence,
462
+ "entry_points": cs.entry_point_confidence,
463
+ }
464
+ if cs.hard_signals:
465
+ conf["hard_signals"] = cs.hard_signals
466
+ if cs.soft_signals:
467
+ conf["soft_signals"] = cs.soft_signals
468
+ if cs.ignored_signals:
469
+ conf["ignored_signals"] = cs.ignored_signals
470
+ if cs.anomalies:
471
+ conf["anomalies"] = cs.anomalies
472
+ result["confidence_summary"] = conf
473
+
474
+ # ── 7. Analysis gaps ──────────────────────────────────────────────────────
475
+ analysis_gaps: list[dict[str, Any]] = []
476
+
477
+ if sm.analysis_gaps:
478
+ analysis_gaps = [asdict(g) for g in sm.analysis_gaps]
479
+ else:
480
+ # Fallback gap derivation when confidence_analyzer was not run
481
+ if not sm.entry_points:
482
+ analysis_gaps.append({
483
+ "area": "entry_points",
484
+ "reason": "No entry point detected — project structure may be non-standard",
485
+ "impact": "high",
486
+ })
487
+ if primary and primary.confidence == "low":
488
+ analysis_gaps.append({
489
+ "area": "stack",
490
+ "reason": f"Low-confidence detection for '{primary.stack}' — no manifest found",
491
+ "impact": "medium",
492
+ })
493
+ heuristic_stacks = [s for s in sm.stacks if s.detection_method == "heuristic"]
494
+ if heuristic_stacks:
495
+ analysis_gaps.append({
496
+ "area": "stack",
497
+ "reason": f"Heuristic-only detection (no manifest): {', '.join(s.stack for s in heuristic_stacks)}",
498
+ "impact": "medium",
499
+ })
500
+ if not sm.dependency_summary or not sm.dependency_summary.requested:
501
+ analysis_gaps.append({
502
+ "area": "dependencies",
503
+ "reason": "Dependencies not analyzed — add --dependencies for full context",
504
+ "impact": "medium",
505
+ })
506
+
507
+ if analysis_gaps:
508
+ result["analysis_gaps"] = analysis_gaps
454
509
 
455
510
  return result
456
511
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: sourcecode
3
- Version: 0.22.0
3
+ Version: 0.23.0
4
4
  Summary: Genera un mapa de contexto estructurado de proyectos de software para agentes IA
5
5
  License: MIT
6
6
  Requires-Python: >=3.9
@@ -1,22 +1,23 @@
1
- sourcecode/__init__.py,sha256=fj7lPtxQ0hLFlBv6uWwpvAPQdgoXjKNjaM1Tpn2SFKg,100
1
+ sourcecode/__init__.py,sha256=Dh82QypoDBYj3Gur3QVPfjnJcgmk8S2TvsTDOr0Z0Mc,100
2
2
  sourcecode/architecture_analyzer.py,sha256=zxgqeqn2FhIUDn06X-whjGKiXbrHFkRlzZcr2zKd0Gw,16874
3
3
  sourcecode/architecture_summary.py,sha256=rr-Q2LLe6bXZ27NuV2pYb0_LZyEFn3sngKMdXCZSGbE,13263
4
4
  sourcecode/classifier.py,sha256=Ft_RfYS-KOe0t7vjgUx04OoCJd1-DXK7k9-I0CFDSnU,6934
5
- sourcecode/cli.py,sha256=x_ZjTyy9IaBqCFXOLxwTvopB3OeHVCv2mk1s1E2Hc_g,29446
5
+ sourcecode/cli.py,sha256=SyO5JHR0cKMoMEas-of1pnWneZMjr7JhTxURR7-Kv5w,30925
6
6
  sourcecode/code_notes_analyzer.py,sha256=rRd8bFYV0krjlxxQV0wenwE9K7pVpUQSR7KvSvUQKw4,9226
7
+ sourcecode/confidence_analyzer.py,sha256=gvQ92XCeGsACskSGU-GWTB7OqU1tT0eTgV-A7XA_lGs,8968
7
8
  sourcecode/coverage_parser.py,sha256=q0LeZJaX1bnntLu-ImksdBsMlpsVmk_iUfSaB4eaJGo,19702
8
- sourcecode/dependency_analyzer.py,sha256=C-G2paetrBUCMTA2KlWrD04fl9Y4q75sk4X0M0utzpE,50863
9
+ sourcecode/dependency_analyzer.py,sha256=Exq0BfInvfS5iAg9xAr6WI2uPNuotkIudTKcYJcRhB8,52757
9
10
  sourcecode/doc_analyzer.py,sha256=Ec3orx6vBKsh5cNM3-F4y2Got2KuKx8w3dErwtdtM-A,19891
10
11
  sourcecode/env_analyzer.py,sha256=JZxBOuIxnM0xw0IaJFL6LiPJBErL848L3XoTOHGBqZI,13554
11
12
  sourcecode/git_analyzer.py,sha256=S9PGt8RDasBQYQUsZh9-H_KWVlPvzwR4jgM7TKN9ZCk,7643
12
13
  sourcecode/graph_analyzer.py,sha256=cxl7Xb88aGxIVOCsatZJAE1CtgXGzIbJfqytV-9kkFs,45172
13
14
  sourcecode/metrics_analyzer.py,sha256=4uh11v-Q0gdrN87BOxuFWUym3N3AOkOuy21K5N8peB8,20126
14
- sourcecode/prepare_context.py,sha256=yPRohYZ3wvX156GQSo1vYuoCDBIG3uy3G-3DdNbGHKU,19271
15
+ sourcecode/prepare_context.py,sha256=1Z8UcefgIWu1J_tYXVIu2qNbTt08S72gW7QUe9c7u0s,27561
15
16
  sourcecode/redactor.py,sha256=xuGcadGEHaPw4qZXlMDvzMCsr4VOkdp3oBQptHyJk8c,2884
16
17
  sourcecode/scanner.py,sha256=aM3h9-DCQ3xKpeHpHYdo2vX6T5P95HA_YwZbkAVNwmo,8288
17
- sourcecode/schema.py,sha256=YJhzdn5C9_S1OPWoMgn7gdtY9jgWfiESN-m9znBTjIg,15833
18
+ sourcecode/schema.py,sha256=u8WorLBlbcG-I5oaB0ShTGHDoNaLcl4vyADNV6P2O9g,17159
18
19
  sourcecode/semantic_analyzer.py,sha256=asQfJf-EhzYaOTA-iMuZsrVXtbW7SV2WEKCxgsxa88Y,79413
19
- sourcecode/serializer.py,sha256=Yqn-AOZL0kSFwfY0U72yOvVuYi7quPjyq6xXHbW_vc0,22462
20
+ sourcecode/serializer.py,sha256=-dNeUFingePa3WcCTjLe0o6jSdLu4PfL9rPlD8uuky4,25163
20
21
  sourcecode/summarizer.py,sha256=YdIA5gGEc0XSctPgmTnFnuZI72bprrOYhkJNoDmvHwM,12114
21
22
  sourcecode/tree_utils.py,sha256=Fj9OIuUksBvgibNd3feog0sMDjVypJzPexp5lvMoYWI,1424
22
23
  sourcecode/workspace.py,sha256=fQlVoNx8S-fSHpKoJ0JBvEHCFkxszH0KZVJed1i3TRk,6845
@@ -26,20 +27,20 @@ sourcecode/detectors/dart.py,sha256=QbqaL5v18-_ort75HihVBt8MsKUfOcFDF8IpWFLiXpI,
26
27
  sourcecode/detectors/dotnet.py,sha256=UC-74VW850r3admcl36R-rl3M0-nBFXw8ph5e9RSas8,1978
27
28
  sourcecode/detectors/elixir.py,sha256=jCpvt5Yi6jvplc80ovRtWh17q-11ZGo9qX7o8b57TJE,1713
28
29
  sourcecode/detectors/go.py,sha256=KehAyvC3H_s8zwwqFM0bAbztQbbUBDjpNbGvM2kQlk8,1836
29
- sourcecode/detectors/heuristic.py,sha256=253JB1iD0IXC72AdGRivQtZNgGziyOJOJrJeztOzdJs,1953
30
+ sourcecode/detectors/heuristic.py,sha256=JWb_dXNI1YDxk9a3DFeXTbKYfAGOFqV6jVxxqouWSiA,2238
30
31
  sourcecode/detectors/java.py,sha256=qFVpMy5mu490ENhutO1LV2Gidms4ZZUfNX5Qv5Qxh2I,7188
31
32
  sourcecode/detectors/jvm_ext.py,sha256=EgHJ5W8EE-ZTN9V607mVzohyKgZE8Mc2jCi-DF8RAZU,2616
32
33
  sourcecode/detectors/nodejs.py,sha256=qNJ1m0E_TsPiR_iNcSgK4igb4mm99h-HDKE7C8gME6o,4762
33
34
  sourcecode/detectors/parsers.py,sha256=ugPg8yNUf0Ai1gA7Fnn6wAkYGFjTxRodSP3IeViYJJ4,2290
34
35
  sourcecode/detectors/php.py,sha256=W_AQD0WMVDdWHa9h_ilX6W8XSpz0X4ctpMK2WXfXf1I,1887
35
36
  sourcecode/detectors/project.py,sha256=YCynNC8drJutPMiDGPutlYaL49IO8M-1Ms_RwVY09ss,6221
36
- sourcecode/detectors/python.py,sha256=EGhHxvgjSVAZujOswdrK7awuKj79bfYRKXI38pqpJs8,9504
37
+ sourcecode/detectors/python.py,sha256=GLuLxJw-6tTrnC1Ng8NLDtt8AOT7waM1MxVWc9jBaEw,9846
37
38
  sourcecode/detectors/ruby.py,sha256=Q4B5ePAw6-T4DLfanKJiuLHLqUigTPVrzylcXJMei3M,1591
38
39
  sourcecode/detectors/rust.py,sha256=V23NO6Y8NsrhrUlGC2feUEcNK63hf3gc1y3jI4tes1Q,2264
39
40
  sourcecode/detectors/systems.py,sha256=nYaKbGDFu0EOXFcd_1doWFT3tTUdkbxc2DjHUF5TcqQ,1627
40
41
  sourcecode/detectors/terraform.py,sha256=cxORPR_zVLOJpHlh4e9JnFpkQsn_UnqMMom5yG65hZ4,1693
41
42
  sourcecode/detectors/tooling.py,sha256=hIvop80No22pqyGVJ32NKliSdjkHRePQkIRroqG01bY,1875
42
- sourcecode-0.22.0.dist-info/METADATA,sha256=R7dNRAnWzgeRSNAnDT78YOaThzkojXX0eKZXkyVD6Pw,30326
43
- sourcecode-0.22.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
44
- sourcecode-0.22.0.dist-info/entry_points.txt,sha256=voDtdlGU2-jR3wCZpQoOls_0eUKaDVCKIfajGHFDATs,50
45
- sourcecode-0.22.0.dist-info/RECORD,,
43
+ sourcecode-0.23.0.dist-info/METADATA,sha256=EwoOCYdt8LinpV5ymkOsnd3A01aEcFxbeidutgfwPsw,30326
44
+ sourcecode-0.23.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
45
+ sourcecode-0.23.0.dist-info/entry_points.txt,sha256=voDtdlGU2-jR3wCZpQoOls_0eUKaDVCKIfajGHFDATs,50
46
+ sourcecode-0.23.0.dist-info/RECORD,,