Skip to content

Commit 237db5c

Browse files
GiggleLiuclaude
andauthored
feat(cli): CLI UX improvements (#84)
* docs: add CLI v2 features design (issue #81) Design for k-neighbor exploration, colored output, and shell completions. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * docs: add CLI v2 implementation plan (issue #81) 10-task plan covering shell completions, colored output, and k-neighbor graph exploration for the pred CLI tool. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): add shell completions command (bash/zsh/fish) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test(cli): add integration tests for shell completions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): add owo-colors dependency and color helper functions Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): add aligned columns and color to pred list Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): add colors to pred show output Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): add colors to pred path output Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(lib): add k_neighbors BFS method to ReductionGraph Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): add --hops and --direction flags to pred show for neighbor exploration Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * test(cli): add integration tests for k-neighbor exploration Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: apply rustfmt formatting Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: address PR review comments - Remove unused `fmt_arrow_out()` dead code (Copilot review) - Deduplicate `find_node_index()` by delegating to existing `lookup_node()` - Extract table formatting into reusable `format_table()` helper in output.rs - Remove `completions` command and `clap_complete` dependency (manual setup not useful) - Update CLI documentation with new table output and k-neighbor exploration examples Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): re-add completions with auto-detection and eval setup Shell argument is now optional — auto-detects from $SHELL env. Setup is just one line: eval "$(pred completions)" Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): add problem name completion for shell tab-completion Custom ProblemNameParser provides all registered problem names and aliases as completion candidates while still accepting any string (so MIS/UnitDiskGraph variant syntax works). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(cli): use left arrow for "Reduces from" and green for all arrows Makes direction immediately clear: → for outgoing, ← for incoming. Both arrows use green for visual consistency. Removes unused fmt_incoming() function. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(cli): add tab completion hint to pred --help Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): make --to optional when --via is given in pred reduce The target problem is already in the path file, so --to is redundant. Now: pred reduce problem.json --via path.json -o reduced.json Either --to or --via must be provided; error if neither is given. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): unify solve output format for direct and bundle solving Use consistent Problem/Solver/Solution/Evaluation format for both direct problem solving and reduction bundle solving. Bundle solver shows "via <target>" in solver description. Add hint about -o flag for JSON output with intermediate results. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(cli): print solve hint after output, not before Move the -o hint eprintln to after emit_with_default_name so the hint appears at the last line of terminal output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix(cli): only show solve hint when stderr is a TTY Suppress the "Hint: use -o to save full solution details as JSON." message when stderr is piped, so scripts and non-interactive usage are not cluttered with the hint. Interactive terminal users still see it. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): show auto-reduction info in solve human output When solving a non-ILP problem with the ILP solver, the human text output now shows "Solver: ilp (via ILP)" to indicate that auto-reduction occurred. Previously this information was only visible in JSON output. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): add global -q/--quiet flag to suppress stderr info Add a -q/--quiet global flag that suppresses all informational messages on stderr (hints, "Wrote..." messages, summary messages). Only errors still go to stderr in quiet mode. This enables clean scripting usage. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): print JSON to stdout when create is used without -o Previously `pred create MIS --edges 0-1,1-2` printed "Created MaximumIndependentSet instance" but discarded the actual data. Now it prints the problem JSON to stdout, consistent with how `reduce` works and enabling piping to other commands. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): add fuzzy matching for unknown problem names Add "Did you mean ...?" suggestions when a problem name is not recognized, using Levenshtein edit distance. All "Unknown problem" error sites now also suggest running `pred list`. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): add --random flag for random problem instance generation Support random Erdos-Renyi graph generation for graph-based problems via `pred create <PROBLEM> --random --num-vertices N [--edge-prob P] [--seed S]`. Uses a simple LCG PRNG with no external dependencies. Supported for MIS, MVC, MaxCut, MaxClique, MaximumMatching, MinimumDominatingSet, SpinGlass, KColoring, and TravelingSalesman. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): add inspect, stdin support, --json reduce, export-graph consistency Remaining changes from CLI UX improvement plan: - P4: export-graph uses global -o flag instead of positional arg - P7: accept - for stdin in solve, evaluate, reduce - P2: human-readable reduce output by default, --json flag for raw JSON - F1: new inspect command for problem/bundle introspection Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add cli-demo Makefile target for end-to-end CLI testing Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: apply rustfmt formatting Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): refactor show --hops --direction into pred to / pred from commands Split neighbor exploration out of `pred show` into dedicated subcommands: - `pred to MIS --hops 2` for outgoing neighbors - `pred from QUBO --hops 1` for incoming neighbors Tree output now shows variant-level information (e.g., `MIS {graph=SimpleGraph, weight=i32}`) instead of just problem names. The `pred show` command is simplified to only inspect problem details (variants, fields, reductions). Resolves H2 from the CLI UX improvements audit. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): add create Factoring and solve --timeout - P8: Support `pred create Factoring --target 15 --bits-m 4 --bits-n 4` with all three flags required (replaces unhelpful bail message) - H3: Add `--timeout <seconds>` to `pred solve` using thread+channel pattern (default 0 = no limit, process exits on timeout) - Add 6 CLI tests for Factoring creation and timeout behavior - Update cli-demo, docs, and audit file Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: unify name+variant formatting and move tree-building to core - Add fmt_node() as single source of truth for "bold name + plain variant" - Move neighbor tree BFS from CLI into ReductionGraph::k_neighbor_tree() - Add NeighborTree type to core library - Remove petgraph-exposing helpers (find_node_index, neighbor_indices, etc.) - Remove petgraph dependency from CLI crate - Fix path header not formatting names as bold Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * chore: remove completed CLI plan files Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat(cli): add global --json flag for JSON output to stdout All commands now support --json to print JSON to stdout. Previously only `create` (always JSON) and `reduce` (per-command --json) did. - Add `--json` as a global CLI flag on OutputConfig - Update emit_with_default_name to handle -o / --json / human-text - Remove per-command --json from ReduceArgs (now global) - Wire path and path --all through emit or --json branch - Update docs and after_help Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 6e36c4a commit 237db5c

35 files changed

Lines changed: 3196 additions & 377 deletions

.claude/CLAUDE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ make diagrams # Generate SVG diagrams from Typst (light + dark)
2727
make examples # Generate example JSON for paper
2828
make compare # Generate and compare Rust mapping exports
2929
make jl-testdata # Regenerate Julia parity test data (requires julia)
30+
make cli # Build the pred CLI tool (release mode)
31+
make cli-demo # Run closed-loop CLI demo (exercises all commands)
3032
make run-plan # Execute a plan with Claude autorun
3133
make release V=x.y.z # Tag and push a new release (CI publishes to crates.io)
3234
```
@@ -48,6 +50,7 @@ make test clippy # Must pass before PR
4850
- `src/traits.rs` - `Problem`, `OptimizationProblem`, `SatisfactionProblem` traits
4951
- `src/rules/traits.rs` - `ReduceTo<T>`, `ReductionResult` traits
5052
- `src/registry/` - Compile-time reduction metadata collection
53+
- `problemreductions-cli/` - `pred` CLI tool (separate crate in workspace)
5154
- `src/unit_tests/` - Unit test files (mirroring `src/` structure, referenced via `#[path]`)
5255
- `tests/main.rs` - Integration tests (modules in `tests/suites/`); example tests use `include!` for direct invocation (no subprocess)
5356
- `tests/data/` - Ground truth JSON for integration tests

Makefile

Lines changed: 139 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Makefile for problemreductions
22

3-
.PHONY: help build test fmt clippy doc mdbook paper examples clean coverage rust-export compare qubo-testdata export-schemas release run-plan diagrams jl-testdata cli
3+
.PHONY: help build test fmt clippy doc mdbook paper examples clean coverage rust-export compare qubo-testdata export-schemas release run-plan diagrams jl-testdata cli cli-demo
44

55
# Default target
66
help:
@@ -25,6 +25,7 @@ help:
2525
@echo " jl-testdata - Regenerate Julia parity test data (requires julia)"
2626
@echo " release V=x.y.z - Tag and push a new release (triggers CI publish)"
2727
@echo " cli - Build the pred CLI tool"
28+
@echo " cli-demo - Run closed-loop CLI demo (build + exercise all commands)"
2829
@echo " run-plan - Execute a plan with Claude autorun (latest plan in docs/plans/)"
2930

3031
# Build the project
@@ -196,3 +197,140 @@ run-plan:
196197
--verbose \
197198
--max-turns 500 \
198199
-p "$$PROMPT" 2>&1 | tee "$(OUTPUT)"
200+
201+
# Closed-loop CLI demo: exercises all commands end-to-end
202+
PRED := cargo run -p problemreductions-cli --release --
203+
CLI_DEMO_DIR := /tmp/pred-cli-demo
204+
cli-demo: cli
205+
@echo "=== pred CLI closed-loop demo ==="
206+
@rm -rf $(CLI_DEMO_DIR) && mkdir -p $(CLI_DEMO_DIR)
207+
@set -e; \
208+
PRED="./target/release/pred"; \
209+
\
210+
echo ""; \
211+
echo "--- 1. list: all registered problems ---"; \
212+
$$PRED list; \
213+
$$PRED list -o $(CLI_DEMO_DIR)/problems.json; \
214+
\
215+
echo ""; \
216+
echo "--- 2. show: inspect MIS (variants, fields, reductions) ---"; \
217+
$$PRED show MIS; \
218+
$$PRED show MIS -o $(CLI_DEMO_DIR)/mis_info.json; \
219+
\
220+
echo ""; \
221+
echo "--- 3. to: explore 2-hop outgoing neighborhood ---"; \
222+
$$PRED to MIS --hops 2; \
223+
$$PRED to MIS --hops 2 -o $(CLI_DEMO_DIR)/mis_hops.json; \
224+
\
225+
echo ""; \
226+
echo "--- 4. from: incoming neighbors ---"; \
227+
$$PRED from QUBO --hops 1; \
228+
\
229+
echo ""; \
230+
echo "--- 5. path: find reduction paths ---"; \
231+
$$PRED path MIS QUBO; \
232+
$$PRED path MIS QUBO -o $(CLI_DEMO_DIR)/path_mis_qubo.json; \
233+
$$PRED path Factoring SpinGlass; \
234+
$$PRED path MIS QUBO --cost minimize:num_variables; \
235+
\
236+
echo ""; \
237+
echo "--- 6. path --all: enumerate all paths ---"; \
238+
$$PRED path MIS QUBO --all; \
239+
$$PRED path MIS QUBO --all -o $(CLI_DEMO_DIR)/all_paths/; \
240+
\
241+
echo ""; \
242+
echo "--- 7. export-graph: full reduction graph ---"; \
243+
$$PRED export-graph -o $(CLI_DEMO_DIR)/graph.json; \
244+
\
245+
echo ""; \
246+
echo "--- 8. create: build problem instances ---"; \
247+
$$PRED create MIS --edges 0-1,1-2,2-3,3-4,4-0 -o $(CLI_DEMO_DIR)/mis.json; \
248+
$$PRED create MIS --edges 0-1,1-2,2-3 --weights 2,1,3,1 -o $(CLI_DEMO_DIR)/mis_weighted.json; \
249+
$$PRED create SAT --num-vars 3 --clauses "1,2;-1,3;2,-3" -o $(CLI_DEMO_DIR)/sat.json; \
250+
$$PRED create 3SAT --num-vars 4 --clauses "1,2,3;-1,2,-3;1,-2,3" -o $(CLI_DEMO_DIR)/3sat.json; \
251+
$$PRED create QUBO --matrix "1,-0.5;-0.5,2" -o $(CLI_DEMO_DIR)/qubo.json; \
252+
$$PRED create KColoring --k 3 --edges 0-1,1-2,2-0 -o $(CLI_DEMO_DIR)/kcol.json; \
253+
$$PRED create SpinGlass --edges 0-1,1-2 -o $(CLI_DEMO_DIR)/sg.json; \
254+
$$PRED create MaxCut --edges 0-1,1-2,2-0 -o $(CLI_DEMO_DIR)/maxcut.json; \
255+
$$PRED create MVC --edges 0-1,1-2,2-3 -o $(CLI_DEMO_DIR)/mvc.json; \
256+
$$PRED create MaximumMatching --edges 0-1,1-2,2-3 -o $(CLI_DEMO_DIR)/matching.json; \
257+
$$PRED create Factoring --target 15 --bits-m 4 --bits-n 4 -o $(CLI_DEMO_DIR)/factoring.json; \
258+
$$PRED create Factoring --target 21 --bits-m 3 --bits-n 3 -o $(CLI_DEMO_DIR)/factoring2.json; \
259+
\
260+
echo ""; \
261+
echo "--- 9. evaluate: test configurations ---"; \
262+
$$PRED evaluate $(CLI_DEMO_DIR)/mis.json --config 1,0,1,0,0; \
263+
$$PRED evaluate $(CLI_DEMO_DIR)/mis.json --config 1,1,0,0,0; \
264+
$$PRED evaluate $(CLI_DEMO_DIR)/sat.json --config 0,1,1; \
265+
$$PRED evaluate $(CLI_DEMO_DIR)/mis.json --config 1,0,1,0,0 -o $(CLI_DEMO_DIR)/eval.json; \
266+
\
267+
echo ""; \
268+
echo "--- 10. solve: direct ILP (auto-reduces to ILP) ---"; \
269+
$$PRED solve $(CLI_DEMO_DIR)/mis.json; \
270+
$$PRED solve $(CLI_DEMO_DIR)/mis.json -o $(CLI_DEMO_DIR)/sol_ilp.json; \
271+
\
272+
echo ""; \
273+
echo "--- 11. solve: brute-force ---"; \
274+
$$PRED solve $(CLI_DEMO_DIR)/mis.json --solver brute-force; \
275+
\
276+
echo ""; \
277+
echo "--- 12. solve: weighted MIS ---"; \
278+
$$PRED solve $(CLI_DEMO_DIR)/mis_weighted.json; \
279+
\
280+
echo ""; \
281+
echo "--- 13. reduce: MIS → QUBO (auto-discover path) ---"; \
282+
$$PRED reduce $(CLI_DEMO_DIR)/mis.json --to QUBO -o $(CLI_DEMO_DIR)/bundle_qubo.json; \
283+
\
284+
echo ""; \
285+
echo "--- 14. solve bundle: brute-force on reduced QUBO ---"; \
286+
$$PRED solve $(CLI_DEMO_DIR)/bundle_qubo.json --solver brute-force; \
287+
\
288+
echo ""; \
289+
echo "--- 15. reduce --via: use explicit path file ---"; \
290+
$$PRED reduce $(CLI_DEMO_DIR)/mis.json --via $(CLI_DEMO_DIR)/path_mis_qubo.json -o $(CLI_DEMO_DIR)/bundle_via.json; \
291+
\
292+
echo ""; \
293+
echo "--- 16. solve bundle with ILP: MIS → MVC → ILP ---"; \
294+
$$PRED reduce $(CLI_DEMO_DIR)/mis.json --to MVC -o $(CLI_DEMO_DIR)/bundle_mvc.json; \
295+
$$PRED solve $(CLI_DEMO_DIR)/bundle_mvc.json --solver ilp; \
296+
\
297+
echo ""; \
298+
echo "--- 17. solve: other problem types ---"; \
299+
$$PRED solve $(CLI_DEMO_DIR)/sat.json --solver brute-force; \
300+
$$PRED solve $(CLI_DEMO_DIR)/kcol.json --solver brute-force; \
301+
$$PRED solve $(CLI_DEMO_DIR)/maxcut.json --solver brute-force; \
302+
$$PRED solve $(CLI_DEMO_DIR)/mvc.json; \
303+
\
304+
echo ""; \
305+
echo "--- 18. closed-loop: create → reduce → solve → verify ---"; \
306+
echo "Creating a 6-vertex graph..."; \
307+
$$PRED create MIS --edges 0-1,1-2,2-3,3-4,4-5,0-5,1-4 -o $(CLI_DEMO_DIR)/big.json; \
308+
echo "Solving with ILP..."; \
309+
$$PRED solve $(CLI_DEMO_DIR)/big.json -o $(CLI_DEMO_DIR)/big_sol.json; \
310+
echo "Reducing to QUBO and solving with brute-force..."; \
311+
$$PRED reduce $(CLI_DEMO_DIR)/big.json --to QUBO -o $(CLI_DEMO_DIR)/big_qubo.json; \
312+
$$PRED solve $(CLI_DEMO_DIR)/big_qubo.json --solver brute-force -o $(CLI_DEMO_DIR)/big_qubo_sol.json; \
313+
echo "Verifying both solutions have the same evaluation..."; \
314+
ILP_EVAL=$$(jq -r '.evaluation' $(CLI_DEMO_DIR)/big_sol.json); \
315+
BF_EVAL=$$(jq -r '.evaluation' $(CLI_DEMO_DIR)/big_qubo_sol.json); \
316+
echo " ILP solution evaluation: $$ILP_EVAL"; \
317+
echo " Brute-force (via QUBO) evaluation: $$BF_EVAL"; \
318+
if [ "$$ILP_EVAL" = "$$BF_EVAL" ]; then \
319+
echo " ✅ Solutions agree!"; \
320+
else \
321+
echo " ❌ Solutions disagree!" && exit 1; \
322+
fi; \
323+
\
324+
echo ""; \
325+
echo "--- 19. show with alias and variant slash syntax ---"; \
326+
$$PRED show MIS/UnitDiskGraph; \
327+
\
328+
echo ""; \
329+
echo "--- 20. completions: generate shell completions ---"; \
330+
$$PRED completions bash > /dev/null && echo "bash completions: OK"; \
331+
$$PRED completions zsh > /dev/null && echo "zsh completions: OK"; \
332+
$$PRED completions fish > /dev/null && echo "fish completions: OK"; \
333+
\
334+
echo ""; \
335+
echo "=== Demo complete: $$(ls $(CLI_DEMO_DIR)/*.json | wc -l | tr -d ' ') JSON files in $(CLI_DEMO_DIR) ==="
336+
@echo "=== All 20 steps passed ✅ ==="

0 commit comments

Comments
 (0)