Add CLI interface and extract shared analysis library#15
Add CLI interface and extract shared analysis library#15baronfel merged 12 commits intobaronfel:mainfrom
Conversation
All MCP tools are now available as individual CLI commands: load, list-files, get-file, diagnostics, search, list-projects, expensive-projects, project-build-time, project-target-list, project-target-times, expensive-targets, search-targets, target-info, expensive-tasks, task-info, list-tasks, search-tasks, expensive-analyzers, task-analyzers, list-evaluations, eval-global-props, eval-properties, eval-items, timeline CLI and MCP share the same static tool implementations. CliRunner.EnsureLoaded delegates to LoadBinlogTool.Load so the load path is identical in both modes. Output is trim-safe indented JSON via source-generated BinlogJsonContext. MCP server still starts with no args (backward compat) or 'mcp'. server.json updated to pass 'mcp' arg when run via MCP host. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Agents are the primary CLI consumers so human-readable formatting is not needed. PrintJson<T> now delegates directly to JsonSerializer.Serialize(value, typeInfo) using the same source-generated BinlogJsonContext as the MCP server. Removes the JsonDocument + Utf8JsonWriter pretty-print round-trip. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…onary for public property
ConcurrentDictionary<int, NodeStats> was silently skipped by the STJ
source generator, producing {} for the timeline command.
The fix: populate in parallel using a local ConcurrentDictionary,
then assign a plain Dictionary<int, NodeStats> to the public property
once Populate() completes. Dictionary<int, Timeline.NodeStats> is
already registered in BinlogJsonContext and serializes correctly.
Also removes the now-unnecessary ConcurrentDictionary entry that was
added to BinlogJsonContext.
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Every CLI command now carries the exact same description text as the corresponding [Description(...)] attribute on its MCP tool method. The search command uses the full multi-line query language reference. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…r), binlog.cli (CLI) - Create binlog/ class library with all analysis tools and infrastructure - Slim binlog.mcp/ down to just Program.cs and *Extensions.cs files - Create binlog.cli/ with CLI commands (without MCP server command) - Remove args from server.json transport (MCP server now launched directly) - Make BinlogJsonContext and BinlogJsonOptions public for cross-project access - Update solution file to include all three projects Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ackaging - Add ToolCommandName, Ionide.KeepAChangelog.Tasks, CHANGELOG.md, and PACKAGE_README.md to binlog.cli - Add IsPackable=false to binlog shared library (internal only) - Update release.yml to push all baronfel.binlog.*.nupkg on tag Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
baronfel
left a comment
There was a problem hiding this comment.
What's funny about this PR is that I have a library for converting S.CL commands into MCP tools - so completely the other way around :D
One design question - why have separate clients at all? Could the 'argument-less' form of the CLI be an MCP mode, and then also provide the CLI commands for direct usage? That could reduce confusion and enable both usage modes from the same tool.
binlog.cli/Cli/CliCommands.cs
Outdated
| // Inject the binlog path after the command name. | ||
| // Input: "expensive-projects --top 5" | ||
| // We need: "expensive-projects <binlog> --top 5" | ||
| var parts = SplitCommandLine(line); |
There was a problem hiding this comment.
S.CL should be able to parse these lines directly if you don't make BatchCommand a root command, and the position of the binlog argument doesn't matter to S.CL - options and arguments are interchangeable so you can always add it to the end of the argument array safely. Basically from a CLI hygiene PoV I don't want to be managing splitting tokens, that's a hell I have so many scars from.
I agree that having a single tool is better, but it would be confusing if the tool goes into mcp server mode instead of printing usage instructions, when started without cmd-line arguments. A better way is to start mcp server would be using 'mcp' argument. This way 'no arguments' would print help text which is expected by agents and humans. However, this is a breaking change to the existing mcp package, and that is why I added the cli package. It is your call though, and I am happy to merge into mcp, or keep it separately, just let me know which direction you prefer. @baronfel |
…andle parsing Append binlog path to the raw input line and let S.CL tokenize and resolve argument positions. Eliminates fragile hand-rolled splitter. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
| </PropertyGroup> | ||
| <ItemGroup> | ||
| <PackageVersion Include="Ionide.KeepAChangelog.Tasks" Version="0.3.1" /> | ||
| <PackageVersion Include="System.CommandLine" Version="2.0.0-beta4.22272.1" /> |
There was a problem hiding this comment.
I missed that this is using beta-4. I don't want that since there's a stable release, so I'm doing that update then will do a release.
Motivation
The MCP server is great for AI assistants, but agents that work via shell commands (and humans) need a CLI. Extracting a shared library also lets both frontends evolve independently without duplicating analysis logic.
Approach
Split the single
binlog.mcpproject into three: base implementation, MCP server, CLIAll 24 analysis tools are now CLI subcommands that call the same static methodd as the MCP.
Namespaces are preserved as
Binlog.MCP.*to keep the diff minimal.CLI highlights
diagnostics,expensive-projects,search,target-info, etc.batchloads the binlog once, reads commands from stdin. Avoids the ~30s per-command reload overhead for multi-command sessions.modeBinlogJsonContextsource-gen serializer as MCP.Bug fix
Timeline.NodesByNodeIdwas aConcurrentDictionarySTJ source gen silently skipped it, producing{}. Changed to aDictionary<int, NodeStats>property populated after parallel traversal.Packaging
binloglibrary isIsPackable=false(internal only).binlog.clihas its ownCHANGELOG.md(version0.0.1),PACKAGE_README.md, andIonide.KeepAChangelog.Tasksfor changelog-driven versioning.release.ymlupdated to glob-pushbaronfel.binlog.*.nupkgso both packages land on NuGet.Non-obvious decisions
global.jsonSDK version bumped from10.0.100to10.0.201to match the installed SDK.expensive-projects --top 5and the batch loop prepends the binlog path before dispatching to the sameSystem.CommandLineroot. No separate command definitions needed.path