どうも!オペ部の西村祐二です!
miseのタスクを、シェルスクリプトやコーディングエージェントから足したり直したりしたい場面があります。これまでCLIでできたのは、mise tasks addによる追加まででした。説明文だけを変える、環境変数を1つ足す、使わなくなったタスクを消すといった変更には、addで定義を丸ごと書き直すか、mise.tomlを直接書き換える必要がありました。sedや正規表現で書き換えると、コメントや隣のタスクを壊しやすくなります。
mise 2026.10.25で、既存のタスクを変更するmise tasks setと、削除するmise tasks rmが追加されました。リリースノートでは、スクリプトやコーディングエージェントからの利用に向く機能として紹介されています。
mise tasks set build --description "Build for release"
mise tasks set test --env-var CI=1 --unset quiet
mise tasks rm --yes scratch
setとrmを使うには、mise 2026.10.25以降が必要です。この記事では、mise 2026.11.0で次の点を確かめました。
setで書き換わる範囲と、残るコメント--dry-runと--jsonによる書き込み前の確認- 書き込み先のファイルの選び方
rmの確認プロンプトと、消える範囲--の後ろに渡すコマンドの扱い
同じ2026.10.25で入ったmise watchの--onceと、addに増えた--usageや--confirmの使い方は扱いません。
add・set・rmの使い分け
リリースノートとCLIリファレンスをもとに、3つのコマンドの役割を整理しました。
| コマンド | 対象 | 動き |
|---|---|---|
mise tasks add |
新しいタスク | [tasks.<name>]を書きます。同じ内容をもう一度追加しても何も変わりません |
mise tasks set |
既存のTOMLタスク | 渡したフィールドだけを書き換えます。--unsetでフィールドを消せます。タスクがなければエラーになります |
mise tasks rm(別名remove) |
TOMLタスク、file task(mise-tasks/などに置いたスクリプトのタスク) |
タスクのテーブル、またはfile taskのスクリプトを消します。--yesがなければ確認します |
3つとも--env、--global、--pathで書き込むファイルを選べるほか、--jsonと--dry-runも受け付けます。2026.10.25ではaddに--env-var、--tools、--usage、--confirmが追加され、setも同じフラグを受け付けます。
addで同じ名前のタスクに違う内容を書くと、2026.11.0では--forceなしでも警告なしで置き換わりました。リリースノートによると、この置き換えは2027.5.0から警告、2028.5.0から拒否される予定です。現在のmiseだけで動かすスクリプトなら、今のうちに--forceを付けられると案内されています。
試してみる
環境
- macOS 26.7.2(arm64)
- mise 2026.11.0(GitHub Releasesのバイナリを常用のmiseとは別の場所に置き、HOMEも検証用のディレクトリに向けて実行)
- 比較用: mise 2026.10.24
比較用の2026.10.24でmise tasks set build --description xを実行すると、setがタスク名として扱われ、error: unrecognized subcommand 'build'というエラーで終了しました。
テスト用のmise.tomlは次のとおりです。見出しのコメントと行末のコメントを入れています。
# チームで共有するタスク定義
[tools]
node = "24"
# --- ビルド ---
[tasks.build]
description = "Build the app" # CI からも呼ぶ
run = "npm run build"
sources = ["src/**/*.ts"]
outputs = ["dist/**"]
# --- テスト ---
[tasks.test]
run = "npm test"
quiet = true
1. setで必要なフィールドだけ変える
buildの説明とコマンドを変え、testには環境変数とツールを足してquietを消しました。--の後ろに書いたコマンドは、タスクのrunを置き換えます。--の後ろの引数の扱いには注意点があるので、後半の「--の後ろに渡すコマンドの注意点」で詳しく見ます。
mise tasks set build --description "Build for release" -- npm run build -- --mode production
mise tasks set test --env-var CI=1 --tools node@24 --unset quiet
実行後に元のファイルと比べた差分です。

変わったのは渡したフィールドの行だけでした。descriptionの行末にあった# CI からも呼ぶは、値が変わったあとも同じ行に残っています。見出しのコメント、sourcesとoutputs、[tools]も元のままです。
--env-varと--toolsはenv = { CI = "1" }、tools = { node = "24" }のようにインラインテーブルで書き込まれました。CLIリファレンスによると、この2つはenvとtoolsの中の1項目だけを追加、または置き換えます。一方、--dependsや--sourcesのようなリストのフィールドは、リスト全体を置き換えます(--sourcesは次の節で確かめます)。
なお、存在しないタスクにmise tasks set lint --description xを実行すると、mise ERROR no task named lintを出して、終了コード1で終了しました。setが新しいタスクを作ることはないので、変更のつもりで実行してタスクが増えることはありません。
2. --dry-runと--jsonで書き込む前に確かめる
--dry-runを付けると、ファイルは変わりません。2026.11.0では、--dry-runだけだとmise task build would be updated in .../mise.tomlの1行が出るだけで、何が変わるかは分かりませんでした。中身まで見たいときは--jsonを併用します。
mise tasks set build --dry-run --json --sources "src/**/*.ts" --sources "package.json"
{
"task": "build",
"action": "updated",
"kind": "toml",
"path": "/path/to/proj/mise.toml",
"table": {
"description": "Build for release",
"run": "npm run build -- --mode production",
"sources": [
"src/**/*.ts",
"package.json"
],
"outputs": [
"dist/**"
]
},
"newly_trusted": false,
"dry_run": true
}
tableには、差分ではなく書き込み後のタスク全体が入っています。tableのsourcesは、元の値に足されるのではなく、渡した2つに置き換わる内容になっていました。
--jsonを付けると、JSONは標準出力に出て、標準エラーには何も出ませんでした。--jsonなしで出るupdated in ...やadded to ...のメッセージは、標準エラーに出ます。スクリプトでは標準出力をそのままjqに渡せます。
3. 書き込み先のファイルを選ぶ
書き込み先は、フラグと、どのファイルにタスクがあるかで決まります。公式ドキュメントのWhich file mise writes toと今回の結果をもとに、自作の概念図にしました(公式の構成図ではありません)。--env localはカレントディレクトリのmise.local.toml、--globalはグローバル設定、--pathは指定したファイルに書き込みます。フラグがない場合、addは最も近い設定のあるディレクトリの共有側のファイル(通常はmise.toml。mise.local.tomlしかなければそちら)に書き込みます。setとrmは、そのタスクを定義しているファイルを探します。

自分だけで使うタスクは、--env localでmise.local.tomlに置けます。
mise tasks add --env local lint --tools node@24 -- npx eslint .
[tasks.lint]
tools = { node = "24" }
run = "npx eslint ."
mise.tomlとmise.local.tomlの両方があるディレクトリで、フラグなしのaddを実行すると、mise.tomlのほうに書き込まれました。
同じ名前のタスクが2つのファイルにあると、setとrmは、どちらのファイルも書き換えずに止まります。mise.tomlとmise.local.tomlの両方にtestを置いて試しました(rmは--yesを付けても同じエラーでした)。
mise tasks set test --description "changed"
mise ERROR task test is defined in several files (/path/to/proj/mise.local.toml, /path/to/proj/mise.toml); choose one with --path, --env or --global
--env localを付けると、mise.local.tomlのtestだけが更新されました。
まだ信頼していないディレクトリでaddを実行すると、mise自身がそのディレクトリを信頼し、標準エラーに次のメッセージを出しました。CLIリファレンスでは、書き込んだファイルをmiseが読み込めるようにするための動きと説明されています。
mise task hello added to /path/to/proj2/mise.toml
mise trusted /path/to/proj2 so that mise loads /path/to/proj2/mise.toml; `mise trust --untrust /path/to/proj2` undoes it
CLIリファレンスでは、setも書き込み先を信頼し、セーフモード(MISE_SAFE=1)では信頼せずに書き込むだけとされています。この2つは今回試していません。
4. rmの確認プロンプトと、消える範囲
端末がない状態(標準入力を/dev/nullにした実行)で--yesを付けずにrmを実行すると、次のエラーで終了しました。
mise tasks rm scratch </dev/null
mise ERROR mise tasks rm requires confirmation but there was nobody to ask; pass --yes to proceed non-interactively
確認の入力待ちで止まったままにはならず、終了コード1で終わります。端末から実行したときの確認プロンプトの表示は試していません。消す前に対象を見るなら、--dry-run --jsonが使えます。
mise tasks rm --dry-run --json scratch
[
{
"task": "scratch",
"action": "removed",
"kind": "toml",
"path": "/path/to/proj3/mise.toml",
"table": {
"run": "echo scratch"
},
"newly_trusted": false,
"dry_run": true
}
]
rmの--jsonは、setと違って配列で返ります。jqでは.[0].pathのように取り出します。
次のファイルで、scratchとbuildを--yes付きで順に消しました。
# 共有タスク
[tasks.build]
run = "echo build"
# build のあとに動く
[tasks.ci]
depends = ["build"]
run = "echo ci"
[tasks.scratch] # 一時的な実験用
run = "echo scratch"
mise tasks rm --yes scratch
mise tasks rm --yes build
# build のあとに動く
[tasks.ci]
depends = ["build"]
run = "echo ci"
[tasks.scratch]のテーブルは、行末のコメントごと消えました。rmは、テーブルの直前にあるコメントもテーブルと一緒に消します。[tasks.build]の上にあったファイル先頭のコメント# 共有タスクも、buildと一緒に消えました。コメントとテーブルの間に空行を1行挟んだファイルでも、結果は同じでした。
消したタスクに依存するタスクは、そのまま残ります。ciがdepends = ["build"]を持つ状態でbuildを消しても、ciの定義は変わりませんでした。このときmise tasks validateを実行すると、Dependency 'build' not found [missing-dependency]が報告されました。rmのあとにvalidateを実行すれば、壊れた依存に気付けます。
file taskにrmを実行すると、スクリプトのファイルが消えました。
5. setとrmが書き換えないもの
次の対象では、setとrmはファイルを書き換えずにエラーで止まりました。
| 対象 | 結果 |
|---|---|
task_config.includes(別のTOMLファイルからタスクを読み込む設定)で読み込んだタスク |
setはtask lint is defined in .../tasks.toml, which mise tasks set does not edit、rmは... which mise tasks rm does not editというエラーで止まります |
file taskへのset |
task hello is a file task (...); change its script, for example with `mise tasks edit hello` というエラーで止まります |
file taskのスクリプトを直すときは、エラーの案内どおりmise tasks editを使います。
リリースノートには、rmがpackage.jsonから推測されたタスクも消さないと書かれています。この挙動は、package.jsonのタスクを読ませる構成を用意していないため確かめていません。
--の後ろに渡すコマンドの注意点
addとsetで--の後ろに書いた引数は、語ごとに、必要なものだけシェル向けにクォートされてからrunに入ります。このため、コマンド全体を1つの文字列で渡すと、全体が1語のコマンド名として扱われます。ソースコード(v2026.11.0のsrc/cli/tasks/write.rs)でも、runの文字列をshell_words::joinで組み立てていました。
| 渡し方 | 書き込まれたrun |
mise runの結果 |
|---|---|---|
-- "echo a b" |
'echo a b' |
sh: echo a b: command not found(終了コード127) |
-- echo --mode=prod "a b" |
echo '--mode=prod' 'a b' |
--mode=prod a b |
-- echo one "&&" echo two |
echo one '&&' echo two |
one && echo two(&&が文字として出力される) |
-- echo '$HOME' |
echo '$HOME' |
$HOME(展開されない) |
-- sh -c "echo one && echo two" |
sh -c 'echo one && echo two' |
one、two |
コマンドと引数は、1語ずつ分けて渡します。&&やパイプ、変数の展開を使いたいときは、最後の例のようにsh -cに1つの文字列として渡すと動きました。パイプと変数でも、次のように動いています。
mise tasks set hello -- sh -c 'echo one | tr a-z A-Z' # mise run hello の出力: ONE
mise tasks set hello -- sh -c 'echo $HOME' # mise run hello の出力: 実行時の HOME
変数を含む文字列は、単一引用符で囲む必要があります。sh -c "echo $GREETING"のように二重引用符で書くと、mise tasks setを実行したシェルが先に展開し、run = "sh -c 'echo from-caller'"のように展開後の値が書き込まれました。sh -cで包んでも読みにくいほど長いコマンドなら、mise.tomlを直接編集するほうが読みやすくなりそうです。
検証結果の考察
行末のコメントが値の変更後も残ったのは、setがコメントや空白を保ったままTOMLを編集できるtoml_editのDocumentMutでファイルを扱っているためです。rmで直前のコメントまで消えたのも、toml_editがテーブルの直前のコメントをそのテーブルの装飾として持つためだと推測しています。ソースコード中にも、空になった[tasks]の見出しを消す処理に、コメントが付いた見出しは消すとコメントも一緒に消えるので残す、という趣旨の注記がありました。
存在しないタスクへのset、複数のファイルにあるタスクやincludesで読み込んだタスクへのsetとrm、端末がない状態のrmは、どれもエラーメッセージを出し、終了コード1で終了しました。推測で書き込まないので、スクリプトやエージェントの側で終了コードを見て次の手を決められます。
引数をクォートする作りのため、引数に含まれる&&や|で続けて別のコマンドが実行されることはありませんでした。一方、シェルの機能を使うためにsh -cで包むと、包んだ文字列の中身はシェルに解釈されます。外から受け取った値をsh -cの文字列に埋め込む場合は、この保護が働かない点に注意が要ります。
試してみた感想
コーディングエージェントにタスクの追加や修正を頼むときは、mise.tomlを直接編集させるより、mise tasks addやsetを使ってもらうほうが差分が小さく済みそうです。--dry-run --jsonの結果を見てから実行すれば、何が書き込まれるかもレビューできます。
自分用のタスクを--env localでmise.local.tomlに出し入れできるのも便利だと感じました。共有のmise.tomlを誤って書き換える心配が減ります。
rmで直前のコメントが消えるのは想定と違いました。テーブルだけが消えると思っていました。
まとめ
mise 2026.10.25で追加されたmise tasks setとmise tasks rmを2026.11.0で試しました。setは渡したフィールドだけを書き換え、ほかのフィールドやコメントは残します。--dry-run --jsonで書き込み後のタスクを確認できます。複数のファイルに同じタスクがあれば、推測で書き換えずに止まります。
使うときは、次の点に注意が必要です。
- 書き込み先は
--env local、--global、--pathで選べます。フラグがない場合、addは共有側のファイル(通常はmise.toml)に、setとrmはタスクを定義している1つのファイルに書き込みます rmは、端末がない環境では--yesを付けないとエラーで止まります。テーブルの直前のコメントも一緒に消えます。消したタスクに依存しているタスクは残るので、mise tasks validateで確認します--の後ろの引数は語ごとにクォートされます。コマンドは語ごとに分けて渡し、&&やパイプはsh -cで包み、変数は単一引用符で囲みます- includesで読み込んだTOMLのタスクは、
setとrmの対象外です。file taskはsetの対象外で、rmではスクリプトごと消えます addは、同じ名前で内容が違うタスクを--forceなしでも置き換えます(2026.11.0時点)
誰かの参考になれば幸いです。