From 8525603a628bdd31398f3935b29f24a3fde4810b Mon Sep 17 00:00:00 2001 From: olavostauros Date: Mon, 22 Jun 2026 14:38:30 -0300 Subject: [PATCH 1/2] fix: support multi-path scope values in [_.codebase.scope] MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The codebase_target_for_rule() function treated the entire scope value as a single path, so a TOML string like or-true = ".mise/tasks lib" would produce a single target /repo/.mise/tasks lib (with space) instead of two separate targets /repo/.mise/tasks and /repo/lib. This meant multi-path scope overrides either errored (path doesn't exist) or silently scanned nothing — users thought they had multi-path coverage but got false passes. Changes: - lib/codebase-config.sh: Replace codebase_target_for_rule() with codebase_targets_for_rule() that splits the scope value on whitespace and emits one target line per token. - .mise/tasks/lint/_default: Iterate over the multi-line output so each target is linted independently. - test/lint/default.bats: Add test covering multi-path scope with space-separated path list. The fix uses shell word-splitting on the scope string (unquoted in a for loop), which naturally handles TOML string values with internal spaces. Each token is resolved as a relative path against the repo root, or passed through unchanged if absolute. Fixes #22 --- .mise/tasks/lint/_default | 37 ++++++++++++++++++++++--------------- lib/codebase-config.sh | 24 +++++++++++++++++------- test/lint/default.bats | 38 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 22 deletions(-) diff --git a/.mise/tasks/lint/_default b/.mise/tasks/lint/_default index 297abc7..f7c1dfb 100755 --- a/.mise/tasks/lint/_default +++ b/.mise/tasks/lint/_default @@ -36,24 +36,31 @@ failures=0 FAILED=() for rule in "${RULES[@]}"; do - rule_target=$(codebase_target_for_rule "$REPO_ROOT" "$rule") - echo "codebase: lint:$rule $rule_target" + # Collect all targets for this rule (supports multi-path scopes) + TARGETS=() + while IFS= read -r t; do + [[ -n "$t" ]] && TARGETS+=("$t") + done < <(codebase_targets_for_rule "$REPO_ROOT" "$rule") - output="" - if output=$(mise run -q "lint:$rule" "$rule_target" 2>&1); then - status=0 - else - status=$? - fi + for rule_target in "${TARGETS[@]}"; do + echo "codebase: lint:$rule $rule_target" - if [[ -n "$output" ]]; then - printf '%s\n' "$output" - fi + output="" + if output=$(mise run -q "lint:$rule" "$rule_target" 2>&1); then + status=0 + else + status=$? + fi - if [[ "$status" -ne 0 ]]; then - failures=$((failures + 1)) - FAILED+=("lint:$rule ($rule_target) exited $status") - fi + if [[ -n "$output" ]]; then + printf '%s\n' "$output" + fi + + if [[ "$status" -ne 0 ]]; then + failures=$((failures + 1)) + FAILED+=("lint:$rule ($rule_target) exited $status") + fi + done done if [[ "$failures" -gt 0 ]]; then diff --git a/lib/codebase-config.sh b/lib/codebase-config.sh index 2d43820..7b917a1 100644 --- a/lib/codebase-config.sh +++ b/lib/codebase-config.sh @@ -109,19 +109,29 @@ codebase_scope_for_rule() { codebase_default_scope_for_rule "$rule" } -# codebase_target_for_rule +# codebase_targets_for_rule # -# Emit the concrete target path for a rule after scope resolution. -codebase_target_for_rule() { +# Emit the concrete target path(s) for a rule after scope resolution. +# When the scope value is a space-separated list of paths (from TOML string +# values like or-true = ".mise/tasks lib"), emits one line per target. +codebase_targets_for_rule() { local repo_root="$1" local rule="$2" - local scope + local scope token scope=$(codebase_scope_for_rule "$repo_root" "$rule") + # Handle empty/dot scope — single target: the repo root itself. case "$scope" in - ""|".") printf '%s\n' "$repo_root" ;; - /*) printf '%s\n' "$scope" ;; - *) printf '%s/%s\n' "$repo_root" "$scope" ;; + ""|".") printf '%s\n' "$repo_root"; return 0 ;; esac + + # Split scope on whitespace into individual path tokens and resolve each. + # This supports multi-path scopes like ".mise/tasks lib" from TOML. + for token in $scope; do + case "$token" in + /*) printf '%s\n' "$token" ;; + *) printf '%s/%s\n' "$repo_root" "$token" ;; + esac + done } diff --git a/test/lint/default.bats b/test/lint/default.bats index 9a6eac1..1f99003 100644 --- a/test/lint/default.bats +++ b/test/lint/default.bats @@ -182,6 +182,44 @@ EOF [[ "$output" == *"no lint rules configured"* ]] } +@test "lint: honors multi-path scope (space-separated targets)" { + write_config <<'EOF' +[settings] +quiet = true +task_output = "interleave" + +[_.codebase] +lint = ["gum-table"] + +[_.codebase.scope] +gum-table = ".mise/tasks scripts" +EOF + + # Create a dirty file in .mise/tasks/ (printf inside loop = WARN) + write_clean_task + cat > "$REPO/.mise/tasks/build" <<'SCRIPT' +#!/usr/bin/env bash +while read -r item; do + printf "%-10s %s\n" "$item" ok +done < input +SCRIPT + + # Create a dirty file in scripts/ (printf padding = INFO, not a WARN) + mkdir -p "$REPO/scripts" + cat > "$REPO/scripts/deploy" <<'SCRIPT' +#!/usr/bin/env bash +printf "%-20s %s\n" "$1" "$2" +SCRIPT + + run codebase lint "$REPO" + # gum-table fails on .mise/tasks/build (loop-table WARN) + [ "$status" -ne 0 ] + [[ "$output" == *"codebase: lint:gum-table $REPO_ROOT/.mise/tasks"* ]] + [[ "$output" == *"codebase: lint:gum-table $REPO_ROOT/scripts"* ]] + [[ "$output" == *"WARN"*"tasks:build"* ]] + [[ "$output" == *"scripts:deploy"* ]] +} + @test "lint: parses multiline lint arrays" { write_config <<'EOF' [settings] From d98a783545ccf68d3a2b24d33b14ed40c25f85cd Mon Sep 17 00:00:00 2001 From: olavostauros Date: Thu, 25 Jun 2026 13:28:24 -0300 Subject: [PATCH 2/2] feat(lint): add lint groups for standard convention bundles MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Adds first-class lint groups/presets so repos can opt into convention bundles with a single @maintained-tool reference instead of enumerating every lint rule individually. Includes: - lib/lint-groups.sh — built-in group registry, expansion, and discoverability functions - lib/codebase-config.sh — source lint-groups.sh, expand @group references in codebase_configured_lint_rules(), support lint_exclude - .mise/tasks/lint/_default — show expanded group count in preamble; updated error hint to mention groups - .mise/tasks/lint/groups — new lint:groups discoverability task - test/lint/lint-groups/ — 17 BATS tests Closes #69 --- .mise/tasks/lint/_default | 14 ++ .mise/tasks/lint/groups | 53 +++++ lib/codebase-config.sh | 54 +++++- lib/lint-groups.sh | 99 ++++++++++ test/lint/lint-groups/lint-groups.bats | 258 +++++++++++++++++++++++++ 5 files changed, 474 insertions(+), 4 deletions(-) create mode 100755 .mise/tasks/lint/groups create mode 100644 lib/lint-groups.sh create mode 100644 test/lint/lint-groups/lint-groups.bats diff --git a/.mise/tasks/lint/_default b/.mise/tasks/lint/_default index f7c1dfb..bc9aafa 100755 --- a/.mise/tasks/lint/_default +++ b/.mise/tasks/lint/_default @@ -19,6 +19,13 @@ if [[ ! -f "$TOML" ]]; then exit 1 fi +# Check if the original config uses groups, for preamble display. +ORIG_LINT="$(mise config get -f "$TOML" _.codebase.lint 2>/dev/null || true)" +HAS_GROUPS=false +if [[ "$ORIG_LINT" == *"@"* ]]; then + HAS_GROUPS=true +fi + RULES=() while IFS= read -r rule; do [[ -n "$rule" ]] && RULES+=("$rule") @@ -29,9 +36,16 @@ if [[ ${#RULES[@]} -eq 0 ]]; then echo "Add to mise.toml:" >&2 echo ' [_.codebase]' >&2 echo ' lint = ["mise-settings", "gum-table"]' >&2 + echo " # Or use a preset group:" >&2 + echo ' # lint = ["@maintained-tool"]' >&2 + echo " # See 'mise run lint:groups' for available groups" >&2 exit 1 fi +if $HAS_GROUPS; then + echo "codebase: expanded ${#RULES[@]} rule(s) from lint groups" +fi + failures=0 FAILED=() diff --git a/.mise/tasks/lint/groups b/.mise/tasks/lint/groups new file mode 100755 index 0000000..78f895f --- /dev/null +++ b/.mise/tasks/lint/groups @@ -0,0 +1,53 @@ +#!/usr/bin/env bash +#MISE description="List available lint groups and their rule membership" +#USAGE flag "-a --all" help="Show expanded rule count for each group (default: only group names)" +#USAGE example "mise run lint:groups" header="List available groups" +#USAGE example "mise run lint:groups --all" header="Show groups with expanded rule details" + +set -euo pipefail + +# shellcheck source=../../../lib/lint-groups.sh +source "$MISE_CONFIG_ROOT/lib/lint-groups.sh" + +ALL="${usage_all:-false}" + +groups=() +while IFS= read -r g; do + [[ -n "$g" ]] && groups+=("$g") +done < <(codebase_available_groups) + +if [[ ${#groups[@]} -eq 0 ]]; then + echo "No lint groups defined." + exit 0 +fi + +echo "Available lint groups:" +echo "" + +for group in "${groups[@]}"; do + echo " $group" + if [[ "$ALL" == "true" ]]; then + members=() + while IFS= read -r rule; do + [[ -n "$rule" ]] && members+=("$rule") + done < <(codebase_group_members "$group") + for rule in "${members[@]}"; do + echo " - $rule" + done + echo "" + fi +done + +if [[ "$ALL" != "true" ]]; then + echo " (use --all to see expanded rules for each group)" +fi + +echo "" +echo "Usage in mise.toml:" +echo ' [_.codebase]' +echo ' lint = ["@maintained-tool"]' +echo "" +echo "Exclude a rule:" +echo ' [_.codebase]' +echo ' lint = ["@maintained-tool"]' +echo ' lint_exclude = ["caller-pwd-contract"]' \ No newline at end of file diff --git a/lib/codebase-config.sh b/lib/codebase-config.sh index 7b917a1..b1cf7e2 100644 --- a/lib/codebase-config.sh +++ b/lib/codebase-config.sh @@ -8,6 +8,8 @@ _CODEBASE_CONFIG_LIB_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" # shellcheck source=./shell-files.sh source "$_CODEBASE_CONFIG_LIB_DIR/shell-files.sh" +# shellcheck source=./lint-groups.sh +source "$_CODEBASE_CONFIG_LIB_DIR/lint-groups.sh" # codebase_git_root # @@ -56,8 +58,9 @@ codebase_resolve_repo() { # codebase_configured_lint_rules # -# Emit configured [_.codebase].lint rules, one per line. Rule names are expected -# to be simple task suffixes such as "mise-settings" or "gum-table". +# Emit configured [_.codebase].lint rules, one per line, with @group +# references expanded and any excluded rules removed. Rule names are simple +# task suffixes such as "mise-settings" or "gum-table". codebase_configured_lint_rules() { local repo_root="$1" local toml="$repo_root/mise.toml" @@ -69,10 +72,53 @@ codebase_configured_lint_rules() { return 0 fi - printf '%s\n' "$raw" | awk '{ + # Parse raw TOML array values into an array of rule names. + local -a parsed + while IFS= read -r token; do + [[ -n "$token" ]] && parsed+=("$token") + done < <(printf '%s\n' "$raw" | awk '{ gsub(/[\[\]",]/, " ") for (i = 1; i <= NF; i++) print $i - }' + }') + + # Expand @group references. + local -a expanded + if codebase_has_group_reference "${parsed[@]}"; then + while IFS= read -r rule; do + [[ -n "$rule" ]] && expanded+=("$rule") + done < <(codebase_expand_lint_groups "${parsed[@]}") + else + expanded=("${parsed[@]}") + fi + + # Apply [_.codebase].lint_exclude if present. + local excludes_raw + excludes_raw=$(mise config get -f "$toml" _.codebase.lint_exclude 2>/dev/null || true) + if [[ -n "$excludes_raw" ]]; then + local -a exclude_list + while IFS= read -r ex; do + local clean + clean=$(printf '%s' "$ex" | tr -d '[]," ') + [[ -n "$clean" ]] && exclude_list+=("$clean") + done < <(printf '%s\n' "$excludes_raw" | awk '{ + gsub(/[\[\]",]/, " ") + for (i = 1; i <= NF; i++) print $i + }') + + local -a filtered + local rule skip + for rule in "${expanded[@]}"; do + skip=false + for ex in "${exclude_list[@]}"; do + [[ "$rule" == "$ex" ]] && { skip=true; break; } + done + $skip && continue + filtered+=("$rule") + done + expanded=("${filtered[@]}") + fi + + printf '%s\n' "${expanded[@]}" } # codebase_default_scope_for_rule diff --git a/lib/lint-groups.sh b/lib/lint-groups.sh new file mode 100644 index 0000000..9fd2274 --- /dev/null +++ b/lib/lint-groups.sh @@ -0,0 +1,99 @@ +#!/usr/bin/env bash +# Built-in lint group definitions and expansion helpers. +# +# Groups are named bundles of lint rules that repos can reference with +# the @prefix, e.g. lint = ["@maintained-tool"]. This eliminates the +# need to enumerate every convention rule in every repo's mise.toml. +# +# To add a new group: +# 1. Define it in _CODEBASE_LINT_GROUPS below +# 2. List its rules (lowercase-hyphenated, matching .mise/tasks/lint/) +# 3. Update docs in AGENTS.md and/or README +# +# To add a new rule to an existing group: +# 1. Add the rule name to the group's string +# 2. Ensure member repos can opt out via [_.codebase].lint_exclude if needed + +# === Built-in group definitions === + +declare -A _CODEBASE_LINT_GROUPS + +# @maintained-tool — the standard convention bundle for maintained KKL +# tool repos (CLI tools, libraries, SDK wrappers). Includes all shared +# conventions that every maintained-tool repo should follow. +_CODEBASE_LINT_GROUPS["@maintained-tool"]="mise-settings gum-table bats-test-helper bats-test-task mcr-scope or-true shellcheck caller-pwd-contract github-actions" + +# Future groups: +# _CODEBASE_LINT_GROUPS["@minimal"]="mise-settings shellcheck" +# _CODEBASE_LINT_GROUPS["@ci-only"]="github-actions mise-settings" + +# === Public API === + +# codebase_expand_lint_groups ... +# +# Accepts one or more lint entries (rule names or @group references). +# Emits concrete rule names, one per line, expanding @-groups inline. +# Unknown groups produce an ERROR message on stderr and return 1. +# Duplicates are preserved (caller may deduplicate if needed). +codebase_expand_lint_groups() { + local entry expanded rules + + for entry in "$@"; do + if [[ "$entry" == @* ]]; then + expanded="${_CODEBASE_LINT_GROUPS[$entry]:-}" + if [[ -z "$expanded" ]]; then + echo "ERROR: unknown lint group '$entry'" >&2 + printf ' known groups:' >&2 + local g + for g in "${!_CODEBASE_LINT_GROUPS[@]}"; do + printf ' %s' "$g" >&2 + done + echo >&2 + return 1 + fi + # Word-split the expanded string: each token is a rule name. + # shellcheck disable=SC2086 — intentional word-splitting + for _rule in $expanded; do + printf '%s\n' "$_rule" + done + else + printf '%s\n' "$entry" + fi + done +} + +# codebase_available_groups +# +# Emit all known group names, one per line, suitable for help display. +codebase_available_groups() { + local name + for name in "${!_CODEBASE_LINT_GROUPS[@]}"; do + printf '%s\n' "$name" + done +} + +# codebase_group_members +# +# Emit the rule names belonging to a group, one per line. +# Returns 1 if the group is unknown. +codebase_group_members() { + local group="$1" + local members="${_CODEBASE_LINT_GROUPS[$group]:-}" + if [[ -z "$members" ]]; then + return 1 + fi + printf '%s\n' $members +} + +# codebase_has_group_reference ... +# +# Returns 0 if any entry starts with @, 1 otherwise. +codebase_has_group_reference() { + local entry + for entry in "$@"; do + if [[ "$entry" == @* ]]; then + return 0 + fi + done + return 1 +} \ No newline at end of file diff --git a/test/lint/lint-groups/lint-groups.bats b/test/lint/lint-groups/lint-groups.bats new file mode 100644 index 0000000..9aaee09 --- /dev/null +++ b/test/lint/lint-groups/lint-groups.bats @@ -0,0 +1,258 @@ +#!/usr/bin/env bats +# Tests for lint group expansion (lib/lint-groups.sh) and aggregate dispatch + +load ../../test_helper + +# Library-level tests (source lint-groups.sh directly) + +@test "lint-groups: codebase_available_groups lists @maintained-tool" { + source "$REPO_DIR/lib/lint-groups.sh" + run codebase_available_groups + [ "$status" -eq 0 ] + [[ "$output" == *"@maintained-tool"* ]] +} + +@test "lint-groups: codebase_group_members returns 9 rules for @maintained-tool" { + source "$REPO_DIR/lib/lint-groups.sh" + run codebase_group_members "@maintained-tool" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 9 ] + [[ "$output" == *"mise-settings"* ]] + [[ "$output" == *"gum-table"* ]] + [[ "$output" == *"bats-test-helper"* ]] + [[ "$output" == *"bats-test-task"* ]] + [[ "$output" == *"mcr-scope"* ]] + [[ "$output" == *"or-true"* ]] + [[ "$output" == *"shellcheck"* ]] + [[ "$output" == *"caller-pwd-contract"* ]] + [[ "$output" == *"github-actions"* ]] +} + +@test "lint-groups: codebase_group_members fails on unknown group" { + source "$REPO_DIR/lib/lint-groups.sh" + run codebase_group_members "@nonexistent" + [ "$status" -ne 0 ] +} + +@test "lint-groups: codebase_has_group_reference detects @ prefix" { + source "$REPO_DIR/lib/lint-groups.sh" + codebase_has_group_reference "@maintained-tool" "or-true" + [ "$?" -eq 0 ] +} + +@test "lint-groups: codebase_has_group_reference returns 1 when no @ present" { + source "$REPO_DIR/lib/lint-groups.sh" + run codebase_has_group_reference "mise-settings" "or-true" + [ "$status" -ne 0 ] +} + +@test "lint-groups: codebase_expand_lint_groups expands @maintained-tool to 9 rules" { + source "$REPO_DIR/lib/lint-groups.sh" + run codebase_expand_lint_groups "@maintained-tool" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 9 ] +} + +@test "lint-groups: codebase_expand_lint_groups passes through individual rules" { + source "$REPO_DIR/lib/lint-groups.sh" + run codebase_expand_lint_groups "or-true" "shellcheck" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 2 ] + [[ "$output" == *"or-true"* ]] + [[ "$output" == *"shellcheck"* ]] +} + +@test "lint-groups: codebase_expand_lint_groups mixes group + individual rules" { + source "$REPO_DIR/lib/lint-groups.sh" + run codebase_expand_lint_groups "@maintained-tool" "or-true" + [ "$status" -eq 0 ] + # 9 from group + 1 individual = 10 (or-true appears twice, preserved) + [ "${#lines[@]}" -eq 10 ] +} + +@test "lint-groups: codebase_expand_lint_groups errors on unknown group" { + source "$REPO_DIR/lib/lint-groups.sh" + run codebase_expand_lint_groups "@nonexistent" + [ "$status" -ne 0 ] + [[ "$output" == *"ERROR"* ]] + [[ "$output" == *"unknown lint group"* ]] +} + +@test "lint-groups: codebase_expand_lint_groups errors on unknown group mixed with valid" { + source "$REPO_DIR/lib/lint-groups.sh" + run codebase_expand_lint_groups "mise-settings" "@nonexistent" + [ "$status" -ne 0 ] + [[ "$output" == *"ERROR"* ]] + [[ "$output" == *"unknown lint group"* ]] +} + +# Aggregate dispatch — group expansion in codebase lint + +setup() { + REPO="$BATS_TEST_TMPDIR/repo" + mkdir -p "$REPO" + git -C "$REPO" init -q + REPO_ROOT="$(git -C "$REPO" rev-parse --show-toplevel)" +} + +write_config() { + cat > "$REPO/mise.toml" +} + +@test "lint: expands @maintained-tool group and shows preamble" { + # Create a repo with all the dirs/files that @maintained-tool rules need to pass + mkdir -p "$REPO/.mise/tasks" + mkdir -p "$REPO/.github/workflows" + cat > "$REPO/.github/workflows/test.yml" <<'EOF' +name: Test +on: [push] +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 +EOF + write_config <<'EOF' +[settings] +quiet = true +task_output = "interleave" + +[_.codebase] +lint = ["@maintained-tool"] +EOF + + run codebase lint "$REPO" + echo "STATUS=$status" + echo "OUTPUT=$output" + [ "$status" -eq 0 ] + [[ "$output" == *"codebase: expanded 9 rule(s) from lint groups"* ]] + # Verify all 9 rules appear in the output + [[ "$output" == *"codebase: lint:mise-settings"* ]] + [[ "$output" == *"codebase: lint:gum-table"* ]] + [[ "$output" == *"codebase: lint:bats-test-helper"* ]] + [[ "$output" == *"codebase: lint:bats-test-task"* ]] + [[ "$output" == *"codebase: lint:mcr-scope"* ]] + [[ "$output" == *"codebase: lint:or-true"* ]] + [[ "$output" == *"codebase: lint:shellcheck"* ]] + [[ "$output" == *"codebase: lint:caller-pwd-contract"* ]] + [[ "$output" == *"codebase: lint:github-actions"* ]] +} + +@test "lint: expands @maintained-tool group with pass-through for extra rules" { + mkdir -p "$REPO/.mise/tasks" + mkdir -p "$REPO/.github/workflows" + cat > "$REPO/.github/workflows/test.yml" <<'EOF' +name: Test +on: [push] +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 +EOF + write_config <<'EOF' +[settings] +quiet = true +task_output = "interleave" + +[_.codebase] +lint = ["@maintained-tool"] +EOF + + run codebase lint "$REPO" + [ "$status" -eq 0 ] + [[ "$output" == *"9 rule(s)"* ]] + [[ "$output" == *"codebase: all 9 lint rule(s) passed"* ]] +} + +@test "lint: lint_exclude removes a rule from expanded group" { + mkdir -p "$REPO/.mise/tasks" + mkdir -p "$REPO/.github/workflows" + cat > "$REPO/.github/workflows/test.yml" <<'EOF' +name: Test +on: [push] +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 +EOF + write_config <<'EOF' +[settings] +quiet = true +task_output = "interleave" + +[_.codebase] +lint = ["@maintained-tool"] +lint_exclude = ["caller-pwd-contract"] +EOF + + run codebase lint "$REPO" + echo "STATUS=$status" + echo "OUTPUT=$output" + [ "$status" -eq 0 ] + # 8 remaining rules + [[ "$output" == *"expanded 8 rule(s)"* ]] + [[ "$output" == *"codebase: all 8 lint rule(s) passed"* ]] + # caller-pwd-contract should NOT appear + [[ "$output" != *"lint:caller-pwd-contract"* ]] +} + +@test "lint: group + individual rules both shown in preamble count" { + mkdir -p "$REPO/.mise/tasks" + mkdir -p "$REPO/.github/workflows" + cat > "$REPO/.github/workflows/test.yml" <<'EOF' +name: Test +on: [push] +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 +EOF + write_config <<'EOF' +[settings] +quiet = true +task_output = "interleave" + +[_.codebase] +lint = ["@maintained-tool"] +EOF + + run codebase lint "$REPO" + [ "$status" -eq 0 ] + [[ "$output" == *"expanded 9 rule(s)"* ]] +} + +@test "lint: no-group pass-through still works (backward compat)" { + write_config <<'EOF' +[settings] +quiet = true +task_output = "interleave" + +[_.codebase] +lint = ["mise-settings"] +EOF + + run codebase lint "$REPO" + [ "$status" -eq 0 ] + # Should NOT show the groups preamble + [[ "$output" != *"expanded"* ]] + [[ "$output" == *"codebase: lint:mise-settings"* ]] + [[ "$output" == *"codebase: all 1 lint rule(s) passed"* ]] +} + +@test "lint: lint:groups task lists available groups" { + run codebase lint:groups + [ "$status" -eq 0 ] + [[ "$output" == *"@maintained-tool"* ]] + [[ "$output" == *"Usage in mise.toml"* ]] +} + +@test "lint: lint:groups --all shows expanded rules" { + run codebase lint:groups --all + [ "$status" -eq 0 ] + [[ "$output" == *"@maintained-tool"* ]] + [[ "$output" == *"- mise-settings"* ]] + [[ "$output" == *"- github-actions"* ]] +} \ No newline at end of file