Skip to main content

add-cli-command

Add a new subcommand to the Memory Snapshot Data Tool CLI (the .NET 10 `MemorySnapshotDataTools` exe), wiring it through CliOptions → CommandLineBuilder → Program → a Core runner, then documenting it in README.md. Use when asked to add, create, or expose a new CLI command/subcommand (alongside export, batch-export, report, multi-report, validate, summary, upgrade), or a new flag/argument on an existing one.

Quellinformationen

Repository
Unity-Technologies/MemorySnapshotDataTools
Letzte Quellaktivität
5. Juni 2026 um 18:52
Erkannte Sprache von SKILL.md
Englisch
Sterne
11
Forks
1

Installationsoptionen

Standardmäßig ist der Prompt ausgewählt, der zuerst die Quelle prüft. Sie können zu einem direkten Befehl wechseln oder eine lokale Kopie herunterladen.

Quelldateien prüfen

Lesen Sie SKILL.md und alle von SkillsMP angezeigten Begleitdateien, bevor Sie sich für eine Installation entscheiden.

SKILL.md wird angezeigt

SKILL.md
Quellanweisungen · Schreibgeschützte Vorschau
name
add-cli-command
description
Add a new subcommand to the Memory Snapshot Data Tool CLI (the .NET 10 `MemorySnapshotDataTools` exe), wiring it through CliOptions → CommandLineBuilder → Program → a Core runner, then documenting it in README.md. Use when asked to add, create, or expose a new CLI command/subcommand (alongside export, batch-export, report, multi-report, validate, summary, upgrade), or a new flag/argument on an existing one.
# Add a new CLI command The CLI is built with `System.CommandLine`. A subcommand is wired through **three files in `Cli/`** plus a **Core runner** that holds the logic, and is then **documented in `README.md`**. Match the existing commands (`export`, `batch-export`, `report`, `multi-report`, `validate`, `summary`, `upgrade`) — don't invent a new shape. **Paths below are relative to the repo root** (the directory containing `MemorySnapshotDataTools.sln`). ## When to use - Adding a brand-new subcommand (e.g. `diff`, `prune`, `export-csv`). - Adding a new argument or `--option` to an existing subcommand. - Exposing an existing Core capability through the CLI. For SQL-touching logic inside the new command, also follow [`CLAUDE.md`](../../../CLAUDE.md) and [`docs/sql-safety.md`](../../../docs/sql-safety.md) — SQL safety is a first-class rule here. ## The files you touch | File | What you add | |------|--------------| | [`Cli/CliOptions.cs`](../../../Cli/CliOptions.cs) | A `CommandKind` enum value + any new option/arg properties on `CliOptions`. | | [`Cli/CommandLineBuilder.cs`](../../../Cli/CommandLineBuilder.cs) | The `Command`, its `Argument<>`/`Option<>`s, the `SetAction` handler, `root.Add(...)`, and a new `Func<CliOptions,int>` parameter on `Build`. | | [`Cli/Program.cs`](../../../Cli/Program.cs) | A `RunXxx(CliOptions)` handler that calls the Core runner, plus threading it through the `Build(...)` call in `Main`. | | `Core/...` (a runner) | The actual logic — a `XxxRunner` + a `XxxRunOptions`, mirroring `Core/Report/SummaryReportRunner.cs`, `Core/Export/ExportRunner.cs`, etc. | | [`Tests/`](../../../Tests) | Tests for the **Core runner / calculator** (there are no `CommandLineBuilder` tests; test the logic, not the parser). | | [`README.md`](../../../README.md) | **Required** — a usage subsection + example for the new command. See [Document in README.md](#document-in-readmemd). | ## Step by step ### 1. `Cli/CliOptions.cs` - Add a value to the `CommandKind` enum. - Add a property to `CliOptions` for each new argument/option. Reuse existing ones where they fit (`Verbose`, `ReportDbPath`, `Destination`, …) before adding new fields. ### 2. `Cli/CommandLineBuilder.cs` Inside `Build(...)`, following the existing blocks: ```csharp // ---- mycommand ---- var myCmd = new Command("mycommand", "One-line description shown in --help."); var inputArg = new Argument<string>("input") { Description = "Path to ...", Arity = ArgumentArity.ExactlyOne, }; myCmd.Add(inputArg); var someOpt = new Option<string>("--mode") { Description = "...: a, b, or c.", DefaultValueFactory = _ => "a", }; someOpt.AcceptOnlyFromAmong("a", "b", "c"); // validate enum-like options at parse time myCmd.Add(someOpt); myCmd.SetAction((ParseResult parseResult) => { var inputPath = ExpandPath(parseResult.GetValue(inputArg)!); // ALWAYS ExpandPath path args if (!File.Exists(inputPath)) // validate existence, return 1 { Console.Error.WriteLine($"Input file not found: {inputPath}"); return 1; } var options = new CliOptions { Command = CommandKind.MyCommand, // ... map parsed values onto CliOptions ... }; return runMyCommand(options); }); ``` Then: - Register it: `root.Add(myCmd);` near the bottom of `Build`. - Add a `Func<CliOptions, int> runMyCommand` parameter to the `Build(...)` signature (the params are positional — keep the order consistent with `Program.Main`). ### 3. `Cli/Program.cs` - Add a `RunMyCommand(CliOptions options)` static handler that delegates to your Core runner. - If the command **reads an exported database**, call `SchemaGate.Check(path)` first (see `RunReport`/`RunSummary`). - If it does cancellable work, use `CreateCancellationSource()` and catch `OperationCanceledException` → return `2` (see `RunExport`). - Add `RunMyCommand` to the `CommandLineBuilder.Build(...)` call in `Main` in the matching position. ### 4. Core runner (the logic) Put real work in `Core/...`, not in `Cli/`. Mirror an existing runner (`SummaryReportRunner`, `ExportRunner`): a `XxxRunOptions` record + a `static int Run(...)` that returns an exit code and reports via `IProgressReporter`. If it builds SQL, parameterize values and validate identifiers per [`docs/sql-safety.md`](../../../docs/sql-safety.md); open the DB **read-only** if it only reads (`Mode=ReadOnly` / `ACCESS_MODE=READ_ONLY`). ### 5. Tests Add tests under [`Tests/`](../../../Tests) targeting the Core runner / calculator (e.g. `BatchExportRunnerTests.cs`, `SummaryMetricsCalculatorTests.cs`). Helpers you assert on must be **`public`** — `InternalsVisibleTo` is a no-op here, so Core internals are not visible to Tests. ### 6. Document in README.md **This step is required — a new command is not done until the README documents it.** In [`README.md`](../../../README.md): - Add a `### <verb the command does>` subsection under **How to use**, after the existing command subsections, with the invocation form and a worked example, matching the style of the `export` and `report` sections: ````markdown ### <What the command does> ```bash dotnet run --project Cli/MemorySnapshotDataTools.Cli.csproj -- mycommand <args> [options] ``` - **`--option`:** what it does (default: …). **Example:** ```bash dotnet run --project Cli/MemorySnapshotDataTools.Cli.csproj -- mycommand ./in.duckdb --mode b ``` ```` - If the command changes the headline behavior, add a bullet to the **What it does** list at the top, and update **Output**/**Schema** if it writes new tables/files. - Consider also updating, when relevant: the **Direct CLI invocation** list in [`run-memory-snapshot-data-tool/SKILL.md`](../run-memory-snapshot-data-tool/SKILL.md), and `docs/intro.md` / `docs/runbook.md`. ### 7. Build, test, and verify it runs ```bash dotnet build MemorySnapshotDataTools.sln dotnet test MemorySnapshotDataTools.sln dotnet run --project Cli/MemorySnapshotDataTools.Cli.csproj -- --help # command listed? dotnet run --project Cli/MemorySnapshotDataTools.Cli.csproj -- mycommand --help # args/options correct? ``` To exercise the full pipeline (build → run → screenshot), use the `run-memory-snapshot-data-tool` skill's driver. ## Conventions & gotchas - **Always `ExpandPath(...)` path arguments/options** before use — it expands `~`, env vars, and resolves to a full path. Every existing handler does this. - **Validate inputs in `SetAction` and return `1`** for bad args / missing files, before constructing `CliOptions`. - **Exit codes are meaningful and asserted on:** `0` success, `1` bad args / file not found, `2` cancelled (Ctrl-C), `3` validation/error. Keep yours consistent. - **Enum-like string options** → `option.AcceptOnlyFromAmong("a","b","c")` so bad values are rejected at parse time (see `--validate`, `--destination`). - **`Build`'s handlers are positional `Func<CliOptions,int>` parameters.** Add yours in the same position in both the `Build` signature and the `Main` call site, or the wrong handler runs. - **Reading an exported DB?** Call `SchemaGate.Check(path)` first so a stale/newer schema fails with a clear message instead of a confusing query error. - **SQL safety is non-negotiable** — never interpolate external values into SQL; bind parameters and validate identifiers (`CLAUDE.md`, `docs/sql-safety.md`). ## Checklist - [ ] `CommandKind` value + `CliOptions` properties added (`Cli/CliOptions.cs`). - [ ] Command, args/options, `SetAction`, `root.Add`, and new `Build` param added (`Cli/CommandLineBuilder.cs`). - [ ] `RunXxx` handler added and threaded through `Main` (`Cli/Program.cs`). - [ ] Logic lives in a Core `XxxRunner` (+ `XxxRunOptions`); SQL is parameterized / read-only. - [ ] Tests added for the Core logic (public helpers). - [ ] **`README.md` documents the command** (usage + example), other docs/skill updated if relevant. - [ ] `dotnet build` + `dotnet test` pass; `-- --help` and `-- mycommand --help` look right.
Auf GitHub ansehen