Claude Skill

mix-compression

Reduce mix output noise (5-15% token savings) by installing rtk filters that compress mix test/credo/dialyzer/compile output before it reaches Claude. Use when long mix output floods context.

LLM Mart · 0 points · 0 views 0 listing impressions 0 install-command copies
Virus-scanned Reviewed automatically before listing.

Full trust report

Download oliver-kriska-claude-elixir-phoenix-plugins_elixir-phoenix_skills_mix-compression-9767a82.zip · 7 KB
Part of oliver-kriska/claude-elixir-phoenix — 93 skills

Install

skills CLI npx skills add https://github.com/oliver-kriska/claude-elixir-phoenix/tree/main/plugins/elixir-phoenix/skills/mix-compression
Claude Code claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install oliver-kriska-claude-elixir-phoenix@llmmart
Git git clone https://github.com/oliver-kriska/claude-elixir-phoenix.git

The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole oliver-kriska/claude-elixir-phoenix collection as a plugin from our marketplace. Git is the plain clone.

Skill manifest

Mix Output Compression

Mix commands (mix test, mix credo, mix dialyzer, mix compile) emit verbose, repetitive output that consumes context fast. This skill installs rtk — a CLI proxy that filters tool output before it lands in the transcript.

The filters short-circuit happy paths to a single line (mix test: all pass) while preserving full failure blocks, compile errors, and stack traces. Net win: 5-15% per-session token reduction on mix-heavy workflows.

When to use

  • Long sessions — /phx:work or /phx:full hitting context limits from mix output
  • Debugging loops — /phx:investigate retrying mix compile/mix test repeatedly
  • Dialyzer-heavy projects — mix dialyzer output dominates the transcript

Iron Laws

  1. NEVER strip critical signals — compile errors (** (CompileError), == Compilation error in), test failures (FAILURES, 0 failures — preserved even on short-circuit), dialyzer warnings, and stack traces with file:line MUST pass through unchanged
  2. Verify after install — run rtk verify (or rtk verify --filter mix-test) to confirm the bundled test fixtures pass before declaring success
  3. Never overwrite existing .rtk/filters.toml — diff and merge instead

Workflow

Step 1: Detect rtk

which rtk && rtk --version

Read ${CLAUDE_SKILL_DIR}/references/install.md if rtk is missing — covers homebrew install + shell hook setup.

Step 2: Seed .rtk/filters.toml

Reference filters live at ${CLAUDE_SKILL_DIR}/references/rtk-filters.toml. Six production-tested filters covering:

  • mix-test — short-circuits all-pass, preserves failure blocks + compile errors
  • mix-credo — collapses clean runs, preserves violation blocks
  • mix-dialyzer — drops PLT progress, keeps warnings + summary
  • mix-deps-get — collapses unchanged package lists
  • mix-ecto-migrate — strips compile prefix, short-circuits "already up"
  • mix-compile — handles parallel worker prefixes (N>) and MIX_ENV

Run this if the project has no .rtk/filters.toml yet:

mkdir -p .rtk
cp "${CLAUDE_SKILL_DIR}/references/rtk-filters.toml" .rtk/filters.toml

Read both files if one already exists. Present a diff to the user. Merge only the filters they don't already have.

Step 3: Verify filters work

rtk verify                       # runs all embedded [[tests.*]] fixtures
rtk verify --filter mix-test     # one filter only

Check that all report "passed". Flag and stop if any fail — usually means the user has a custom rtk version with regex differences.

Step 4: Confirm shell hook

Run rtk init zsh (or rtk init bash) to install the transparent rewrite hook that turns mix X into rtk mix X. Re-running is safe (idempotent). Skip this step and mix calls run unfiltered.

Customization

Add custom regex patterns to strip_lines_matching for project-specific noise sources (e.g., third-party hex deps spamming stack traces). See the inline example in ${CLAUDE_SKILL_DIR}/references/rtk-filters.toml lines 57-59.

What this is NOT

  • Not a hook — Claude Code's PostToolUse hooks fire after the tool result is in the transcript and cannot shrink it. rtk works at the subprocess layer (the only layer where transcript-shortening is possible).
  • Not project-analysis — the bundled filter set is universal across Phoenix projects. No mix.exs inspection needed.
  • Not telemetry — rtk has telemetry off by default (enabled = false in config.toml). Filters run locally, no data leaves the machine.

References

  • ${CLAUDE_SKILL_DIR}/references/rtk-filters.toml — bundled filter set
  • ${CLAUDE_SKILL_DIR}/references/install.md — rtk install + shell hook setup
  • rtk on GitHub
Files (claude-elixir-phoenix)
  • references
    • install.md 3.3 KB
      # Installing rtk
      
      [rtk (Rust Token Killer)](https://github.com/rtk-ai/rtk) is a CLI proxy that
      intercepts terminal commands, runs them, and filters output through TOML rules
      before returning to the caller. For Claude Code, this means mix output gets
      compressed *before* it reaches the transcript — the only architectural layer
      where token savings are possible.
      
      ## Install
      
      ### macOS (homebrew)
      
      ```bash
      brew install rtk-ai/tap/rtk
      rtk --version    # verify (expect 0.39.0+)
      ```
      
      ### Other platforms
      
      See the [rtk README](https://github.com/rtk-ai/rtk#install) for Linux and
      manual install options.
      
      ## Shell hook (required)
      
      rtk only intercepts commands when its shell hook is installed. The hook
      rewrites `mix X` to `rtk mix X` transparently — no manual prefixing.
      
      ```bash
      rtk init zsh  # or `rtk init bash`
      ```
      
      This adds a hook to `~/.zshrc` (or `~/.bashrc`). Reload your shell:
      
      ```bash
      exec $SHELL -l
      ```
      
      Verify the hook works:
      
      ```bash
      mix --help    # should run normally — rtk passes through commands without filters
      ```
      
      If you see `rtk: command not found` or `mix` runs raw without filtering, the
      hook didn't install. Check `~/.zshrc` for an `rtk init` block.
      
      ## Verify filters
      
      After dropping filters into `.rtk/filters.toml`:
      
      ```bash
      rtk verify                              # run all embedded [[tests.*]] fixtures
      rtk verify --filter mix-test            # run fixtures for one filter only
      rtk verify --filter mix-ash-codegen
      ```
      
      Each filter should report all fixtures as passed. If any fail, the regex
      patterns may not match your rtk version's regex engine — file an issue on
      the rtk repo.
      
      ## Measure the win
      
      ```bash
      rtk gain                  # token savings summary
      rtk gain --history        # per-command breakdown
      rtk cc-economics          # spending vs savings analysis
      ```
      
      Run after a week of normal usage to see actual reduction. Expected ranges
      for Phoenix projects:
      
      | Command | Typical compression | Big win cases |
      |---------|-------------------:|---------------|
      | `mix test` (passing) | 90%+ | All-green runs collapse to one line |
      | `mix credo` (clean) | 95%+ | Clean runs collapse to one line |
      | `mix dialyzer` (clean) | 95%+ | PLT progress disappears |
      | `mix compile` (no warnings) | 70-90% | Worker-prefix noise stripped |
      | `mix test` (failures) | 10-30% | Failure blocks preserved verbatim |
      
      ## Telemetry
      
      rtk has telemetry **disabled by default**. Verify in
      `~/Library/Application Support/rtk/config.toml`:
      
      ```toml
      [telemetry]
      enabled = false
      consent_given = false
      ```
      
      Filters run locally. No command output, file paths, or filter content leaves
      your machine.
      
      ## Disable / uninstall
      
      To disable temporarily without uninstalling:
      
      ```bash
      # Remove the rtk init line from ~/.zshrc, then:
      exec $SHELL -l
      ```
      
      To uninstall:
      
      ```bash
      brew uninstall rtk
      rm -rf ~/Library/Application\ Support/rtk
      rm -rf .rtk    # in any project that has one
      ```
      
      ## Troubleshooting
      
      **Filter not applying**: rtk only filters when the command matches a filter's
      `match_command` regex. Check `rtk config show-filters` to list registered
      filters in the current directory.
      
      **Test fixtures fail after copying**: rtk uses Rust's regex crate which does
      NOT support backreferences (`\1`, `\2`). If you wrote your own patterns, avoid
      them.
      
      **Want raw output for one command**: prefix with `command`:
      
      ```bash
      command mix test    # bypasses rtk
      ```
      
    • rtk-filters.toml 15.6 KB
      schema_version = 1
      
      # RTK TOML filters for Elixir mix commands.
      #
      # Drop into `.rtk/filters.toml` at the project root. Filters apply when mix
      # commands run via `rtk mix ...` (rtk's shell hook rewrites `mix X` -> `rtk mix X`
      # transparently — see references/install.md).
      #
      # Design notes:
      # - Every mix-* filter includes shared compile-strip patterns because parallel
      #   deps.compile often runs as a side effect, emitting "Compiling N files" and
      #   "Generated X app" noise with worker-prefix form `N> `.
      # - filter_stderr = true because mix emits warnings to stderr; we want them
      #   filtered through the same pipeline, not leaking raw.
      # - match_output short-circuits the "happy path" to a one-liner before any
      #   other processing — biggest wins for `mix test` (passing runs are the norm).
      # - Rust regex crate (RTK's engine) does NOT support backreferences (\1, \2).
      #   Any pattern using them will silently fail.
      # - Critical-preserve: compile errors, test failures, dialyzer warnings, and
      #   stack traces with file:line are NEVER stripped — only progress noise is.
      
      # ============================================================================
      # mix test — compact pass/fail, preserve failure blocks, stacktraces, compile errors
      # ============================================================================
      [filters.mix-test]
      description = "Compact mix test — short-circuit all-green, preserve failures and compile errors"
      match_command = "^(?:[A-Z_][A-Z0-9_]*=\\S+\\s+)*mix\\s+test(\\s|$)"
      strip_ansi = true
      filter_stderr = true
      
      match_output = [
        { pattern = "\\b0 failures\\b", message = "mix test: all pass" },
      ]
      
      replace = [
        { pattern = "^[.*sF]{3,}", replacement = "" },
      ]
      
      strip_lines_matching = [
        "^$",
        "^\\s*$",
        "^Excluding tags:",
        "^Including tags:",
        "^Randomized with seed",
        "^OK$",
        "^Compiling \\d+ file",
        "^Generated \\w+ app$",
        "^\\d+> Compiling \\d+ file",
        "^\\d+> Generated \\w+ app",
        "^\\d+> ==> \\w+",
        "^\\d+> ===> (Compiling|Analyzing)",
        "^==> \\w+$",
        "^build ended with \\d+ errors$",
        "^\\d{2}:\\d{2}:\\d{2}\\.\\d+ ",
        "^\\d{2}:\\d{2}:\\d{2}\\.\\d+ app_version=",
        "^\\d{2}:\\d{2}:\\d{2}\\.\\d+ line=\\d+",
        # Stack-trace frame strippers — drop frames from libraries you don't care about.
        # Add your own deps here: e.g., "^\\s+\\(my_dep \\d" strips lines like "    (my_dep 1.2.3) ..."
        "^\\s+\\(phoenix_live_view \\d",
        "^\\s+\\(elixir \\d",
        "^\\s+\\(ecto \\d",
        "^warning: Range\\.new",
        "^warning: redefining module ",
        "^Query: INSERT INTO",
        "^Query: UPDATE",
        "^Query: SELECT",
        "^Params: ",
        "^Reason: %Postgrex",
        "^Function: #Function<",
        "^    Args: \\[\\]",
      ]
      
      tail_lines = 200
      on_empty = "mix test: ok"
      
      [[tests.mix-test]]
      name = "all pass short-circuits"
      input = """
      Running ExUnit with seed: 986129, max_cases: 28
      Excluding tags: [:ci_only]
      
      .
      Finished in 1.1 seconds (0.00s async, 1.1s sync)
      1 test, 0 failures
      
      OK
      """
      expected = "mix test: all pass"
      
      [[tests.mix-test]]
      name = "compile error preserved"
      input = """
      Running ExUnit with seed: 755757, max_cases: 28
      Excluding tags: [:ci_only]
      
          error: undefined variable ^b_item
          │
       20 │       assert [{^a_item, ^b_item}] = [Enum.at(a, 0)]
          │                          ^
          │
          └─ test/validation_match_test.exs:20:26: ValidationMatchTest
      
      == Compilation error in file test/validation_match_test.exs ==
      ** (CompileError) test/validation_match_test.exs: cannot compile module
      """
      expected = """Running ExUnit with seed: 755757, max_cases: 28
          error: undefined variable ^b_item
          │
       20 │       assert [{^a_item, ^b_item}] = [Enum.at(a, 0)]
          │                          ^
          │
          └─ test/validation_match_test.exs:20:26: ValidationMatchTest
      == Compilation error in file test/validation_match_test.exs ==
      ** (CompileError) test/validation_match_test.exs: cannot compile module"""
      
      # ============================================================================
      # mix credo — drop redefining-module warnings, progress lines, keep violations
      # ============================================================================
      [filters.mix-credo]
      description = "Compact mix credo — drop custom-check redefine warnings and progress"
      match_command = "^(?:[A-Z_][A-Z0-9_]*=\\S+\\s+)*mix\\s+credo(\\s|$)"
      strip_ansi = true
      filter_stderr = true
      
      match_output = [
        { pattern = "found no issues\\.", message = "mix credo: clean" },
      ]
      
      strip_lines_matching = [
        "^$",
        "^\\s*$",
        "^OK$",
        "^Compiling \\d+ file",
        "^Generated \\w+ app$",
        "^\\d+> Compiling",
        "^\\d+> Generated",
        "^\\s*warning: redefining module ",
        "^\\s+│$",
        "^\\s+~+$",
        "^  \\d+ │ defmodule ",
        "^\\s+└─ lib/mix/credo/",
        "^Checking \\d+ source files",
        "^Please report incorrect results:",
        "^Analysis took \\d",
        "^Use `mix credo explain`",
      ]
      
      on_empty = "mix credo: clean"
      
      [[tests.mix-credo]]
      name = "clean run short-circuits"
      input = """
          warning: redefining module MyApp.Test.Credo.WarnApplicationConfig
          │
        1 │ defmodule MyApp.Test.Credo.WarnApplicationConfig do
          │ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          │
          └─ lib/mix/credo/warn_application_config.ex:1: MyApp.Test.Credo.WarnApplicationConfig (module)
      
      Checking 2579 source files (this might take a while) ...
      Analysis took 13.4 seconds
      29543 mods/funs, found no issues.
      
      Use `mix credo explain` to explain issues.
      OK
      """
      expected = "mix credo: clean"
      
      [[tests.mix-credo]]
      name = "violation block preserved"
      input = """
      Compiling 2 files (.ex)
      Generated my_app app
      
        Code Readability
      ┃
      ┃ [R] ↗ Variable names should be written in snake_case.
      ┃       config/runtime.exs:16:1 #()
      ┃ [R] ↘ Line is too long (max is 120, was 158).
      ┃       config/runtime.exs:85:121 #()
      
      Please report incorrect results: https://github.com/rrrene/credo/issues
      Analysis took 0.3 seconds
      0 mods/funs, found 2 code readability issues.
      
      Use `mix credo explain` to explain issues.
      """
      expected = """  Code Readability
      ┃
      ┃ [R] ↗ Variable names should be written in snake_case.
      ┃       config/runtime.exs:16:1 #()
      ┃ [R] ↘ Line is too long (max is 120, was 158).
      ┃       config/runtime.exs:85:121 #()
      0 mods/funs, found 2 code readability issues."""
      
      # ============================================================================
      # mix dialyzer — drop PLT progress and config dumps, keep warnings + summary
      # ============================================================================
      [filters.mix-dialyzer]
      description = "Compact mix dialyzer — drop PLT progress, keep warnings and summary"
      match_command = "^(?:[A-Z_][A-Z0-9_]*=\\S+\\s+)*mix\\s+dialyzer(\\s|$)"
      strip_ansi = true
      filter_stderr = true
      
      match_output = [
        { pattern = "done \\(passed successfully\\)", message = "mix dialyzer: clean" },
      ]
      
      strip_lines_matching = [
        "^$",
        "^\\s*$",
        "^OK$",
        "^Compiling \\d+ file",
        "^Generated \\w+ app$",
        "^Finding suitable PLTs",
        "^Checking PLT\\.\\.\\.",
        "^Looking up modules in ",
        "^Finding applications for ",
        "^Finding modules for ",
        "^Checking \\d+ modules in ",
        "^Adding \\d+ modules to ",
        "^Removing \\d+ modules from ",
        "^done in \\d+m?\\d+",
        "^ignore_warnings: ",
        "^Starting Dialyzer",
        "^\\[$",
        "^  check_plt: ",
        "^  init_plt: ",
        "^  files: ",
        "^   ~c\"",
        "^   \\.\\.\\.\\],",
        "^  \\.\\.\\.",
        "^\\]$",
        "^\\[:[a-z_]+,",
        "^_{40,}$",
        "^Halting VM with exit status \\d",
      ]
      
      on_empty = "mix dialyzer: clean"
      
      [[tests.mix-dialyzer]]
      name = "clean run short-circuits"
      input = """
      Generated my_app app
      Finding suitable PLTs
      Checking PLT...
      [:abnf_parsec, :acceptor_pool, :algoliax, :apollo_io]
      Looking up modules in dialyxir_erlang-_elixir-_deps-dev.plt
      Checking 9618 modules in dialyxir_erlang-_elixir-_deps-dev.plt
      Adding 223 modules to dialyxir_erlang-_elixir-_deps-dev.plt
      done in 0m51.82s
      ignore_warnings: .dialyzer_ignore_warnings.exs
      
      Starting Dialyzer
      [
        check_plt: false,
        init_plt: ~c"priv/plts/dev/dialyxir_erlang.plt",
        files: [~c"/Users/.../my_app.beam",
         ...],
        ...
      ]
      Total errors: 156, Skipped: 156, Unnecessary Skips: 0
      done in 0m27.35s
      done (passed successfully)
      OK
      """
      expected = "mix dialyzer: clean"
      
      [[tests.mix-dialyzer]]
      name = "warnings preserved dividers stripped"
      input = """
      Total errors: 156, Skipped: 154, Unnecessary Skips: 0
      done in 0m40.32s
      ________________________________________________________________________________
      lib/my_app/reports/excel_formatting.ex:407:14:unknown_function
      Function Elixlsx.Sheet.set_col_width/3 does not exist.
      ________________________________________________________________________________
      lib/my_app/reports/excel_formatting.ex:418:13:unknown_function
      Function Elixlsx.Sheet.set_col_width/3 does not exist.
      ________________________________________________________________________________
      done (warnings were emitted)
      Halting VM with exit status 2
      """
      expected = """Total errors: 156, Skipped: 154, Unnecessary Skips: 0
      lib/my_app/reports/excel_formatting.ex:407:14:unknown_function
      Function Elixlsx.Sheet.set_col_width/3 does not exist.
      lib/my_app/reports/excel_formatting.ex:418:13:unknown_function
      Function Elixlsx.Sheet.set_col_width/3 does not exist.
      done (warnings were emitted)"""
      
      # ============================================================================
      # mix deps.get — collapse "Unchanged" package list
      # ============================================================================
      [filters.mix-deps-get]
      description = "Compact mix deps.get — collapse unchanged package lists"
      match_command = "^(?:[A-Z_][A-Z0-9_]*=\\S+\\s+)*mix\\s+deps\\.get(\\s|$)"
      strip_ansi = true
      filter_stderr = true
      
      match_output = [
        { pattern = "All dependencies (have been fetched|are up to date)", message = "mix deps.get: unchanged" },
      ]
      
      strip_lines_matching = [
        "^$",
        "^\\s*$",
        "^OK$",
        "^Resolving Hex dependencies",
        "^Resolution completed in ",
        "^Unchanged:$",
        "^  [a-z_][a-z0-9_]* \\d+\\.\\d+",
        "^Compiling \\d+ file",
        "^Generated \\w+ app$",
      ]
      
      on_empty = "mix deps.get: unchanged"
      
      [[tests.mix-deps-get]]
      name = "unchanged collapses"
      input = """
      Resolving Hex dependencies...
      Resolution completed in 1.638s
      Unchanged:
        abnf_parsec 2.1.0
        acceptor_pool 1.0.1
        algoliax 0.10.0
        credo 1.7.18
      All dependencies have been fetched
      OK
      """
      expected = "mix deps.get: unchanged"
      
      # ============================================================================
      # mix ecto.migrate / rollback / gen.migration — strip compile prefix
      # ============================================================================
      [filters.mix-ecto-migrate]
      description = "Compact mix ecto.migrate — strip compile prefix"
      match_command = "^(?:[A-Z_][A-Z0-9_]*=\\S+\\s+)*mix\\s+ecto\\.(migrate|rollback|gen\\.migration)(\\s|$)"
      strip_ansi = true
      filter_stderr = true
      
      match_output = [
        { pattern = "\\[info\\] Migrations already up", message = "mix ecto.migrate: up to date" },
      ]
      
      strip_lines_matching = [
        "^$",
        "^\\s*$",
        "^OK$",
        "^Compiling \\d+ file",
        "^Generated \\w+ app$",
        "^\\d+> Compiling",
        "^\\d+> Generated",
      ]
      
      on_empty = "mix ecto.migrate: ok"
      
      [[tests.mix-ecto-migrate]]
      name = "already up short-circuits"
      input = """
      Compiling 2 files (.ex)
      Generated my_app app
      
      06:54:27.843 [info] Migrations already up
      """
      expected = "mix ecto.migrate: up to date"
      
      # ============================================================================
      # mix compile (extended) — handles parallel worker prefix `N> ` and MIX_ENV
      # Shadows the built-in mix-compile filter.
      # ============================================================================
      [filters.mix-compile]
      description = "Compact mix compile — handles parallel workers and MIX_ENV prefixes"
      match_command = "^(?:[A-Z_][A-Z0-9_]*=\\S+\\s+)*mix\\s+compile(\\s|$)"
      strip_ansi = true
      filter_stderr = true
      
      replace = [
        { pattern = "^\\d+> ", replacement = "" },
      ]
      
      strip_lines_matching = [
        "^$",
        "^\\s*$",
        "^OK$",
        "^mix deps\\.compile running across \\d+ OS processes",
        "^Compiling \\d+ file",
        "^Generated \\w+ app$",
        "^==> \\w+$",
        "^===> (Compiling|Analyzing) ",
        "^\\[debug\\] Copying NIF ",
        "^AppSignal extension installation",
      ]
      
      on_empty = "mix compile: ok"
      
      [[tests.mix-compile]]
      name = "parallel compile with worker prefix strips cleanly"
      input = """
      mix deps.compile running across 7 OS processes
      2> ==> peri
      2> Compiling 7 files (.ex)
      3> ==> fine
      3> Compiling 1 file (.ex)
      3> Generated fine app
      2> Generated peri app
      Compiling 1821 files (.ex)
      Generated my_app app
      OK
      """
      expected = "mix compile: ok"
      
      [[tests.mix-compile]]
      name = "preserves warning with file:line"
      input = """
      2> ==> peri
      2> Compiling 7 files (.ex)
      3>     warning: variable "assigns" is unused
      3>     │
      3> 183 │     defp prepare_icon_name(assigns) do
      3>     │                            ~~~~~~~
      3>     │
      3>     └─ lib/icon.ex:183:28: Iconify.Icon.prepare_icon_name/1
      2> Generated peri app
      Compiling 1821 files (.ex)
      Generated my_app app
      """
      expected = """    warning: variable "assigns" is unused
          │
      183 │     defp prepare_icon_name(assigns) do
          │                            ~~~~~~~
          │
          └─ lib/icon.ex:183:28: Iconify.Icon.prepare_icon_name/1"""
      
      # ============================================================================
      # mix ash.codegen — strip compile prefix, preserve snapshot/migration lists
      # ============================================================================
      [filters.mix-ash-codegen]
      description = "Compact mix ash.codegen — strip compile prefix, preserve snapshot and migration file lists"
      match_command = "^(?:[A-Z_][A-Z0-9_]*=\\S+\\s+)*mix\\s+ash\\.codegen(\\s|$)"
      strip_ansi = true
      filter_stderr = true
      
      match_output = [
        { pattern = "No changes", message = "mix ash.codegen: no changes" },
      ]
      
      strip_lines_matching = [
        "^$",
        "^\\s*$",
        "^OK$",
        "^Compiling \\d+ file",
        "^Generated \\w+ app$",
        "^\\d+> Compiling",
        "^\\d+> Generated",
      ]
      
      on_empty = "mix ash.codegen: ok"
      
      [[tests.mix-ash-codegen]]
      name = "no changes short-circuits"
      input = """
      Compiling 5 files (.ex)
      Generated my_app app
      
      No changes detected.
      OK
      """
      expected = "mix ash.codegen: no changes"
      
      [[tests.mix-ash-codegen]]
      name = "snapshot and migration list preserved"
      input = """
      Compiling 5 files (.ex)
      Generated my_app app
      
      Snapshots Updated:
        * priv/resource_snapshots/repo/accounts/user_v2.json
      
      Migrations Generated:
        * priv/repo/migrations/20260511120000_migrate_resources2.exs
      """
      expected = """Snapshots Updated:
        * priv/resource_snapshots/repo/accounts/user_v2.json
      Migrations Generated:
        * priv/repo/migrations/20260511120000_migrate_resources2.exs"""
      
      # ============================================================================
      # mix ash.migrate — strip compile prefix, preserve migration steps
      # ============================================================================
      [filters.mix-ash-migrate]
      description = "Compact mix ash.migrate — strip compile prefix, preserve migration steps"
      match_command = "^(?:[A-Z_][A-Z0-9_]*=\\S+\\s+)*mix\\s+ash\\.(migrate|rollback)(\\s|$)"
      strip_ansi = true
      filter_stderr = true
      
      match_output = [
        { pattern = "\\[info\\] Migrations already up", message = "mix ash.migrate: up to date" },
      ]
      
      strip_lines_matching = [
        "^$",
        "^\\s*$",
        "^OK$",
        "^Compiling \\d+ file",
        "^Generated \\w+ app$",
        "^\\d+> Compiling",
        "^\\d+> Generated",
      ]
      
      on_empty = "mix ash.migrate: ok"
      
      [[tests.mix-ash-migrate]]
      name = "already up short-circuits"
      input = """
      Compiling 2 files (.ex)
      Generated my_app app
      
      06:54:27.843 [info] Migrations already up
      """
      expected = "mix ash.migrate: up to date"
      
      [[tests.mix-ash-migrate]]
      name = "migration steps preserved"
      input = """
      Compiling 2 files (.ex)
      Generated my_app app
      
      07:00:00.000 [info] == Running 20260511120000 MyApp.Repo.Migrations.MigrateResources2.up/0 forward
      07:00:00.000 [info] == Migrated 20260511120000 in 0.0s
      """
      expected = """07:00:00.000 [info] == Running 20260511120000 MyApp.Repo.Migrations.MigrateResources2.up/0 forward
      07:00:00.000 [info] == Migrated 20260511120000 in 0.0s"""
      
  • SKILL.md 4 KB
    ---
    name: mix-compression
    description: Reduce mix output noise (5-15% token savings) by installing rtk filters that compress mix test/credo/dialyzer/compile output before it reaches Claude. Use when long mix output floods context.
    effort: low
    ---
    
    # Mix Output Compression
    
    Mix commands (`mix test`, `mix credo`, `mix dialyzer`, `mix compile`) emit
    verbose, repetitive output that consumes context fast. This skill installs
    [rtk](https://github.com/rtk-ai/rtk) — a CLI proxy that filters tool output
    **before it lands in the transcript**.
    
    The filters short-circuit happy paths to a single line (`mix test: all pass`)
    while preserving full failure blocks, compile errors, and stack traces. Net win:
    5-15% per-session token reduction on mix-heavy workflows.
    
    ## When to use
    
    - **Long sessions** — `/phx:work` or `/phx:full` hitting context limits from mix output
    - **Debugging loops** — `/phx:investigate` retrying `mix compile`/`mix test` repeatedly
    - **Dialyzer-heavy projects** — `mix dialyzer` output dominates the transcript
    
    ## Iron Laws
    
    1. **NEVER strip critical signals** — compile errors (`** (CompileError)`,
       `== Compilation error in`), test failures (`FAILURES`, `0 failures` — preserved
       even on short-circuit), dialyzer warnings, and stack traces with `file:line` MUST
       pass through unchanged
    2. **Verify after install** — run `rtk verify` (or `rtk verify --filter mix-test`) to
       confirm the bundled test fixtures pass before declaring success
    3. **Never overwrite existing `.rtk/filters.toml`** — diff and merge instead
    
    ## Workflow
    
    ### Step 1: Detect rtk
    
    ```bash
    which rtk && rtk --version
    ```
    
    Read `${CLAUDE_SKILL_DIR}/references/install.md` if rtk is missing — covers
    homebrew install + shell hook setup.
    
    ### Step 2: Seed `.rtk/filters.toml`
    
    Reference filters live at `${CLAUDE_SKILL_DIR}/references/rtk-filters.toml`. Six
    production-tested filters covering:
    
    - **`mix-test`** — short-circuits all-pass, preserves failure blocks + compile errors
    - **`mix-credo`** — collapses clean runs, preserves violation blocks
    - **`mix-dialyzer`** — drops PLT progress, keeps warnings + summary
    - **`mix-deps-get`** — collapses unchanged package lists
    - **`mix-ecto-migrate`** — strips compile prefix, short-circuits "already up"
    - **`mix-compile`** — handles parallel worker prefixes (`N>`) and MIX_ENV
    
    Run this if the project has no `.rtk/filters.toml` yet:
    
    ```bash
    mkdir -p .rtk
    cp "${CLAUDE_SKILL_DIR}/references/rtk-filters.toml" .rtk/filters.toml
    ```
    
    Read both files if one already exists. Present a diff to the user. Merge only
    the filters they don't already have.
    
    ### Step 3: Verify filters work
    
    ```bash
    rtk verify                       # runs all embedded [[tests.*]] fixtures
    rtk verify --filter mix-test     # one filter only
    ```
    
    Check that all report "passed". Flag and stop if any fail — usually means the
    user has a custom rtk version with regex differences.
    
    ### Step 4: Confirm shell hook
    
    Run `rtk init zsh` (or `rtk init bash`) to install the transparent rewrite hook
    that turns `mix X` into `rtk mix X`. Re-running is safe (idempotent). Skip this
    step and `mix` calls run unfiltered.
    
    ## Customization
    
    Add custom regex patterns to `strip_lines_matching` for project-specific noise
    sources (e.g., third-party hex deps spamming stack traces). See the inline
    example in `${CLAUDE_SKILL_DIR}/references/rtk-filters.toml` lines 57-59.
    
    ## What this is NOT
    
    - **Not a hook** — Claude Code's `PostToolUse` hooks fire after the tool result
      is in the transcript and cannot shrink it. rtk works at the subprocess layer
      (the only layer where transcript-shortening is possible).
    - **Not project-analysis** — the bundled filter set is universal across Phoenix
      projects. No `mix.exs` inspection needed.
    - **Not telemetry** — rtk has telemetry off by default (`enabled = false` in
      `config.toml`). Filters run locally, no data leaves the machine.
    
    ## References
    
    - `${CLAUDE_SKILL_DIR}/references/rtk-filters.toml` — bundled filter set
    - `${CLAUDE_SKILL_DIR}/references/install.md` — rtk install + shell hook setup
    - [rtk on GitHub](https://github.com/rtk-ai/rtk)
    

Comments (0)

Sign in to join the conversation.

No comments yet.

Reviews (0)

No reviews yet.

Related