module Noir::OptionsParsing

Overview

CLI option parsing: the legacy-alias rewrites, the --set-pvalue / --include / --ai-context target tables, and the helpers that apply them to the options hash.

These were eight top-level defs and five SCREAMING constants leaking into every compilation unit — INCLUDE_TARGETS and AI_CONTEXT_FEATURES under names generic enough to collide. run_options_parser stays top-level: it is the CLI's entry point, same as detect_techs and analysis_endpoints on the scan side.

Extended Modules

Defined in:

options.cr

Constant Summary

AI_CONTEXT_FEATURES = NoirAIContext::ACCEPTED_FEATURES

--ai-context accepts an optional comma-separated feature list. Crystal's OptionParser cannot express "optional positional value", so we rewrite the few well-defined ambiguous forms upfront:

--ai-context → --ai-context= (bare, all features) --ai-context=guards,sinks → unchanged (explicit value) --ai-context guards,sinks → --ai-context=guards,sinks (heuristic) --ai-context ./app → --ai-context= (next token is a path)

The heuristic for "is the next token a feature list?": lowercase words joined by commas, where either (a) there's more than one comma-separated word, or (b) the single word matches the fixed vocabulary below exactly. A real filesystem path essentially never contains a literal comma, so any multi-word comma list — typo'd feature names included — is routed to --ai-context=... and left for the vocabulary check in #apply_ai_context to reject with a precise "unknown feature" error, rather than silently falling through to "Base path does not exist: ". A single bare word still has to match a known feature exactly, since that shape is genuinely ambiguous with a real one-word directory name (noir scan --ai-context myapp must keep scanning myapp). Both this and the flag's own validator used to spell the vocabulary out by hand, and both omitted sources.

INCLUDE_TARGETS = {"path" => "include_path", "techs" => "include_techs", "callee" => "include_callee"}
LEGACY_INCLUDE_TARGETS = {"--include-path" => "include_path", "--include-techs" => "include_techs", "--include-callee" => "include_callee"}
LEGACY_PVALUE_TARGETS = {"--set-pvalue" => "set_pvalue", "--set-pvalue-header" => "set_pvalue_header", "--set-pvalue-cookie" => "set_pvalue_cookie", "--set-pvalue-query" => "set_pvalue_query", "--set-pvalue-form" => "set_pvalue_form", "--set-pvalue-json" => "set_pvalue_json", "--set-pvalue-path" => "set_pvalue_path"}

Silently translate v0 flag spellings to their v1 storage. Keeping the work here (instead of parser.on "--include-path") means the legacy names no longer clutter noir scan -h, while every v0 script keeps working untouched in v1.x.

PVALUE_TYPE_KEYS = {"any" => "set_pvalue", "all" => "set_pvalue", "header" => "set_pvalue_header", "cookie" => "set_pvalue_cookie", "query" => "set_pvalue_query", "form" => "set_pvalue_form", "json" => "set_pvalue_json", "path" => "set_pvalue_path"}

Split TYPE=VAL (or bare VAL → all-types) and route it into the right slot in noir_options. --pvalue may be repeated to set multiple values across different types.

Instance Method Summary

Instance Method Detail

def apply_ai_context(noir_options : Hash(String, YAML::Any), spec : String) #

--ai-context[=LIST] always enables AI context output. An empty LIST means "every category"; a non-empty LIST narrows the output to the named categories.


[View source]
def apply_include_list(noir_options : Hash(String, YAML::Any), spec : String) #

[View source]
def extract_hidden_prompt_flags(noir_options : Hash(String, YAML::Any)) : Array(String) #

[View source]
def extract_legacy_aliases(args : Array(String), noir_options : Hash(String, YAML::Any)) : Array(String) #

[View source]
def handle_pvalue(noir_options : Hash(String, YAML::Any), spec : String) #

[View source]
def normalize_ai_context_flag(args : Array(String)) : Array(String) #

[View source]
def validate_ai_context_features(spec : String, origin : String) #

Rejects AI-context bucket names outside the accepted vocabulary. origin names the source in the error so a config-file typo doesn't read as a command-line one.


[View source]