flash-context 0.3.1__tar.gz → 0.3.2__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.
- {flash_context-0.3.1 → flash_context-0.3.2}/CHANGELOG.md +22 -0
- {flash_context-0.3.1 → flash_context-0.3.2}/PKG-INFO +11 -19
- {flash_context-0.3.1 → flash_context-0.3.2}/README.md +10 -18
- {flash_context-0.3.1 → flash_context-0.3.2}/pyproject.toml +1 -1
- {flash_context-0.3.1 → flash_context-0.3.2}/src/flash_context/agent.py +138 -15
- {flash_context-0.3.1 → flash_context-0.3.2}/.env.example +0 -0
- {flash_context-0.3.1 → flash_context-0.3.2}/.gitignore +0 -0
- {flash_context-0.3.1 → flash_context-0.3.2}/LICENSE +0 -0
- {flash_context-0.3.1 → flash_context-0.3.2}/config.yaml.example +0 -0
- {flash_context-0.3.1 → flash_context-0.3.2}/src/flash_context/__init__.py +0 -0
- {flash_context-0.3.1 → flash_context-0.3.2}/src/flash_context/cli.py +0 -0
- {flash_context-0.3.1 → flash_context-0.3.2}/src/flash_context/config.py +0 -0
- {flash_context-0.3.1 → flash_context-0.3.2}/src/flash_context/llm.py +0 -0
- {flash_context-0.3.1 → flash_context-0.3.2}/src/flash_context/logging.py +0 -0
- {flash_context-0.3.1 → flash_context-0.3.2}/src/flash_context/mcp_server.py +0 -0
- {flash_context-0.3.1 → flash_context-0.3.2}/src/flash_context/tools/__init__.py +0 -0
- {flash_context-0.3.1 → flash_context-0.3.2}/src/flash_context/tools/glob.py +0 -0
- {flash_context-0.3.1 → flash_context-0.3.2}/src/flash_context/tools/grep.py +0 -0
- {flash_context-0.3.1 → flash_context-0.3.2}/src/flash_context/tools/read.py +0 -0
- {flash_context-0.3.1 → flash_context-0.3.2}/src/flash_context/tools/safety.py +0 -0
- {flash_context-0.3.1 → flash_context-0.3.2}/src/flash_context/types.py +0 -0
|
@@ -4,6 +4,28 @@ User-visible changes and migration instructions for each release.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [0.3.2] - 2026-11-01
|
|
8
|
+
|
|
9
|
+
### Fixed
|
|
10
|
+
|
|
11
|
+
- Require whole, case-sensitive query tokens inside exact-symbol citations,
|
|
12
|
+
including read-derived fallback evidence. Reject plausible-home file citations
|
|
13
|
+
without matching source text and retain scoped Python preflight diagnostics.
|
|
14
|
+
- Report `query symbol not found in verified evidence` when symbol lookups finish
|
|
15
|
+
without accepted evidence, rather than relying on generic termination warnings.
|
|
16
|
+
|
|
17
|
+
### Migration
|
|
18
|
+
|
|
19
|
+
- Exact-symbol queries may now return `NO_CITATIONS_FOUND` with empty citations
|
|
20
|
+
and snippets where previous versions returned unrelated files. Token presence
|
|
21
|
+
is lexical evidence, not proof of a definition or semantic relevance. Public
|
|
22
|
+
schemas, configuration, and CLI exit semantics are unchanged.
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- Condense the README overview and move safety and evidence-validation details
|
|
27
|
+
to the architecture documentation.
|
|
28
|
+
|
|
7
29
|
## [0.3.1] - 2026-11-01
|
|
8
30
|
|
|
9
31
|
### Fixed
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: flash-context
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Read-only context exploration and retrieval for AI agents, starting with codebases and repositories.
|
|
5
5
|
Project-URL: Repository, https://github.com/zyf0717/flash-context
|
|
6
6
|
Project-URL: Issues, https://github.com/zyf0717/flash-context/issues
|
|
@@ -28,24 +28,16 @@ Description-Content-Type: text/markdown
|
|
|
28
28
|
[](https://pypi.org/project/flash-context/)
|
|
29
29
|
[](LICENSE)
|
|
30
30
|
|
|
31
|
-
A lightweight, model-agnostic repository explorer
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
exploration core with read-only tools, path safety, and resource limits.
|
|
42
|
-
Scans and regex execution are bounded; partial results report truncation.
|
|
43
|
-
Citation validation checks safe paths and existing line coverage, not semantic
|
|
44
|
-
relevance or minimal ranges. Citation precision depends on the explorer model.
|
|
45
|
-
Implementation queries continue past documentation-only citations within the turn budget; documentation-only
|
|
46
|
-
fallbacks carry incomplete-result metadata and warnings. Build outputs such as
|
|
47
|
-
`dist/` remain excluded; inspect a source checkout for implementation evidence.
|
|
48
|
-
Optional metadata-only trajectory logs must stay outside the inspected repository.
|
|
31
|
+
A lightweight, model-agnostic repository explorer for AI agents, available as a
|
|
32
|
+
Python CLI and Model Context Protocol (MCP) server. It combines deterministic
|
|
33
|
+
lookup with search and reading delegated to a utility model through an
|
|
34
|
+
OpenAI-compatible endpoint, returning compact, validated file-line citations and
|
|
35
|
+
source snippets.
|
|
36
|
+
|
|
37
|
+
CLI and MCP share a read-only core for exploring code, documentation,
|
|
38
|
+
configuration, and other local text files with path safety and resource limits.
|
|
39
|
+
See [safety and evidence validation](docs/architecture.md#safety-and-evidence-validation)
|
|
40
|
+
for details and limitations.
|
|
49
41
|
|
|
50
42
|
Inspired by [FastContext](https://arxiv.org/abs/2606.14066), a now-withdrawn paper
|
|
51
43
|
on separating repository exploration from task solving, using specialized models
|
|
@@ -5,24 +5,16 @@
|
|
|
5
5
|
[](https://pypi.org/project/flash-context/)
|
|
6
6
|
[](LICENSE)
|
|
7
7
|
|
|
8
|
-
A lightweight, model-agnostic repository explorer
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
exploration core with read-only tools, path safety, and resource limits.
|
|
19
|
-
Scans and regex execution are bounded; partial results report truncation.
|
|
20
|
-
Citation validation checks safe paths and existing line coverage, not semantic
|
|
21
|
-
relevance or minimal ranges. Citation precision depends on the explorer model.
|
|
22
|
-
Implementation queries continue past documentation-only citations within the turn budget; documentation-only
|
|
23
|
-
fallbacks carry incomplete-result metadata and warnings. Build outputs such as
|
|
24
|
-
`dist/` remain excluded; inspect a source checkout for implementation evidence.
|
|
25
|
-
Optional metadata-only trajectory logs must stay outside the inspected repository.
|
|
8
|
+
A lightweight, model-agnostic repository explorer for AI agents, available as a
|
|
9
|
+
Python CLI and Model Context Protocol (MCP) server. It combines deterministic
|
|
10
|
+
lookup with search and reading delegated to a utility model through an
|
|
11
|
+
OpenAI-compatible endpoint, returning compact, validated file-line citations and
|
|
12
|
+
source snippets.
|
|
13
|
+
|
|
14
|
+
CLI and MCP share a read-only core for exploring code, documentation,
|
|
15
|
+
configuration, and other local text files with path safety and resource limits.
|
|
16
|
+
See [safety and evidence validation](docs/architecture.md#safety-and-evidence-validation)
|
|
17
|
+
for details and limitations.
|
|
26
18
|
|
|
27
19
|
Inspired by [FastContext](https://arxiv.org/abs/2606.14066), a now-withdrawn paper
|
|
28
20
|
on separating repository exploration from task solving, using specialized models
|
|
@@ -62,12 +62,21 @@ EXACT_TEXT_RE = re.compile(r"[`'\"](?P<target>[^`'\"]{1,120})[`'\"]")
|
|
|
62
62
|
TOKEN_RE = re.compile(r"\b(?P<token>[A-Za-z_][A-Za-z0-9_]*)\b")
|
|
63
63
|
|
|
64
64
|
|
|
65
|
+
@dataclass(frozen=True, slots=True)
|
|
66
|
+
class _DefinitionScan:
|
|
67
|
+
citations: tuple[Citation, ...]
|
|
68
|
+
status: Literal["unique", "missing", "ambiguous", "incomplete"]
|
|
69
|
+
warnings: tuple[str, ...]
|
|
70
|
+
|
|
71
|
+
|
|
65
72
|
@dataclass(slots=True)
|
|
66
73
|
class EvidenceState:
|
|
67
74
|
reads: list[ToolObservation] = field(default_factory=list)
|
|
68
75
|
citations: list[Citation] = field(default_factory=list)
|
|
69
76
|
observations: dict[str, ToolObservation] = field(default_factory=dict)
|
|
70
77
|
prefer_source: bool = False
|
|
78
|
+
symbol: str | None = None
|
|
79
|
+
definition_scan: _DefinitionScan | None = None
|
|
71
80
|
|
|
72
81
|
|
|
73
82
|
def explore(
|
|
@@ -79,10 +88,15 @@ def explore(
|
|
|
79
88
|
request.validate()
|
|
80
89
|
repo_root = request.repo_root.resolve()
|
|
81
90
|
validate_trajectory_dir(settings.traj_dir, repo_root)
|
|
91
|
+
evidence = EvidenceState(
|
|
92
|
+
prefer_source=_implementation_query(request.query),
|
|
93
|
+
symbol=_query_symbol(request.query),
|
|
94
|
+
)
|
|
82
95
|
fast_result = _try_exact_fast_path(
|
|
83
96
|
request,
|
|
84
97
|
repo_root=repo_root,
|
|
85
98
|
settings=settings,
|
|
99
|
+
evidence=evidence,
|
|
86
100
|
)
|
|
87
101
|
if fast_result is not None:
|
|
88
102
|
return fast_result
|
|
@@ -99,12 +113,28 @@ def explore(
|
|
|
99
113
|
limits=settings.limits,
|
|
100
114
|
)
|
|
101
115
|
messages = _initial_messages(request, file_sample, settings)
|
|
116
|
+
if evidence.definition_scan is not None:
|
|
117
|
+
scan = evidence.definition_scan
|
|
118
|
+
messages.append(
|
|
119
|
+
{
|
|
120
|
+
"role": "system",
|
|
121
|
+
"content": (
|
|
122
|
+
f"Local Python .py/.pyi definition scan for {evidence.symbol}: "
|
|
123
|
+
f"{scan.status}. "
|
|
124
|
+
"A complete missing result means zero Python definition matches; "
|
|
125
|
+
"assignments, imports, docs, and other languages may still "
|
|
126
|
+
"contain the symbol. Never infer a plausible home file. "
|
|
127
|
+
"Cited lines must contain the exact case-sensitive whole symbol."
|
|
128
|
+
),
|
|
129
|
+
}
|
|
130
|
+
)
|
|
102
131
|
tools = tool_schemas()
|
|
103
132
|
# A capped file sample is expected; actual scan failures must reach callers.
|
|
104
133
|
warnings = list(file_sample.warnings)
|
|
134
|
+
if evidence.definition_scan is not None:
|
|
135
|
+
warnings.extend(evidence.definition_scan.warnings)
|
|
105
136
|
truncated = bool(warnings)
|
|
106
137
|
last_content = ""
|
|
107
|
-
evidence = EvidenceState(prefer_source=_implementation_query(request.query))
|
|
108
138
|
|
|
109
139
|
try:
|
|
110
140
|
for turn in range(1, effective_max_turns + 1):
|
|
@@ -186,6 +216,22 @@ def explore(
|
|
|
186
216
|
tool_calls = _extract_tool_calls(message)
|
|
187
217
|
if not tool_calls:
|
|
188
218
|
citations = _best_current_citations(evidence)
|
|
219
|
+
if evidence.symbol and content_text == NO_CITATIONS_FOUND:
|
|
220
|
+
citations = []
|
|
221
|
+
elif evidence.symbol and not citations and turn < effective_max_turns:
|
|
222
|
+
messages.append({"role": "assistant", "content": content_text})
|
|
223
|
+
messages.append(
|
|
224
|
+
{
|
|
225
|
+
"role": "user",
|
|
226
|
+
"content": (
|
|
227
|
+
"No verified citation contains the exact query symbol. "
|
|
228
|
+
"Search with fresh read-only calls, or return "
|
|
229
|
+
"NO_CITATIONS_FOUND."
|
|
230
|
+
),
|
|
231
|
+
}
|
|
232
|
+
)
|
|
233
|
+
last_content = ""
|
|
234
|
+
continue
|
|
189
235
|
if evidence.prefer_source and _documentation_only(citations):
|
|
190
236
|
last_content = ""
|
|
191
237
|
messages.append({"role": "assistant", "content": content_text})
|
|
@@ -308,6 +354,8 @@ def explore(
|
|
|
308
354
|
last_content,
|
|
309
355
|
evidence.reads,
|
|
310
356
|
)
|
|
357
|
+
if evidence.symbol:
|
|
358
|
+
citations = _best_current_citations(evidence)
|
|
311
359
|
_append_warning_once(warnings, "max turns exceeded after partial answer")
|
|
312
360
|
if request.citation and not citations:
|
|
313
361
|
_append_warning_once(
|
|
@@ -439,6 +487,9 @@ def _finalize_result(
|
|
|
439
487
|
truncated: bool,
|
|
440
488
|
warnings: list[str],
|
|
441
489
|
) -> ExploreResult:
|
|
490
|
+
if _query_symbol(request.query) and not citations:
|
|
491
|
+
answer = NO_CITATIONS_FOUND
|
|
492
|
+
_append_warning_once(warnings, "query symbol not found in verified evidence")
|
|
442
493
|
citations = _merge_adjacent_citations(citations)[:MAX_FINAL_CITATIONS]
|
|
443
494
|
if _implementation_query(request.query) and _documentation_only(citations):
|
|
444
495
|
truncated = True
|
|
@@ -580,6 +631,7 @@ def _try_exact_fast_path(
|
|
|
580
631
|
*,
|
|
581
632
|
repo_root: Path,
|
|
582
633
|
settings: Settings,
|
|
634
|
+
evidence: EvidenceState,
|
|
583
635
|
) -> ExploreResult | None:
|
|
584
636
|
bare_path = request.query.strip()
|
|
585
637
|
if len(bare_path) >= 2 and bare_path[0] in "`'\"" and bare_path[-1] == bare_path[0]:
|
|
@@ -610,7 +662,6 @@ def _try_exact_fast_path(
|
|
|
610
662
|
)
|
|
611
663
|
if not _is_exact_lookup(request.query):
|
|
612
664
|
return None
|
|
613
|
-
evidence = EvidenceState()
|
|
614
665
|
warnings: list[str] = []
|
|
615
666
|
citations = _validated_citations_from_content(
|
|
616
667
|
request.query,
|
|
@@ -643,6 +694,13 @@ def _try_exact_fast_path(
|
|
|
643
694
|
target=target,
|
|
644
695
|
allow_assignment=True,
|
|
645
696
|
)
|
|
697
|
+
citations = _validate_citations(
|
|
698
|
+
citations,
|
|
699
|
+
repo_root=repo_root,
|
|
700
|
+
settings=settings,
|
|
701
|
+
evidence=evidence,
|
|
702
|
+
warnings=warnings,
|
|
703
|
+
)
|
|
646
704
|
if citations:
|
|
647
705
|
return _finalize_result(
|
|
648
706
|
request=request,
|
|
@@ -659,25 +717,77 @@ def _try_exact_fast_path(
|
|
|
659
717
|
symbol = _extract_pathless_symbol(request.query)
|
|
660
718
|
if symbol is None:
|
|
661
719
|
return None
|
|
662
|
-
|
|
720
|
+
scan = _scan_definitions(
|
|
663
721
|
repo_root=repo_root,
|
|
664
722
|
settings=settings,
|
|
665
723
|
symbol=symbol,
|
|
666
724
|
)
|
|
667
|
-
|
|
725
|
+
evidence.definition_scan = scan
|
|
726
|
+
if scan.status != "unique":
|
|
668
727
|
return None
|
|
669
728
|
return _finalize_result(
|
|
670
729
|
request=request,
|
|
671
730
|
repo_root=repo_root,
|
|
672
731
|
settings=settings,
|
|
673
732
|
answer="",
|
|
674
|
-
citations=
|
|
733
|
+
citations=list(scan.citations),
|
|
675
734
|
turns_used=0,
|
|
676
735
|
truncated=False,
|
|
677
736
|
warnings=[],
|
|
678
737
|
)
|
|
679
738
|
|
|
680
739
|
|
|
740
|
+
def _query_symbol(query: str) -> str | None:
|
|
741
|
+
if not _is_exact_lookup(query) or extract_citations(query):
|
|
742
|
+
return None
|
|
743
|
+
path = _extract_explicit_path(query)
|
|
744
|
+
target = (
|
|
745
|
+
_extract_exact_target(query, path) if path else _extract_pathless_symbol(query)
|
|
746
|
+
)
|
|
747
|
+
if target and re.fullmatch(r"[A-Za-z_][A-Za-z0-9_]*", target):
|
|
748
|
+
return target
|
|
749
|
+
return None
|
|
750
|
+
|
|
751
|
+
|
|
752
|
+
def _symbol_in_range(
|
|
753
|
+
citation: Citation,
|
|
754
|
+
reads: list[ToolObservation],
|
|
755
|
+
symbol: str | None,
|
|
756
|
+
) -> bool:
|
|
757
|
+
if symbol is None:
|
|
758
|
+
return True
|
|
759
|
+
pattern = re.compile(rf"\b{re.escape(symbol)}\b")
|
|
760
|
+
for read in reads:
|
|
761
|
+
if not _citation_has_read_coverage(citation, [read]) or read.content is None:
|
|
762
|
+
continue
|
|
763
|
+
start, _ = _parse_line_range(read.line_range or "")
|
|
764
|
+
assert start is not None and citation.start_line is not None
|
|
765
|
+
assert citation.end_line is not None
|
|
766
|
+
lines = read.content.split("\n")
|
|
767
|
+
lines = [line + "\n" for line in lines[:-1]] + lines[-1:]
|
|
768
|
+
text = "".join(
|
|
769
|
+
lines[citation.start_line - start : citation.end_line - start + 1]
|
|
770
|
+
)
|
|
771
|
+
for match in pattern.finditer(text):
|
|
772
|
+
# A byte-capped trailing token has no verified right boundary.
|
|
773
|
+
if match.end() < len(text) or not read.truncated or text.endswith("\n"):
|
|
774
|
+
return True
|
|
775
|
+
return False
|
|
776
|
+
|
|
777
|
+
|
|
778
|
+
def _eligible_reads(evidence: EvidenceState) -> list[ToolObservation]:
|
|
779
|
+
if evidence.symbol is None:
|
|
780
|
+
return evidence.reads
|
|
781
|
+
return [
|
|
782
|
+
read
|
|
783
|
+
for read in evidence.reads
|
|
784
|
+
if any(
|
|
785
|
+
_symbol_in_range(citation, [read], evidence.symbol)
|
|
786
|
+
for citation in _citations_from_reads([read])
|
|
787
|
+
)
|
|
788
|
+
]
|
|
789
|
+
|
|
790
|
+
|
|
681
791
|
def _is_exact_lookup(query: str) -> bool:
|
|
682
792
|
# Admit a small lookup vocabulary; any unexplained intent goes to the model.
|
|
683
793
|
paths = list(EXPLICIT_PATH_RE.finditer(query))
|
|
@@ -798,12 +908,12 @@ def _exact_matches_in_file(
|
|
|
798
908
|
return citations if 0 < len(citations) <= MAX_FINAL_CITATIONS else []
|
|
799
909
|
|
|
800
910
|
|
|
801
|
-
def
|
|
911
|
+
def _scan_definitions(
|
|
802
912
|
*,
|
|
803
913
|
repo_root: Path,
|
|
804
914
|
settings: Settings,
|
|
805
915
|
symbol: str,
|
|
806
|
-
) ->
|
|
916
|
+
) -> _DefinitionScan:
|
|
807
917
|
definition_pattern = rf"^\s*(?:async\s+def|def|class)\s+{re.escape(symbol)}\b"
|
|
808
918
|
grep_result = repo_grep(
|
|
809
919
|
repo_root=repo_root,
|
|
@@ -826,9 +936,16 @@ def _unique_definition_citation(
|
|
|
826
936
|
for result in (grep_result, pyi_result)
|
|
827
937
|
for hit in result.hits
|
|
828
938
|
]
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
939
|
+
status: Literal["unique", "missing", "ambiguous", "incomplete"]
|
|
940
|
+
if grep_result.truncated or pyi_result.truncated:
|
|
941
|
+
status = "incomplete"
|
|
942
|
+
elif len(citations) > 1:
|
|
943
|
+
status = "ambiguous"
|
|
944
|
+
else:
|
|
945
|
+
status = "unique" if citations else "missing"
|
|
946
|
+
return _DefinitionScan(
|
|
947
|
+
tuple(citations), status, tuple(grep_result.warnings + pyi_result.warnings)
|
|
948
|
+
)
|
|
832
949
|
|
|
833
950
|
|
|
834
951
|
def _grep_exact_target(
|
|
@@ -919,7 +1036,8 @@ def _validate_citations(
|
|
|
919
1036
|
if normalized is None:
|
|
920
1037
|
continue
|
|
921
1038
|
if _citation_has_read_coverage(normalized, evidence.reads):
|
|
922
|
-
|
|
1039
|
+
if _symbol_in_range(normalized, evidence.reads, evidence.symbol):
|
|
1040
|
+
validated.append(normalized)
|
|
923
1041
|
continue
|
|
924
1042
|
verified = _verify_citation_by_read(
|
|
925
1043
|
normalized,
|
|
@@ -928,7 +1046,9 @@ def _validate_citations(
|
|
|
928
1046
|
evidence=evidence,
|
|
929
1047
|
warnings=warnings,
|
|
930
1048
|
)
|
|
931
|
-
if verified is not None
|
|
1049
|
+
if verified is not None and _symbol_in_range(
|
|
1050
|
+
verified, evidence.reads, evidence.symbol
|
|
1051
|
+
):
|
|
932
1052
|
validated.append(verified)
|
|
933
1053
|
return _merge_adjacent_citations(_dedupe_citations(validated))[:MAX_FINAL_CITATIONS]
|
|
934
1054
|
|
|
@@ -1045,9 +1165,10 @@ def _dedupe_citations(citations: list[Citation]) -> list[Citation]:
|
|
|
1045
1165
|
|
|
1046
1166
|
|
|
1047
1167
|
def _best_current_citations(evidence: EvidenceState) -> list[Citation]:
|
|
1168
|
+
reads = _eligible_reads(evidence)
|
|
1048
1169
|
if evidence.prefer_source:
|
|
1049
1170
|
source_reads = [
|
|
1050
|
-
read for read in
|
|
1171
|
+
read for read in reads if not _documentation_path(read.path or "")
|
|
1051
1172
|
]
|
|
1052
1173
|
source_citations = [
|
|
1053
1174
|
citation
|
|
@@ -1060,7 +1181,7 @@ def _best_current_citations(evidence: EvidenceState) -> list[Citation]:
|
|
|
1060
1181
|
)
|
|
1061
1182
|
if evidence.citations:
|
|
1062
1183
|
return evidence.citations[:MAX_FINAL_CITATIONS]
|
|
1063
|
-
return _citations_from_reads(
|
|
1184
|
+
return _citations_from_reads(reads)
|
|
1064
1185
|
|
|
1065
1186
|
|
|
1066
1187
|
def _citations_from_reads(observations: list[ToolObservation]) -> list[Citation]:
|
|
@@ -1112,7 +1233,7 @@ def _best_narrow_read_citation(
|
|
|
1112
1233
|
evidence: EvidenceState,
|
|
1113
1234
|
settings: Settings,
|
|
1114
1235
|
) -> Citation | None:
|
|
1115
|
-
for citation in _citations_from_reads(evidence
|
|
1236
|
+
for citation in _citations_from_reads(_eligible_reads(evidence)):
|
|
1116
1237
|
for observation in evidence.reads:
|
|
1117
1238
|
start, end = _parse_line_range(observation.line_range or "")
|
|
1118
1239
|
if (
|
|
@@ -1314,6 +1435,8 @@ def _initial_messages(
|
|
|
1314
1435
|
"tool calls in the same turn. "
|
|
1315
1436
|
"For exact symbol, function, or class names, use repo_grep "
|
|
1316
1437
|
"before reading broad file ranges. "
|
|
1438
|
+
"Exact-symbol citations must contain the whole case-sensitive "
|
|
1439
|
+
"query token inside the cited lines. "
|
|
1317
1440
|
"For implementation questions, prefer implementation source over "
|
|
1318
1441
|
"documentation or Markdown; use documentation when requested or "
|
|
1319
1442
|
"needed to support the answer. Cite the smallest line ranges "
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|