From 0f14d1088bdf4d5a199f35c240177dcbbc4c309f Mon Sep 17 00:00:00 2001 From: Yuto Nishida Date: Sun, 18 Oct 2026 17:04:31 -0700 Subject: [PATCH] silverwood: add cached CLI spec views as indented Nix apps --- experimental/silverwood/CLI_SPEC-views.jq | 54 ++++++++ experimental/silverwood/flake.nix | 119 ++++++++++++++---- .../view-commands-positionals-intent.cmd.sh | 11 ++ .../view-commands-positionals.cmd.sh | 11 ++ 4 files changed, 174 insertions(+), 21 deletions(-) create mode 100644 experimental/silverwood/CLI_SPEC-views.jq create mode 100755 experimental/silverwood/view-commands-positionals-intent.cmd.sh create mode 100755 experimental/silverwood/view-commands-positionals.cmd.sh diff --git a/experimental/silverwood/CLI_SPEC-views.jq b/experimental/silverwood/CLI_SPEC-views.jq new file mode 100644 index 0000000..ead1a80 --- /dev/null +++ b/experimental/silverwood/CLI_SPEC-views.jq @@ -0,0 +1,54 @@ +# Select fields without introducing defaults into the projected spec. +def _view_fields($names; $intent): + ($names + (if $intent then ["intentHint"] else [] end)) as $keep + | with_entries(select(.key as $name | $keep | index($name) != null)); + +def _commands_positionals($intent): + _view_fields(["name", "positionals", "subcommands"]; $intent) + | if has("positionals") then + .positionals |= map(_view_fields(["domain", "required"]; $intent)) + else . end + | if has("subcommands") then + .subcommands |= ( + _view_fields(["choices"]; $intent) + | if has("choices") then + .choices |= map(_commands_positionals($intent)) + else . end + ) + else . end; + +def command_positionals: + _commands_positionals(false); + +def commands_positionals_intent: + _commands_positionals(true); + +# Render an already projected command tree as lines for jq -r. +def _positional_usage: + (.domain.semanticName // "ARG") as $name + | if .required == false then "[\($name)]" else "<\($name)>" end; + +def _intent_lines($depth; $label): + if has("intentHint") then + .intentHint | gsub("\r\n?"; "\n") | split("\n")[] + | (" " * $depth) + "# " + $label + . + else empty end; + +def _indented_commands($depth): + (" " * $depth) as $indent + | ($indent + .name + + ([.positionals[]? | _positional_usage] + | if length > 0 then " " + join(" ") else "" end)), + _intent_lines($depth + 1; ""), + (.positionals[]? | _positional_usage as $label + | _intent_lines($depth + 1; $label + ": ")), + (if (.subcommands // {} | has("intentHint")) then + ($indent + " subcommands:"), + (.subcommands | _intent_lines($depth + 2; "")), + (.subcommands.choices[]? | _indented_commands($depth + 2)) + else + (.subcommands.choices[]? | _indented_commands($depth + 1)) + end); + +def indented_commands: + _indented_commands(0); diff --git a/experimental/silverwood/flake.nix b/experimental/silverwood/flake.nix index 63b8e2b..1d9dd03 100644 --- a/experimental/silverwood/flake.nix +++ b/experimental/silverwood/flake.nix @@ -29,6 +29,70 @@ pkgs = nixpkgs.legacyPackages.${system}; inherit (pkgs) lib; + # These views depend only on the contract and queries, independently of Cargo. + cliSpecSource = lib.fileset.toSource { + root = ./.; + fileset = lib.fileset.unions [ + ./CLI_SPEC.jsonnet + ./CLI_SPEC-views.jq + ]; + }; + + cliSpecJson = + pkgs.runCommand "silverwood-cli-spec-json" + { + nativeBuildInputs = [ pkgs.go-jsonnet ]; + } + '' + mkdir -p "$out" + jsonnet ${cliSpecSource}/CLI_SPEC.jsonnet > "$out/spec.json" + ''; + + mkCliSpecView = + name: query: + pkgs.runCommand "silverwood-cli-spec-${name}-json" + { + nativeBuildInputs = [ pkgs.jq ]; + } + '' + mkdir -p "$out" + jq -L ${cliSpecSource} ${lib.escapeShellArg ''include "CLI_SPEC-views"; ${query}''} \ + ${cliSpecJson}/spec.json > "$out/view.json" + ''; + + cliSpecCommandsPositionals = mkCliSpecView "commands-positionals" "command_positionals"; + cliSpecCommandsPositionalsIntent = mkCliSpecView "commands-positionals-intent" "commands_positionals_intent"; + + mkCliSpecTextView = + name: view: + pkgs.runCommand "silverwood-cli-spec-${name}-text" + { + nativeBuildInputs = [ pkgs.jq ]; + } + '' + mkdir -p "$out" + jq -r -L ${cliSpecSource} 'include "CLI_SPEC-views"; indented_commands' \ + ${view}/view.json > "$out/view.txt" + ''; + + cliSpecCommandsPositionalsText = mkCliSpecTextView "commands-positionals" cliSpecCommandsPositionals; + cliSpecCommandsPositionalsIntentText = mkCliSpecTextView "commands-positionals-intent" cliSpecCommandsPositionalsIntent; + + mkCliSpecViewApp = + name: view: + flake-utils.lib.mkApp { + drv = pkgs.writeShellScriptBin name '' + if (( $# != 0 )); then + printf '%s\n' 'This command accepts no arguments.' >&2 + exit 2 + fi + exec ${pkgs.coreutils}/bin/cat ${view}/view.txt + ''; + } + // { + meta.description = "Print the Silverwood CLI spec ${name} view."; + }; + craneLib = crane.mkLib pkgs; # Keep Rust/Cargo sources plus the frozen schema-migration test corpus # (binary `.loro` + expected `.json` fixtures under any `corpus/` dir), @@ -71,20 +135,23 @@ # The shipped package: the binary with jujutsu + git + direnv on PATH, since # checkout provisioning shells out to them (`jj git clone --colocate`, and # `direnv allow` for the jj-colocated-direnv-unsafe mode). - silverwood = pkgs.runCommand "silverwood" - { - nativeBuildInputs = [ pkgs.makeWrapper ]; - meta.mainProgram = "silverwood"; - } - '' - mkdir -p "$out/bin" - makeWrapper ${silverwood-unwrapped}/bin/silverwood "$out/bin/silverwood" \ - --prefix PATH : ${lib.makeBinPath [ - pkgs.jujutsu - pkgs.git - pkgs.direnv - ]} - ''; + silverwood = + pkgs.runCommand "silverwood" + { + nativeBuildInputs = [ pkgs.makeWrapper ]; + meta.mainProgram = "silverwood"; + } + '' + mkdir -p "$out/bin" + makeWrapper ${silverwood-unwrapped}/bin/silverwood "$out/bin/silverwood" \ + --prefix PATH : ${ + lib.makeBinPath [ + pkgs.jujutsu + pkgs.git + pkgs.direnv + ] + } + ''; in { checks = { @@ -130,13 +197,22 @@ packages.default = silverwood; packages.unwrapped = silverwood-unwrapped; - - apps.default = flake-utils.lib.mkApp { - drv = silverwood; - } - // { - meta.description = "Run the silverwood CLI."; - }; + packages.cli-spec-json = cliSpecJson; + packages.cli-spec-commands-positionals-json = cliSpecCommandsPositionals; + packages.cli-spec-commands-positionals-intent-json = cliSpecCommandsPositionalsIntent; + packages.cli-spec-commands-positionals-text = cliSpecCommandsPositionalsText; + packages.cli-spec-commands-positionals-intent-text = cliSpecCommandsPositionalsIntentText; + + apps.view-commands-positionals = mkCliSpecViewApp "view-commands-positionals" cliSpecCommandsPositionalsText; + apps.view-commands-positionals-intent = mkCliSpecViewApp "view-commands-positionals-intent" cliSpecCommandsPositionalsIntentText; + + apps.default = + flake-utils.lib.mkApp { + drv = silverwood; + } + // { + meta.description = "Run the silverwood CLI."; + }; devShells.default = craneLib.devShell { # cargo + rustc + clippy + rustfmt come from craneLib; add jj/git + direnv @@ -147,6 +223,7 @@ pkgs.direnv pkgs.taplo pkgs.go-jsonnet + pkgs.jq ]; }; } diff --git a/experimental/silverwood/view-commands-positionals-intent.cmd.sh b/experimental/silverwood/view-commands-positionals-intent.cmd.sh new file mode 100755 index 0000000..73a409f --- /dev/null +++ b/experimental/silverwood/view-commands-positionals-intent.cmd.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail + +if (( $# != 0 )); then + printf '%s\n' 'This command accepts no arguments.' >&2 + exit 2 +fi + +script_dir="$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +# Let Nix use tracked working-tree files and exclude ignored build artifacts. +exec nix run "$script_dir#view-commands-positionals-intent" diff --git a/experimental/silverwood/view-commands-positionals.cmd.sh b/experimental/silverwood/view-commands-positionals.cmd.sh new file mode 100755 index 0000000..1e6ec9e --- /dev/null +++ b/experimental/silverwood/view-commands-positionals.cmd.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash +set -euo pipefail + +if (( $# != 0 )); then + printf '%s\n' 'This command accepts no arguments.' >&2 + exit 2 +fi + +script_dir="$(CDPATH= cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)" +# Let Nix use tracked working-tree files and exclude ignored build artifacts. +exec nix run "$script_dir#view-commands-positionals" -- 2.54.0