rtk-ai/rtk

feat: task runner support (mise, just, task) — intelligent output filtering and delegation

Open

#607 opened on 2026年3月15日

GitHub で見る
 (6 comments) (6 reactions) (0 assignees)Rust (2,914 forks)batch import
area:clieffort-largeenhancementhelp wantedpriority:high

Repository metrics

Stars
 (48,085 stars)
PR merge metrics
 (平均マージ 11d 1h) (30d で 45 merged PRs)

説明

Problem

Task runners hide underlying commands from RTK's hook system.

When a user runs mise run lint, RTK's hook sees mise — not biome check --write. Result: 0% token savings for task-runner-heavy workflows. The filtering intelligence built into RTK never activates because the hook can't see through the abstraction layer.

Context

Community report from a contributor who wraps all commands in mise run <task>. Same gap applies to just, task (go-task), and any abstraction layer. make already has basic TOML filter support but the problem is identical.

This is a growing issue given mise adoption in AI-assisted coding workflows — where task runners are increasingly the standard entry point for dev operations.

Task Runner Introspection Capabilities

Runner Config file Dry-run JSON dump Parse difficulty
mise mise.toml[tasks.<name>].run No native flag mise tasks ls --json Easy (TOML)
just justfile just --dry-run <recipe> just --dump --dump-format json Easy (JSON)
task Taskfile.yml task --dry <task> task --list --json Medium (YAML)
make Makefile make -n <target> No Hard (not worth parsing)

Implementation Plan

Phase 1 — TOML Filters + Registry (quick win, ~2-3h)

Strip task runner boilerplate from output via TOML filters. Register patterns in the rewrite registry so the hook intercepts task runner commands.

New files:

  • src/filters/mise.toml — strip [mise] banners, $ command echoes, blank lines
  • src/filters/just.toml — strip recipe echo lines, timing output
  • src/filters/taskfile.toml — strip task: [Name] headers, timing output

Modified files:

  • src/discover/rules.rs — add 3 patterns (^mise\s+run\b, ^just\s+, ^task\s+) + 3 rules
  • CHANGELOG.md

Impact: 30-50% savings from boilerplate removal. Hook rewrites mise run lintrtk mise run lint. TOML filter matches in run_fallback().

Ships independently. Zero breaking changes.


Phase 2 — Dedicated Rust Module with Routing (core, ~4-6h)

Single src/task_runner_cmd.rs module that:

  1. Runs the task runner command, captures stdout/stderr
  2. Resolves the underlying tool via config file introspection
  3. Routes output through the specialized filter_*() function (mirrors npx pattern)
  4. Falls back to TOML filter if no resolution possible

Architecture: Run the task runner (preserves env setup, hooks, dep ordering), then filter captured output through the right RTK filter function. RTK never bypasses the task runner.

Delegation logic:

underlying tool detected → delegate to specialized filter
  biome/eslint          → lint_cmd::filter_lint_output()
  tsc                   → tsc_cmd::filter_tsc_output()
  cargo test            → runner test filter
  vitest/jest           → vitest_cmd filter
  pytest                → pytest_cmd filter
  unknown               → TOML filter fallback → summary heuristic → passthrough

New files:

  • src/task_runner_cmd.rs — unified module with TaskRunner enum (Mise/Just/Task)
  • tests/fixtures/mise_run_lint_raw.txt, just_lint_raw.txt

Modified files:

  • src/main.rs — add Mise, Just, Task to Commands enum + routing
  • src/lint_cmd.rs, src/tsc_cmd.rs, etc. — expose filter_*() functions as pub
  • src/toml_filter.rs — add mise/just/task to RUST_HANDLED_COMMANDS

Needs Phase 1. Ships independently after Phase 1.


Phase 3 — Config Introspection + User Mappings (smart, ~3-4h)

Auto-parse task runner config files to resolve task → command mappings. Add user-configurable overrides.

Resolution priority:

  1. User config [task_runners.mappings] in config.toml (explicit override, always wins)
  2. Config file introspection (mise.tomltasks.lint.run, just --dump --dump-format json)
  3. TOML filter for boilerplate stripping
  4. summary heuristic detection
  5. Raw passthrough

Config example (~/.config/rtk/config.toml):

[task_runners.mappings]
lint = "biome check"
test = "cargo test"
typecheck = "tsc --noEmit"

Modified files:

  • src/config.rs — add TaskRunnerConfig with mappings: HashMap<String, String>
  • src/task_runner_cmd.rs — add introspection functions per runner

Needs Phase 2. Ships independently.


Phase 4 — Discover Integration (polish, ~1h)

rtk discover reports missed savings from task runner commands in Claude Code sessions.

Already covered by Phase 1 registry entries — classify_command() picks up new patterns automatically. Needs verification + a test to confirm classification fires correctly.

Needs Phase 1. Ships independently.


Key Design Decisions

Run the task runner, not the underlying command. RTK executes mise run lint, not biome check --write directly. This preserves task runner semantics: env vars, pre/post hooks, dependency ordering. Bypassing the runner would break workflows that rely on these side effects.

One Rust module for all runners. task_runner_cmd.rs with a TaskRunner enum, not separate modules per runner. The introspection and delegation logic is shared — splitting it across files adds complexity without benefit.

Lazy parsing. Config file introspection happens only when the command is invoked, never on startup. The <10ms startup constraint is non-negotiable.

No persistent cache. In-memory resolution per invocation is sufficient. Parsing mise.toml costs <5ms. A persistent cache adds complexity and invalidation edge cases.

Hook stays dumb. mise run lintrtk mise run lint. All intelligence lives in the Rust binary, not the hook script.


Acceptance Criteria

  • mise run lint rewritten by hook to rtk mise run lint
  • Output filtered with ≥60% token savings when underlying tool is recognized
  • Output filtered with ≥30% savings even when underlying tool is unknown (TOML boilerplate stripping)
  • rtk discover reports missed savings for mise run / just / task commands
  • Exit codes preserved (non-zero from task runner propagates correctly)
  • Fallback to raw output on any parsing or introspection failure
  • <10ms startup overhead (no config file parsing on startup)

Priority and Effort

Phase Effort Savings Ships independently
Phase 1 2-3h 30-50% Yes
Phase 2 4-6h 60-90% Yes (after Phase 1)
Phase 3 3-4h Auto-resolution Yes (after Phase 2)
Phase 4 1h Analytics Yes (after Phase 1)

Phase 1 is a clear quick win — shippable standalone and unblocks all other phases.

コントリビューターガイド