Reviewers should probably either review the entire result of running the scripted diff or review the script itself thoroughly.
Made an attempt at reviewing the script. Leaned on mypy contrib/devtools/reg-settings.py --check-untyped-defs and ruff check to help elucidate types and find issues.
  0diff --git a/contrib/devtools/reg-settings.py b/contrib/devtools/reg-settings.py
  1index f006fd6b1f..40d5ff07af 100644
  2--- a/contrib/devtools/reg-settings.py
  3+++ b/contrib/devtools/reg-settings.py
  4@@ -37,7 +37,7 @@ DefaultValue = str | bool | None
  5 class SettingType:
  6     name: str
  7     primary: bool = False
  8-    defaults: set[str | None] = field(default_factory=set)
  9+    defaults: set[str | bool | None] = field(default_factory=set)
 10     default_value: DefaultValue = False
 11     includes: set[str] = field(default_factory=set)
 12     """Include paths needed in setting file for default expressions"""
 13@@ -90,22 +90,26 @@ class Setting: [@dataclass](/bitcoin-bitcoin/contributor/dataclass/)
 14 class Range:
 15     start: int
 16-    end: int
 17+    end: int | None
 18 
 19-def get_files_with_args(src_dir):
 20+def get_files_with_args(src_dir: str) -> list[str]:
 21     # Run git grep to find files containing AddArg/GetArg/GetIntArg/GetBoolArg/GetArgs
 22     result = subprocess.run(
 23         [
 24-            "git", "grep", "-l", "AddArg(\|AddHiddenArgs(\|GetArg(\|GetPathArg(\|GetIntArg(\|GetBoolArg(\|GetArgs(\|IsArgSet(\|IsArgNegated(", "--", src_dir
 25+            "git", "grep", "-l", r"AddArg(\|AddHiddenArgs(\|GetArg(\|GetPathArg(\|GetIntArg(\|GetBoolArg(\|GetArgs(\|IsArgSet(\|IsArgNegated(", "--", src_dir
 26         ],
 27         capture_output=True,
 28         text=True
 29     )
 30     return result.stdout.splitlines()
 31 
 32-def parse_function_args(arg_str):
 33-    args = []
 34-    stack = []
 35+# Converts something like:
 36+#   '("-foo", strprintf("helptext %i", DEFAULT), ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION, OptionsCategory::OPTIONS)'
 37+# into:
 38+#   (125, ['"-foo"', ' strprintf("helptext %i", DEFAULT', ' ArgsManager::ALLOW_ANY | ArgsManager::DISALLOW_NEGATION', ' OptionsCategory::OPTIONS'])
 39+def parse_function_args(arg_str: str) -> tuple[int, list[str]]:
 40+    args: list[str] = []
 41+    stack: list[str] = []
 42     quot = False
 43     for pos, c in enumerate(arg_str):
 44         if c == '"':
 45@@ -128,26 +132,28 @@ def parse_function_args(arg_str):
 46         args[-1] += c
 47     return pos, args
 48 
 49-def parse_calls(file_path):
 50-    adds = []
 51-    gets = []
 52+def parse_calls(file_path: str) -> tuple[list[AddArg], list[GetArg]]:
 53+    adds: list[AddArg] = []
 54+    gets: list[GetArg] = []
 55     context = get_file_context(file_path)
 56     namespace = get_file_namespace(file_path)
 57     with open(file_path, 'r') as f:
 58         content = f.read()
 59         for match in re.finditer(r'\b(\w+)\.AddArg\((")', content):
 60             call_len, (summary, help_text, flags, category) = parse_function_args(content[match.start(2)-1:])
 61+            arg_name_match = re.match(r'"([^"=(]+).*', summary)
 62+            assert arg_name_match
 63             call = Call(
 64                 file=file_path,
 65                 position=match.start(),
 66                 call_text=content[match.start():match.start(2)+call_len],
 67                 obj_name=match.group(1),
 68-                arg_name=re.match(r'"([^"=(]+).*', summary).group(1),
 69+                arg_name=arg_name_match.group(1),
 70                 context=context,
 71                 namespace=namespace,
 72             )
 73             help_text=help_text.strip()
 74-            help_args = []
 75+            help_args: list[str] = []
 76             if m := re.match(r"strprintf\(", help_text):
 77                 _, help_args = parse_function_args(help_text[m.end()-1:])
 78                 help_text = help_args[0].strip()
 79@@ -162,17 +168,18 @@ def parse_calls(file_path):
 80             ))
 81         for match in re.finditer(r'( *)\b(\w+)\.AddHiddenArgs\((\{)', content):
 82             call_len, call_args = parse_function_args(content[match.start(3):])
 83-            hidden_args = []
 84+            hidden_args: list[AddArg] = []
 85             for summary in call_args:
 86                 summary = summary.strip()
 87                 if not summary: continue
 88-                call_text=content[match.start():match.start(3)+call_len+1]
 89+                arg_name_match = re.match(r'"([^"=(]+).*', summary)
 90+                assert arg_name_match
 91                 call = Call(
 92                     file=file_path,
 93                     position=match.start(),
 94                     call_text=content[match.start():match.start(3)+call_len+2],
 95                     obj_name=match.group(2),
 96-                    arg_name=re.match(r'"([^"=(]+).*', summary).group(1),
 97+                    arg_name=arg_name_match.group(1),
 98                     context=context,
 99                     namespace=namespace,
100                 )
101@@ -215,6 +222,7 @@ def parse_calls(file_path):
102                          DataType.DISABLED if function_name == "IsArgNegated" else
103                          DataType.UNSET if function_name == "IsArgSet" else
104                          None)
105+            assert data_type
106             default_arg = call_args[1].strip() if len(call_args) > 1 else None
107             default_value = (
108                 True if data_type == DataType.STRING and default_arg == '""' else
109@@ -227,7 +235,7 @@ def parse_calls(file_path):
110             gets.append(GetArg(call=call, function_name=function_name, data_type=data_type, default_value=default_value, nots=len(match.group(1))))
111     return adds, gets
112 
113-def make_setting(settings, call):
114+def make_setting(settings: dict[str, Setting], call: Call) -> Setting:
115     name = call.arg_name.lstrip("-")
116     if name in settings:
117         setting = settings[name]
118@@ -235,7 +243,7 @@ def make_setting(settings, call):
119         setting = settings[name] = Setting(name)
120     return setting
121 
122-def flags_to_options(flag_str):
123+def flags_to_options(flag_str: str) -> list[str]:
124     flags = set()
125     for flag in flag_str.split("|"):
126         flag = flag.strip()
127@@ -263,9 +271,9 @@ def flags_to_options(flag_str):
128         raise Exception(f"Unknown flags {flags!r}")
129     return options
130 
131-def collect_argument_information(src_dir):
132+def collect_argument_information(src_dir: str) -> dict[str, Setting]:
133     files = get_files_with_args(src_dir)
134-    settings: Dict[str, Setting] = {}
135+    settings: dict[str, Setting] = {}
136     for file in files:
137         adds, gets = parse_calls(file)
138         for add in adds:
139@@ -290,7 +298,7 @@ def collect_argument_information(src_dir):
140             for get in setting.gets:
141                 if get.add is None and same_context(add, get):
142                     get.add = add
143-        fake_adds = {} # map of (file path, arg name) -> AddArg
144+        fake_adds: dict[tuple[str, str], AddArg] = {} # map of (file path, arg name) -> AddArg
145         for get in setting.gets:
146             if get.add is None and get.call.arg_name:
147                 key = get.call.file, get.call.arg_name
148@@ -303,7 +311,7 @@ def collect_argument_information(src_dir):
149 
150         # Figure out setting types and default values.
151         setting_name = ''.join(NAME_MAP.get(word) or word.capitalize() for word in arg_name.split('-')) + "Setting"
152-        counter = collections.Counter()
153+        counter: collections.Counter = collections.Counter()
154         for add in setting.adds:
155             add.include_path = add.call.file.replace(".cpp", "_settings.h")
156             add.rel_include_path = re.sub("^src/", "", add.include_path)
157@@ -387,7 +395,7 @@ def collect_argument_information(src_dir):
158 
159     return settings
160 
161-def setting_definitions(add):
162+def setting_definitions(add: AddArg) -> tuple[list[str], set[str]]:
163     defs = []
164     includes = set()
165     # If this is hidden argument and there is non-hidden argument with the same
166@@ -405,6 +413,7 @@ def setting_definitions(add):
167                       "common::Disabled" if data_type == DataType.DISABLED else
168                       "common::Unset" if data_type == DataType.UNSET else
169                       None)
170+            assert ctype
171             if None in setting_type.defaults:
172                 ctype = f"std::optional<{ctype}>"
173             help_str = ""
174@@ -447,26 +456,28 @@ def setting_definitions(add):
175             defs.append(f"\nusing {setting_type.name} = common::Setting<\n    {add.summary}, {ctype}, {options_str}{help_str}>{extra};\n")
176     return defs, includes
177 
178-def addarg_replacement(add):
179+def addarg_replacement(add: AddArg) -> str:
180     new_call = ""
181     if add.hidden_args is None:
182         new_call = register_str(add)
183     else:
184         for hadd in add.hidden_args:
185             if new_call: new_call += ";\n"
186-            spaces=re.match(" *", hadd.call.call_text).group()
187+            hidden_match = re.match(" *", hadd.call.call_text)
188+            assert hidden_match
189+            spaces=hidden_match.group()
190             new_call += f"{spaces}{register_str(hadd.nonhidden_arg or hadd, hidden=hadd.nonhidden_arg is not None)}"
191     return new_call
192 
193-def arg_name(add):
194+def arg_name(add: AddArg) -> str:
195     default_data_type = min(add.data_types.keys())
196     return add.data_types[default_data_type].name
197 
198-def register_str(add, hidden=False):
199+def register_str(add: AddArg, hidden: bool=False) -> str:
200     register_args = ", ".join([add.call.obj_name] + add.extern_args)
201     return f"{arg_name(add)}{'::Hidden' if hidden else ''}::Register({register_args})"
202 
203-def getarg_replacement(get):
204+def getarg_replacement(get: GetArg) -> str:
205     if get.data_type == DataType.UNSET:
206         method = "Value"
207         suffix = ".isNull()"
208@@ -476,6 +487,7 @@ def getarg_replacement(get):
209     else:
210         method = "Get"
211         suffix = ""
212+    assert get.add
213     setting_type = get.add.data_types.get(get.data_type) or get.add.data_types[min(get.add.data_types.keys())]
214     default_arg = ""
215     if get.default_value and not setting_type.default_value:
216@@ -490,7 +502,7 @@ def getarg_replacement(get):
217     new_call += f"{setting_type.name}::{method}({get.call.obj_name}{default_arg}){suffix}"
218     return new_call
219 
220-def add_to_file(file_path, local_include, system_include=(), defs=()):
221+def add_to_file(file_path: str, local_include: list[str], system_include: list[str]=[], defs: list[str]=[]):
222     if os.path.exists(file_path):
223         with open(file_path, 'r') as f:
224             lines = f.readlines()
225@@ -510,17 +522,16 @@ def add_to_file(file_path, local_include, system_include=(), defs=()):
226         lines.extend(["\n", f"#endif // {guard}\n"])
227 
228     # Identify include blocks and their positions
229-    blocks = []
230+    blocks: list[Range] = []
231     self_include = f"#include <{file_path.replace('src/', '').replace('.cpp', '.h')}>"
232-    first = last = self = None
233+    first = last = None
234     for i, line in enumerate(lines):
235         if line.startswith('#include') and "IWYU pragma: keep" not in line and not line.startswith(self_include):
236             if not blocks or blocks[-1].end is not None:
237                 blocks.append(Range(i, None))
238         elif blocks and blocks[-1].end is None:
239             blocks[-1].end = i
240-        elif line.startswith('#include'):
241-            self = True
242+
243         if first is None and not line.startswith("//") and not line.startswith("#ifndef") and not line.startswith("#define") and line != "\n":
244             first = i
245         if line != "\n" and not line.startswith("#endif") and not line.startswith("} // namespace "):
246@@ -528,6 +539,8 @@ def add_to_file(file_path, local_include, system_include=(), defs=()):
247 
248     if len(blocks) == 0:
249         # If there are no include blocks, add an empty one where includes should go.
250+        assert first
251+        assert last
252         m = min(first, last)
253         blocks.append(Range(m, m))
254     if len(blocks) == 1:
255@@ -535,6 +548,7 @@ def add_to_file(file_path, local_include, system_include=(), defs=()):
256         # local or system includes, but reasonably heuristic is to assume it
257         # contains local includes in .cpp files and system includes in .h files.
258         if file_path.endswith(".cpp"):
259+            assert blocks[0].end
260             blocks.append(Range(blocks[0].end, blocks[0].end))
261         else:
262             blocks.insert(0, Range(blocks[0].start, blocks[0].start))
263@@ -558,7 +572,7 @@ def add_to_file(file_path, local_include, system_include=(), defs=()):
264     with open(file_path, 'w') as f:
265         f.writelines(lines)
266 
267-def replace_in_file(file_path, old, new, replacements=None):
268+def replace_in_file(file_path: str, old: str, new: str, replacements: list[tuple[str, str]] | None=None):
269     with open(file_path, 'r') as f:
270         content = f.read()
271     if replacements is not None:
272@@ -572,29 +586,33 @@ def replace_in_file(file_path, old, new, replacements=None):
273     with open(file_path, 'w') as f:
274         f.write(new_content)
275 
276-def modify_source_files(settings, git_commit=False):
277+def modify_source_files(settings: dict[str, Setting], git_commit: bool=False):
278     # map file path->list of (old, new) tuples with GetArg->Get replacements made so far
279-    replacements = collections.defaultdict(list)
280+    replacements: dict[str, list[tuple[str, str]]] = collections.defaultdict(list)
281 
282     for setting in settings.values():
283         for add in setting.adds:
284             if not add.fake:
285                 replace_in_file(add.call.file, add.call.call_text, addarg_replacement(add))
286+            assert add.rel_include_path
287             add_to_file(add.call.file, [add.rel_include_path])
288 
289             for get in setting.gets:
290                 if get.add is add:
291                     replace_in_file(get.call.file, get.call.call_text, getarg_replacement(get),
292                                     replacements[get.call.file])
293+                    assert get.add.rel_include_path
294                     add_to_file(get.call.file, [get.add.rel_include_path])
295 
296             defs, def_includes = setting_definitions(add)
297             if defs:
298+                assert add.include_path
299                 add_to_file(add.include_path,
300                             [include for include in def_includes | {"common/setting.h"}],
301                             ["string", "vector"], defs)
302 
303             if git_commit and subprocess.run(["git", "status", "--porcelain", "--untracked-files=no"], stdout=subprocess.PIPE, check=True).stdout:
304+                assert add.include_path
305                 subprocess.run(["git", "add", "-N", add.include_path], check=True)
306                 msg = f"{add.rel_include_path}: Add {arg_name(add.nonhidden_arg or add)}"
307                 subprocess.run(["git", "commit", "-am", msg], check=True)
308@@ -644,7 +662,6 @@ ARG_PATTERNS = {
309     "DEFAULT_ASMAP_FILENAME": PatternOptions(include_path="init.h", runtime=True),
310     "DEFAULT_AVOIDPARTIALSPENDS": PatternOptions(include_path="wallet/coincontrol.h", runtime=True),
311     "DEFAULT_BENCH_FILTER": PatternOptions(runtime=True),
312-    "DEFAULT_BLOCKFILTERINDEX": PatternOptions(include_path="index/blockfilterindex.h"),
313     "DEFAULT_BLOCKFILTERINDEX": PatternOptions(include_path="index/blockfilterindex.h", runtime=True),
314     "DEFAULT_BLOCK_RECONSTRUCTION_EXTRA_TXN": PatternOptions(include_path="net_processing.h"),
315     "DEFAULT_CHOOSE_DATADIR": PatternOptions(include_path="qt/intro.h"),