From 1eb8232bb85de2362b62cb456ebcf8ace56969c1 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 17:04:57 -0700 Subject: [PATCH] feat(editor): add --validate-cli-help self-check for documentation drift MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Self-check that every flag in kArgRequired (the master list of commands needing positional args) appears in the help text emitted by printUsage. Catches drift where a handler+arg-check pair gets added but the help line is forgotten: wowee_editor --validate-cli-help CLI help self-check kArgRequired entries : 126 PASSED — every kArgRequired flag is documented If it ever fails, the missing flags are listed: FAILED — 2 flag(s) missing from help text: - --new-thing-i-forgot-to-document - --another-undocumented-flag CI integration: add this to the test target so a PR that adds a new command without docs can't merge. The --info-cli-stats command gives the surface-size view; this gives the surface-completeness view. Verified: ran on the current binary (126 kArgRequired entries), PASSED — every flag is documented. --- tools/editor/main.cpp | 47 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index a1f75401..95f56101 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -760,6 +760,8 @@ static void printUsage(const char* argv0) { std::printf(" Meta-stats on the CLI surface (command count by category prefix)\n"); std::printf(" --info-cli-help \n"); std::printf(" Substring-search the help text and print matching command lines\n"); + std::printf(" --validate-cli-help [--json]\n"); + std::printf(" Self-check: every kArgRequired flag must appear in the help text\n"); std::printf(" --gen-completion \n"); std::printf(" Print a shell-completion script for wowee_editor (source it from your rc file)\n"); std::printf(" --version Show version and format info\n\n"); @@ -12067,6 +12069,51 @@ int main(int argc, char* argv[]) { } std::fprintf(stderr, "\n%d line(s) matched '%s'\n", matches, pattern.c_str()); return 0; + } else if (std::strcmp(argv[i], "--validate-cli-help") == 0) { + // Self-check: every flag we declare in kArgRequired (the list + // of commands needing positional args) must appear in the + // help text printUsage emits. Catches drift where someone + // adds a handler + argument check but forgets the help line. + bool jsonOut = (i + 1 < argc && + std::strcmp(argv[i + 1], "--json") == 0); + if (jsonOut) i++; + // Capture printUsage's stdout. + FILE* old = stdout; + FILE* tmp = std::tmpfile(); + if (!tmp) { std::fprintf(stderr, "validate-cli-help: tmpfile failed\n"); return 1; } + stdout = tmp; + printUsage(argv[0]); + stdout = old; + std::fseek(tmp, 0, SEEK_SET); + std::string helpText; + char chunk[1024]; + while (std::fgets(chunk, sizeof(chunk), tmp)) helpText += chunk; + std::fclose(tmp); + // Walk kArgRequired and check each appears in the help. + std::vector missing; + for (const char* opt : kArgRequired) { + if (helpText.find(opt) == std::string::npos) { + missing.push_back(opt); + } + } + if (jsonOut) { + nlohmann::json j; + j["totalArgRequired"] = sizeof(kArgRequired) / sizeof(kArgRequired[0]); + j["missing"] = missing; + j["passed"] = missing.empty(); + std::printf("%s\n", j.dump(2).c_str()); + return missing.empty() ? 0 : 1; + } + std::printf("CLI help self-check\n"); + std::printf(" kArgRequired entries : %zu\n", + sizeof(kArgRequired) / sizeof(kArgRequired[0])); + if (missing.empty()) { + std::printf(" PASSED — every kArgRequired flag is documented\n"); + return 0; + } + std::printf(" FAILED — %zu flag(s) missing from help text:\n", missing.size()); + for (const auto& m : missing) std::printf(" - %s\n", m.c_str()); + return 1; } else if (std::strcmp(argv[i], "--gen-completion") == 0 && i + 1 < argc) { // Emit a bash or zsh completion script. Re-execs the editor's // own --list-commands at completion time so newly-added flags