Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,10 @@ jobs:
go install github.com/google/yamlfmt/cmd/yamlfmt@latest
go install golang.org/x/tools/cmd/goimports@latest
echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
# shuck ships prebuilt binaries (the linter is the shuck-cli package;
# `shuck check -` needs >= 0.0.43 for stdin support).
curl -fsSL https://github.com/ewhauser/shuck/releases/latest/download/shuck-cli-x86_64-unknown-linux-gnu.tar.xz | tar -xJ -C /tmp
sudo install /tmp/shuck-cli-x86_64-unknown-linux-gnu/shuck /usr/local/bin/

- name: Run code-block tools execution harness
run: make test-code-block-tools
Expand Down
71 changes: 36 additions & 35 deletions docs/code-block-tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,40 +107,41 @@ rumdl includes definitions for common tools:

<!-- BEGIN builtin-tools (generated) -->

| Tool ID | Language | Type | Command |
| ----------------- | ---------- | ------ | -------------------------------------- |
| `ruff:check` | Python | Lint | `ruff check --output-format=concise -` |
| `ruff:format` | Python | Format | `ruff format -` |
| `black` | Python | Format | `black --quiet -` |
| `prettier` | Multi | Format | `prettier --stdin-filepath=_.EXT` |
| `shellcheck` | Shell | Lint | `shellcheck --shell=bash -` |
| `shfmt` | Shell | Format | `shfmt` |
| `rustfmt` | Rust | Format | `rustfmt` |
| `gofmt` | Go | Format | `gofmt` |
| `goimports` | Go | Format | `goimports` |
| `clang-format` | C/C++ | Format | `clang-format` |
| `sqlfluff:lint` | SQL | Lint | `sqlfluff lint --dialect ansi -` |
| `sqlfluff:fix` | SQL | Format | `sqlfluff fix --dialect ansi -` |
| `jq` | JSON | Both | `jq .` |
| `yamlfmt` | YAML | Format | `yamlfmt -` |
| `taplo` | TOML | Format | `taplo fmt -` |
| `terraform-fmt` | Terraform | Format | `terraform fmt -` |
| `nixfmt` | Nix | Format | `nixfmt -` |
| `stylua` | Lua | Format | `stylua -` |
| `ormolu` | Haskell | Format | `ormolu --stdin-input-file=_.hs` |
| `elm-format` | Elm | Format | `elm-format --stdin` |
| `swift-format` | Swift | Format | `swift-format format -` |
| `ktfmt` | Kotlin | Format | `ktfmt -` |
| `djlint` | Jinja/HTML | Both | `djlint - / djlint - --reformat` |
| `djlint:lint` | Jinja/HTML | Lint | `djlint -` |
| `djlint:reformat` | Jinja/HTML | Format | `djlint - --reformat` |
| `beautysh` | Shell | Both | `beautysh - --check / beautysh -` |
| `tombi` | TOML | Lint | `tombi lint -` |
| `tombi:format` | TOML | Format | `tombi format -` |
| `tombi:lint` | TOML | Lint | `tombi lint -` |
| `oxfmt` | Multi | Format | `oxfmt --stdin-filepath=_.EXT` |
| `deno-fmt` | Multi | Format | `deno fmt --ext=EXT -` |
| `rumdl` | Markdown | Lint | `built-in markdown linting` |
| Tool ID | Language | Type | Command |
| ----------------- | ---------- | ------ | --------------------------------------- |
| `ruff:check` | Python | Lint | `ruff check --output-format=concise -` |
| `ruff:format` | Python | Format | `ruff format -` |
| `black` | Python | Format | `black --quiet -` |
| `prettier` | Multi | Format | `prettier --stdin-filepath=_.EXT` |
| `shellcheck` | Shell | Lint | `shellcheck --shell=bash -` |
| `shfmt` | Shell | Format | `shfmt` |
| `shuck` | Shell | Lint | `shuck check --output-format concise -` |
| `rustfmt` | Rust | Format | `rustfmt` |
| `gofmt` | Go | Format | `gofmt` |
| `goimports` | Go | Format | `goimports` |
| `clang-format` | C/C++ | Format | `clang-format` |
| `sqlfluff:lint` | SQL | Lint | `sqlfluff lint --dialect ansi -` |
| `sqlfluff:fix` | SQL | Format | `sqlfluff fix --dialect ansi -` |
| `jq` | JSON | Both | `jq .` |
| `yamlfmt` | YAML | Format | `yamlfmt -` |
| `taplo` | TOML | Format | `taplo fmt -` |
| `terraform-fmt` | Terraform | Format | `terraform fmt -` |
| `nixfmt` | Nix | Format | `nixfmt -` |
| `stylua` | Lua | Format | `stylua -` |
| `ormolu` | Haskell | Format | `ormolu --stdin-input-file=_.hs` |
| `elm-format` | Elm | Format | `elm-format --stdin` |
| `swift-format` | Swift | Format | `swift-format format -` |
| `ktfmt` | Kotlin | Format | `ktfmt -` |
| `djlint` | Jinja/HTML | Both | `djlint - / djlint - --reformat` |
| `djlint:lint` | Jinja/HTML | Lint | `djlint -` |
| `djlint:reformat` | Jinja/HTML | Format | `djlint - --reformat` |
| `beautysh` | Shell | Both | `beautysh - --check / beautysh -` |
| `tombi` | TOML | Lint | `tombi lint -` |
| `tombi:format` | TOML | Format | `tombi format -` |
| `tombi:lint` | TOML | Lint | `tombi lint -` |
| `oxfmt` | Multi | Format | `oxfmt --stdin-filepath=_.EXT` |
| `deno-fmt` | Multi | Format | `deno fmt --ext=EXT -` |
| `rumdl` | Markdown | Lint | `built-in markdown linting` |

<!-- END builtin-tools (generated) -->

Expand Down Expand Up @@ -373,7 +374,7 @@ zsh = "shell"

| Feature | rumdl | mdsf |
| ---------------- | -------------- | ---------- |
| Built-in tools | 32 | 339 |
| Built-in tools | 33 | 339 |
| Custom tools | Yes | Yes |
| Linting | Yes | No |
| Formatting | Yes | Yes |
Expand Down
30 changes: 30 additions & 0 deletions src/code_block_tools/registry.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,28 @@ static BUILTIN_TOOLS: LazyLock<HashMap<&'static str, ToolDefinition>> = LazyLock
},
);

// Shell - shuck (lint only; faster shellcheck alternative). `--output-format
// concise` keeps shuck's legacy one-line-per-diagnostic output, which parses
// via the same generic "file:line:col: message" path as other tools instead
// of needing a dedicated parser. Requires shuck >= 0.0.43 for `check -` stdin
// support (see rvben/rumdl#655 and ewhauser/shuck#1123).
m.insert(
"shuck",
ToolDefinition {
command: vec![
"shuck".to_string(),
"check".to_string(),
"--output-format".to_string(),
"concise".to_string(),
"-".to_string(),
],
stdin: true,
stdout: true,
lint_args: vec![],
format_args: vec![],
},
);

// Rust - rustfmt
m.insert(
"rustfmt",
Expand Down Expand Up @@ -729,6 +751,14 @@ const BUILTIN_TOOLS_DOCS: &[ToolDocMeta] = &[
display_command: None,
runtime: true,
},
ToolDocMeta {
id: "shuck",
language: "Shell",
kind: ToolKind::Lint,
doc_group: "shuck",
display_command: None,
runtime: true,
},
ToolDocMeta {
id: "rustfmt",
language: "Rust",
Expand Down
25 changes: 25 additions & 0 deletions tests/integration/code_block_tools_execution_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ fn tool_available(tool: &str) -> bool {
.map(|o| o.status.success())
.unwrap_or(false)
}
"shuck" => {
// `shuck` is a contested binary name: the shell linter ships as the
// `shuck-cli` package, while an unrelated microVM manager also
// installs a `shuck`. Only the linter has a `check` subcommand.
Command::new("shuck")
.args(["check", "--help"])
.output()
.map(|o| o.status.success())
.unwrap_or(false)
}
_ => {
// Default spawn check (safe, won't block even if tool expects stdin,
// because we pass --version and kill it immediately if it spawns)
Expand Down Expand Up @@ -147,6 +157,20 @@ fn shellcheck_lints_shell() {
);
}

#[test]
fn shuck_lints_shell() {
require_tool!("shuck");
// Regression guard for the stdin fix upstream (ewhauser/shuck#1123, shipped in
// v0.0.43): a pre-0.0.43 shuck treats `-` as a literal filename instead of
// reading stdin and would report a missing-file error here instead of a
// real diagnostic.
let out = lint("shell", "shuck", "shell", "name=\"world\"\necho \"hello $nombre\"\n");
assert!(
out.contains("referenced before assignment") || out.contains("C006"),
"shuck should flag the reference to the undefined variable:\n{out}"
);
}

#[test]
fn jq_lints_invalid_json() {
require_tool!("jq");
Expand Down Expand Up @@ -367,6 +391,7 @@ const VERIFIED: &[&str] = &[
"prettier",
"shellcheck",
"shfmt",
"shuck",
"rustfmt",
"gofmt",
"goimports",
Expand Down
Loading