From 0f275cfee730cc5aa0e9cf2fe05f616e76ca3d5f Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 15:21:24 -0700 Subject: [PATCH] feat(editor): add --info-cli-stats meta command for surface tracking MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 130 commands and counting — surface inspection has graduated from 'nice to have' to 'necessary to plan'. This drops a per-category breakdown: wowee_editor --info-cli-stats CLI surface stats total commands : 130 longest flag : 24 chars Categories (by verb prefix, sorted by count): --info 33 --export 15 --list 12 --validate 11 --diff 8 --add 6 --remove 5 --convert 5 --bake 3 --migrate 3 --clone 3 ... Pulls the command list via the same printUsage-parser as --list-commands so it auto-tracks new flags. Buckets by the verb prefix (text between '--' and the next '-') so '--info-zone-tree' counts under 'info'. JSON mode emits the histogram as an object for dashboard consumption. Useful for spotting category imbalances ('we have 33 inspectors but only 5 add commands — should consider more authoring CRUD?'), tracking growth over releases, and planning where to invest. --- tools/editor/main.cpp | 70 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index 8219eeea..14407875 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -645,6 +645,8 @@ static void printUsage(const char* argv0) { std::printf(" --pack-wcp [dst] Pack a zone dir/name into a .wcp archive and exit\n"); std::printf(" --unpack-wcp [dst] Extract a WCP archive (default dst=custom_zones/) and exit\n"); std::printf(" --list-commands Print every recognized --flag, one per line, and exit\n"); + std::printf(" --info-cli-stats [--json]\n"); + std::printf(" Meta-stats on the CLI surface (command count by category prefix)\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"); @@ -10535,6 +10537,74 @@ int main(int argc, char* argv[]) { commands.insert("--version"); for (const auto& c : commands) std::printf("%s\n", c.c_str()); return 0; + } else if (std::strcmp(argv[i], "--info-cli-stats") == 0) { + // Meta-stats on the CLI surface: total command count + per- + // category breakdown by prefix verb (--info-*, --validate-*, + // --diff-*, etc.). Useful for tracking growth over time and + // spotting category imbalances. + bool jsonOut = (i + 1 < argc && + std::strcmp(argv[i + 1], "--json") == 0); + if (jsonOut) i++; + // Re-use --list-commands' parser. Capture printUsage stdout. + FILE* old = stdout; + FILE* tmp = std::tmpfile(); + if (!tmp) { std::fprintf(stderr, "info-cli-stats: tmpfile failed\n"); return 1; } + stdout = tmp; + printUsage(argv[0]); + stdout = old; + std::fseek(tmp, 0, SEEK_SET); + std::set commands; + char line[512]; + while (std::fgets(line, sizeof(line), tmp)) { + const char* p = line; + while (*p == ' ' || *p == '\t') ++p; + if (p[0] != '-' || p[1] != '-') continue; + std::string flag; + while (*p && (std::isalnum(static_cast(*p)) || + *p == '-' || *p == '_')) { flag += *p++; } + if (flag.size() > 2) commands.insert(flag); + } + std::fclose(tmp); + commands.insert("--help"); + commands.insert("--version"); + // Bucket by category — verb is the second token after '--', + // up to the next dash. So '--info-zone-tree' -> 'info'. + std::map byCategory; + int maxLen = 0; + for (const auto& c : commands) { + if (static_cast(c.size()) > maxLen) maxLen = static_cast(c.size()); + size_t verbStart = 2; // skip '--' + size_t verbEnd = c.find('-', verbStart); + std::string verb = (verbEnd == std::string::npos) + ? c.substr(verbStart) + : c.substr(verbStart, verbEnd - verbStart); + byCategory[verb]++; + } + if (jsonOut) { + nlohmann::json j; + j["totalCommands"] = commands.size(); + j["maxFlagLength"] = maxLen; + nlohmann::json cats = nlohmann::json::object(); + for (const auto& [v, c] : byCategory) cats[v] = c; + j["byCategory"] = cats; + std::printf("%s\n", j.dump(2).c_str()); + return 0; + } + std::printf("CLI surface stats\n"); + std::printf(" total commands : %zu\n", commands.size()); + std::printf(" longest flag : %d chars\n", maxLen); + std::printf("\n Categories (by verb prefix, sorted by count):\n"); + // Sort by count descending for the table. + std::vector> sorted( + byCategory.begin(), byCategory.end()); + std::sort(sorted.begin(), sorted.end(), + [](const auto& a, const auto& b) { + return a.second > b.second; + }); + for (const auto& [verb, count] : sorted) { + std::printf(" --%-12s %4d\n", verb.c_str(), count); + } + return 0; } 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