diff --git a/utilities/clang-tidy/analyze.py b/utilities/clang-tidy/analyze.py
index d779fa32413f820edd5d3fc866b9f339c45acc00..fa1d768f8c7442794855f315ad9e890390df1247 100644
--- a/utilities/clang-tidy/analyze.py
+++ b/utilities/clang-tidy/analyze.py
@@ -38,6 +38,17 @@ def get_directory_filter(include_dirs: list[pathlib.Path]):
     return filter
 
 
+def remove_duplicate_commands(db: list):
+    seen_files: set[str] = set()
+
+    db_filtered = []
+    for x in db:
+        if x["file"] not in seen_files:
+            seen_files.add(x["file"])
+            db_filtered.append(x)
+    return db_filtered
+
+
 WARNING_PATTERN = re.compile(r"\[[a-z-]+,-warnings-as-errors\]\n")
 TRAILING = len(",-warnings-as-errors]\n")
 
@@ -119,7 +130,7 @@ def main():
         def run_clang_tidy(
             database: list,
             include_dirs: list[pathlib.Path],
-            header_filter: str,
+            header_filter: str | None,
             output: pathlib.Path,
             info: str,
         ):
@@ -135,12 +146,16 @@ def main():
             for x in cc_filtered:
                 x["command"] = removePrecompiler(x["command"])
 
+            cc_filtered = remove_duplicate_commands(cc_filtered)
+
             print(f"  -- Retained {len(cc_filtered)} compile commands")
 
             with database_fp.open("w") as db_out:
                 json.dump(cc_filtered, db_out)
 
-            args = clang_tidy_args + ["-header-filter", header_filter]
+            args = clang_tidy_args
+            if header_filter:
+                args += ["-header-filter", header_filter]
             output.parent.mkdir(exist_ok=True, parents=True)
 
             errfile = output.with_name(output.name + ".err")
@@ -179,6 +194,30 @@ def main():
                 output_dir / "modules" / f"{module_name}.out",
                 f"module {module_name}",
             )
+
+        for app_spec in params.get("apps", []):
+            include_paths: list[pathlib.Path]
+            app_name: str
+
+            match app_spec:
+                case str():
+                    app_name = app_spec
+                    include_paths = [apps_dir / app_name]
+                case dict():
+                    app_name, settings = next(iter(app_spec.items()))
+                    if (only := settings.get("only", None)) is not None:
+                        include_paths = [apps_dir / app_name / o for o in only]
+                    else:
+                        include_paths = [apps_dir / app_name]
+
+            run_clang_tidy(
+                orig_db,
+                include_paths,
+                None,
+                output_dir / "apps" / f"{app_name}.out",
+                f"application {app_name}"
+            )
+
     finally:
         #   Restore the backup
         shutil.move(str(database_backup), str(database_fp))
diff --git a/utilities/clang-tidy/analyze.yml b/utilities/clang-tidy/analyze.yml
index b87477d768d288a08e2d583e997cdad68396de50..fd93fa20c83202aa7d8cd1b9e8b79ebfcceb7157 100644
--- a/utilities/clang-tidy/analyze.yml
+++ b/utilities/clang-tidy/analyze.yml
@@ -18,3 +18,10 @@ modules:
   - timeloop
   - vtk:
       exclude-tests: true
+
+apps:
+  - "benchmarks/CouetteFlow"
+  - "benchmarks/PoiseuilleFlow"
+  - "benchmarks/UniformGridCPU"
+  - "benchmarks/NonUniformGridCPU"
+  # to be extended