@@ -102,32 +102,67 @@ def _data_source_miss_hint(data_source: str) -> str:
102102 )
103103
104104
105- def format_artifacts (data : dict , data_source : str = None ) -> str :
106- """Format fetched artifacts for display."""
105+ def _not_found_lines (not_found : list ) -> list :
106+ """Lines listing requested identifiers the backend could not resolve or that are
107+ outside the caller's access scope, with a re-check/retry hint."""
108+ lines = [
109+ f"\n { '=' * 60 } " ,
110+ f"⚠️ { len (not_found )} requested identifier(s) not found or inaccessible:" ,
111+ ]
112+ for identifier in not_found :
113+ lines .append (f" • { identifier } " )
114+ lines .append (f"{ '=' * 60 } " )
115+ lines .append (
116+ "💡 Do NOT silently omit these. A not-found entry means the identifier did not "
117+ "resolve, or points outside the data sources this key can read — it is NOT proof "
118+ "the code is absent. Re-check those exact identifiers, re-run search.py or grep.py "
119+ "to get fresh ids, then re-fetch the problematic ones; if they still cannot be "
120+ "retrieved, tell the user which artifacts could not be fetched."
121+ )
122+ return lines
123+
124+
125+ def format_artifacts (data : dict , data_source : str = None , requested : list = None ) -> str :
126+ """Format fetched artifacts for display.
127+
128+ Requested identifiers the backend could not resolve — or that are outside the caller's
129+ access scope — come back with ``found: false`` (older backends omit the flag and return
130+ ``content: null``). They are NOT dropped silently: each concrete identifier is listed in
131+ a "not found" section with a hint to re-check the ids and retry the problematic ones.
132+ A ``found: true`` artifact with empty content is still shown (it was located).
133+ ``requested`` is the original identifier list; it backstops the diff so an id the
134+ backend never echoed back is still surfaced as not-found.
135+ """
107136 artifacts = data .get ("artifacts" , [])
108- if not artifacts :
109- msg = "No artifacts returned."
110- return msg + _data_source_miss_hint (data_source ) if data_source else msg
111137
112138 output = []
113139 count = 0
114140 has_any_relationships = False
141+ returned_identifiers = set ()
142+ not_found = []
115143
116144 for artifact in artifacts :
145+ identifier = artifact .get ("identifier" , "unknown" )
146+ returned_identifiers .add (identifier )
147+
117148 content = artifact .get ("content" )
118- if content is None :
149+ # Prefer the backend's explicit `found` flag; fall back to content-is-null for
150+ # older backends that don't emit it yet.
151+ found = artifact .get ("found" )
152+ is_missing = (found is False ) if found is not None else (content is None )
153+ if is_missing :
154+ not_found .append (identifier )
119155 continue
120156
121157 count += 1
122- identifier = artifact .get ("identifier" , "unknown" )
123158 content_byte_size = artifact .get ("contentByteSize" )
124159
125160 size_str = f" ({ content_byte_size } bytes)" if content_byte_size else ""
126161 output .append (f"\n { '=' * 60 } " )
127162 output .append (f"📄 { identifier } { size_str } " )
128163 output .append (f"{ '=' * 60 } " )
129164 start_line = artifact .get ("startLine" ) or 1
130- output .append (_add_line_numbers (content , start_line ))
165+ output .append (_add_line_numbers (content or "" , start_line ))
131166
132167 relationships = artifact .get ("relationships" )
133168 if relationships is not None :
@@ -138,11 +173,14 @@ def format_artifacts(data: dict, data_source: str = None) -> str:
138173 if _has_any_calls (relationships ):
139174 has_any_relationships = True
140175
141- if not output :
142- msg = "No artifacts found."
143- return msg + _data_source_miss_hint (data_source ) if data_source else msg
176+ # Backstop: any requested identifier the backend never echoed back is also missing.
177+ if requested :
178+ for identifier in requested :
179+ if identifier not in returned_identifiers and identifier not in not_found :
180+ not_found .append (identifier )
144181
145- output .append (f"\n ({ count } artifact(s))" )
182+ if count > 0 :
183+ output .append (f"\n ({ count } artifact(s))" )
146184
147185 if has_any_relationships :
148186 output .append (
@@ -154,6 +192,17 @@ def format_artifacts(data: dict, data_source: str = None) -> str:
154192 "[--profile callsOnly|inheritanceOnly|allRelevant|referencesOnly]"
155193 )
156194
195+ if not_found :
196+ output .extend (_not_found_lines (not_found ))
197+
198+ if count == 0 :
199+ # Nothing was actually fetched. Keep the data-source-specific recovery hint when a
200+ # selector was supplied; the not-found section above already lists the ids.
201+ if data_source :
202+ output .append (_data_source_miss_hint (data_source ))
203+ if not output :
204+ return "No artifacts returned."
205+
157206 return "\n " .join (output )
158207
159208
@@ -200,7 +249,7 @@ def main():
200249
201250 result = client .fetch_artifacts (identifiers = identifiers , data_source = data_source )
202251
203- print (format_artifacts (result , data_source = data_source ))
252+ print (format_artifacts (result , data_source = data_source , requested = identifiers ))
204253
205254 except Exception as e :
206255 print (f"❌ Error: { e } " , file = sys .stderr )
0 commit comments