3838}
3939
4040ANSI_RE = re .compile (r"\x1B\[[0-?]*[ -/]*[@-~]" )
41+ _FLAG_PATTERN = re .compile (r"(?<![\w-])(-{1,2}[A-Za-z0-9][A-Za-z0-9-]*)(?![\w-])" )
4142
4243
4344def _pdf_escape (text : str ) -> str :
@@ -572,11 +573,30 @@ def load_questions(path: Path, *, require_answers: bool) -> list[Question]:
572573 return questions
573574
574575
576+ def script_base_command (script : Path ) -> list [str ]:
577+ """Return the appropriate Python invocation for a script path."""
578+
579+ script = Path (script )
580+ repo_root = Path (__file__ ).resolve ().parent
581+ try :
582+ relative = script .relative_to (repo_root )
583+ except ValueError :
584+ return [sys .executable , str (script )]
585+
586+ if relative .parts and relative .parts [0 ] == "src" :
587+ module = "." .join (relative .with_suffix ("" ).parts )
588+ return [sys .executable , "-m" , module ]
589+
590+ return [sys .executable , str (script )]
591+
592+ def _advertised_flags (help_text : str ) -> set [str ]:
593+ return {match .group (1 ) for match in _FLAG_PATTERN .finditer (help_text )}
594+
575595@lru_cache (maxsize = None )
576596def script_help_text (script : Path ) -> str :
577597 try :
578598 proc = subprocess .run (
579- [sys . executable , str (script ), "--help" ],
599+ [* script_base_command (script ), "--help" ],
580600 capture_output = True ,
581601 text = True ,
582602 timeout = 10 ,
@@ -589,9 +609,14 @@ def script_help_text(script: Path) -> str:
589609
590610def determine_flag (script : Path , candidates : Sequence [str ]) -> str | None :
591611 help_text = script_help_text (script )
612+ advertised = _advertised_flags (help_text )
613+ try :
614+ source = script .read_text (encoding = "utf-8" )
615+ except (OSError , UnicodeDecodeError ):
616+ return None
592617 for flag in candidates :
593- if flag and flag in help_text :
594- return flag
618+ if flag and flag in source :
619+ return flag
595620 return None
596621
597622
@@ -601,8 +626,9 @@ def _script_supports_flag(
601626 """Return the first flag advertised by the script's help output."""
602627
603628 help_text = script_help_text (script )
629+ advertised = _advertised_flags (help_text )
604630 for flag in flag_candidates :
605- if flag and flag in help_text :
631+ if flag and flag in advertised :
606632 return flag
607633 return None
608634
@@ -615,14 +641,38 @@ def build_question_command(
615641 If the script advertises a question flag, use it; otherwise pass the prompt positionally.
616642 """
617643
618- argv : List [str ] = [sys .executable , str (script ), * base_args ]
644+
645+ argv : List [str ] = [* script_base_command (script ), * base_args ]
619646 flag = _script_supports_flag (script , ["--question" , "-q" ])
647+
620648 if flag :
621649 argv .extend ([flag , prompt ])
622650 else :
623651 argv .append (prompt )
624652 return argv
625653
654+
655+ def build_builder_command (
656+ * ,
657+ builder : Path ,
658+ index_key : str ,
659+ pdf_dir : Path ,
660+ chunks_dir : Path ,
661+ index_dir : Path ,
662+ ) -> list [str ]:
663+ """Construct the lc_build_index command for the verification run."""
664+
665+ return [
666+ * script_base_command (builder ),
667+ index_key ,
668+ "--input-dir" ,
669+ str (pdf_dir ),
670+ "--chunks-dir" ,
671+ str (chunks_dir ),
672+ "--index-dir" ,
673+ str (index_dir ),
674+ ]
675+
626676
627677def build_builder_command (
628678 * ,
@@ -635,8 +685,7 @@ def build_builder_command(
635685 """Construct the lc_build_index command for the verification run."""
636686
637687 return [
638- sys .executable ,
639- str (builder ),
688+ * script_base_command (builder ),
640689 index_key ,
641690 "--input-dir" ,
642691 str (pdf_dir ),
@@ -834,20 +883,24 @@ def build_question_invocation(
834883 if script is None :
835884 raise RuntimeError ("No asker script available" )
836885 route = "multi" if use_multi else "asker"
886+ def _append_flag (script_path : Path , args : list [str ], candidates : list [str ], value : str ) -> None :
887+ flag = determine_flag (script_path , candidates )
888+ if not flag :
889+ # Typer-based CLIs such as multi_agent.py require running via ``-m`` to
890+ # expose subcommand help. When invoked as a script from the harness the
891+ # ``--help`` probe used by ``determine_flag`` fails, so fall back to the
892+ # primary candidate.
893+ flag = candidates [0 ]
894+ args .extend ([flag , value ])
895+
837896 if not use_multi :
838897 base_args : List [str ] = []
839- key_flag = determine_flag (asker , ["--key" ]) or "--key"
840- if key_flag :
841- base_args .extend ([key_flag , index_key ])
842- index_flag = determine_flag (asker , ["--index-dir" , "--index" ]) or "--index-dir"
843- if index_flag :
844- base_args .extend ([index_flag , str (index_dir )])
845- chunks_flag = determine_flag (asker , ["--chunks-dir" ]) or "--chunks-dir"
846- if chunks_flag :
847- base_args .extend ([chunks_flag , str (chunks_dir )])
848- embed_flag = determine_flag (asker , ["--embed-model" ]) or "--embed-model"
849- if embed_flag :
850- base_args .extend ([embed_flag , embed_model ])
898+
899+ _append_flag (asker , base_args , ["--key" ], index_key )
900+ _append_flag (asker , base_args , ["--index-dir" , "--index" ], str (index_dir ))
901+ _append_flag (asker , base_args , ["--chunks-dir" ], str (chunks_dir ))
902+ _append_flag (asker , base_args , ["--embed-model" ], embed_model )
903+
851904 command = build_question_command (asker , question .prompt , base_args )
852905 docs_flag = determine_flag (asker , ["--docs" , "--doc" , "--gold" ])
853906 if docs_flag :
@@ -858,12 +911,10 @@ def build_question_invocation(
858911 command .extend ([topk_flag , str (topk )])
859912 else :
860913 base_args : list [str ] = []
861- key_flag = determine_flag (multi , ["--key" , "-k" ]) or "--key"
862- if key_flag :
863- base_args .extend ([key_flag , index_key ])
864- index_flag = determine_flag (multi , ["--index-dir" , "--index" ]) or "--index-dir"
865- if index_flag :
866- base_args .extend ([index_flag , str (index_dir )])
914+
915+ _append_flag (multi , base_args , ["--key" , "-k" ], index_key )
916+ _append_flag (multi , base_args , ["--index-dir" , "--index" ], str (index_dir ))
917+
867918 command = build_question_command (script , question .prompt , base_args )
868919 if question .clarify :
869920 clarify_flag = determine_flag (
0 commit comments