pyfltr 3.3.0__tar.gz → 3.3.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (104) hide show
  1. {pyfltr-3.3.0 → pyfltr-3.3.2}/.pre-commit-config.yaml +6 -0
  2. {pyfltr-3.3.0 → pyfltr-3.3.2}/CLAUDE.md +3 -4
  3. {pyfltr-3.3.0 → pyfltr-3.3.2}/PKG-INFO +1 -1
  4. {pyfltr-3.3.0 → pyfltr-3.3.2}/docs/development/development.md +0 -20
  5. {pyfltr-3.3.0 → pyfltr-3.3.2}/docs/development/jsonl-output.md +4 -3
  6. {pyfltr-3.3.0 → pyfltr-3.3.2}/docs/guide/configuration-tools.md +19 -0
  7. {pyfltr-3.3.0 → pyfltr-3.3.2}/docs/guide/configuration.md +1 -1
  8. {pyfltr-3.3.0 → pyfltr-3.3.2}/docs/guide/usage.md +6 -5
  9. {pyfltr-3.3.0 → pyfltr-3.3.2}/mkdocs.yml +2 -2
  10. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/command.py +26 -9
  11. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/config.py +5 -0
  12. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/llm_output.py +19 -15
  13. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/main.py +34 -4
  14. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/command_core_test.py +161 -2
  15. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/config_test.py +20 -0
  16. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/llm_output_test.py +47 -17
  17. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/output_format_test.py +55 -13
  18. {pyfltr-3.3.0 → pyfltr-3.3.2}/.claude/agents/error-parser-reviewer.md +0 -0
  19. {pyfltr-3.3.0 → pyfltr-3.3.2}/.claude/agents/tool-compat-checker.md +0 -0
  20. {pyfltr-3.3.0 → pyfltr-3.3.2}/.claude/skills/pyfltr-add-tool/SKILL.md +0 -0
  21. {pyfltr-3.3.0 → pyfltr-3.3.2}/.editorconfig +0 -0
  22. {pyfltr-3.3.0 → pyfltr-3.3.2}/.gitattributes +0 -0
  23. {pyfltr-3.3.0 → pyfltr-3.3.2}/.github/workflows/ci.yaml +0 -0
  24. {pyfltr-3.3.0 → pyfltr-3.3.2}/.github/workflows/docs.yaml +0 -0
  25. {pyfltr-3.3.0 → pyfltr-3.3.2}/.github/workflows/release.yaml +0 -0
  26. {pyfltr-3.3.0 → pyfltr-3.3.2}/.gitignore +0 -0
  27. {pyfltr-3.3.0 → pyfltr-3.3.2}/.gitmessage +0 -0
  28. {pyfltr-3.3.0 → pyfltr-3.3.2}/.markdownlint-cli2.yaml +0 -0
  29. {pyfltr-3.3.0 → pyfltr-3.3.2}/.npmrc +0 -0
  30. {pyfltr-3.3.0 → pyfltr-3.3.2}/.pylintrc +0 -0
  31. {pyfltr-3.3.0 → pyfltr-3.3.2}/.python-version +0 -0
  32. {pyfltr-3.3.0 → pyfltr-3.3.2}/.textlintignore +0 -0
  33. {pyfltr-3.3.0 → pyfltr-3.3.2}/.textlintrc.yaml +0 -0
  34. {pyfltr-3.3.0 → pyfltr-3.3.2}/.vscode/extensions.json +0 -0
  35. {pyfltr-3.3.0 → pyfltr-3.3.2}/.vscode/settings.json +0 -0
  36. {pyfltr-3.3.0 → pyfltr-3.3.2}/LICENSE +0 -0
  37. {pyfltr-3.3.0 → pyfltr-3.3.2}/Makefile +0 -0
  38. {pyfltr-3.3.0 → pyfltr-3.3.2}/README.md +0 -0
  39. {pyfltr-3.3.0 → pyfltr-3.3.2}/cliff.toml +0 -0
  40. {pyfltr-3.3.0 → pyfltr-3.3.2}/docs/.markdownlint-cli2.yaml +0 -0
  41. {pyfltr-3.3.0 → pyfltr-3.3.2}/docs/development/architecture.md +0 -0
  42. {pyfltr-3.3.0 → pyfltr-3.3.2}/docs/development/archive-and-cache.md +0 -0
  43. {pyfltr-3.3.0 → pyfltr-3.3.2}/docs/development/index.md +0 -0
  44. {pyfltr-3.3.0 → pyfltr-3.3.2}/docs/development/mcp-server.md +0 -0
  45. {pyfltr-3.3.0 → pyfltr-3.3.2}/docs/development/subcommands.md +0 -0
  46. {pyfltr-3.3.0 → pyfltr-3.3.2}/docs/guide/custom-commands.md +0 -0
  47. {pyfltr-3.3.0 → pyfltr-3.3.2}/docs/guide/index.md +0 -0
  48. {pyfltr-3.3.0 → pyfltr-3.3.2}/docs/guide/migration-v3.md +0 -0
  49. {pyfltr-3.3.0 → pyfltr-3.3.2}/docs/guide/recommended-nonpython.md +0 -0
  50. {pyfltr-3.3.0 → pyfltr-3.3.2}/docs/guide/recommended.md +0 -0
  51. {pyfltr-3.3.0 → pyfltr-3.3.2}/docs/index.md +0 -0
  52. {pyfltr-3.3.0 → pyfltr-3.3.2}/mise.toml +0 -0
  53. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/__init__.py +0 -0
  54. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/__main__.py +0 -0
  55. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/archive.py +0 -0
  56. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/builtin_commands.py +0 -0
  57. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/cache.py +0 -0
  58. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/cli.py +0 -0
  59. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/code_quality.py +0 -0
  60. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/error_parser.py +0 -0
  61. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/executor.py +0 -0
  62. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/formatters.py +0 -0
  63. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/github_annotations.py +0 -0
  64. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/mcp_.py +0 -0
  65. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/only_failed.py +0 -0
  66. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/paths.py +0 -0
  67. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/precommit.py +0 -0
  68. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/presets.py +0 -0
  69. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/retry.py +0 -0
  70. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/rule_urls.py +0 -0
  71. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/runs.py +0 -0
  72. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/sarif_output.py +0 -0
  73. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/shell_completion.py +0 -0
  74. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/stage_runner.py +0 -0
  75. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/ui.py +0 -0
  76. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyfltr/warnings_.py +0 -0
  77. {pyfltr-3.3.0 → pyfltr-3.3.2}/pyproject.toml +0 -0
  78. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/__init__.py +0 -0
  79. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/archive_test.py +0 -0
  80. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/cache_test.py +0 -0
  81. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/cli_test.py +0 -0
  82. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/code_quality_test.py +0 -0
  83. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/command_linter_fix_test.py +0 -0
  84. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/command_prettier_test.py +0 -0
  85. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/command_ruff_format_test.py +0 -0
  86. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/command_textlint_fix_test.py +0 -0
  87. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/conftest.py +0 -0
  88. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/error_parser_test.py +0 -0
  89. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/executor_test.py +0 -0
  90. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/github_annotations_test.py +0 -0
  91. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/main_test.py +0 -0
  92. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/mcp_test.py +0 -0
  93. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/only_failed_test.py +0 -0
  94. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/paths_test.py +0 -0
  95. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/precommit_test.py +0 -0
  96. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/retry_test.py +0 -0
  97. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/rule_urls_test.py +0 -0
  98. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/runs_test.py +0 -0
  99. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/sarif_output_test.py +0 -0
  100. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/shell_completion_test.py +0 -0
  101. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/stage_runner_test.py +0 -0
  102. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/ui_test.py +0 -0
  103. {pyfltr-3.3.0 → pyfltr-3.3.2}/tests/warnings_test.py +0 -0
  104. {pyfltr-3.3.0 → pyfltr-3.3.2}/uv.lock +0 -0
@@ -32,3 +32,9 @@ repos:
32
32
  types_or: [python, markdown, toml]
33
33
  require_serial: true
34
34
  language: system
35
+ - id: mkdocs-build
36
+ name: mkdocs build --strict
37
+ entry: uv run --frozen mkdocs build --strict --quiet --site-dir /tmp/mkdocs-check
38
+ language: system
39
+ pass_filenames: false
40
+ files: ^(docs/|mkdocs\.yml|README\.md|CLAUDE\.md)
@@ -4,6 +4,7 @@
4
4
 
5
5
  - `make update`: 依存更新 + pre-commit autoupdate + pinactアクション更新 + 全テスト実行
6
6
  - `make update-actions`: GitHub Actionsのハッシュピン更新のみ(mise経由でpinact実行)
7
+ - リリース手順: [docs/development/development.md](docs/development/development.md) 参照
7
8
  - テストコードは`pyfltr/xxx_.py`に対して`tests/xxx_test.py`として配置する
8
9
  - コミット前の検証方法: `uv run pyfltr run-for-agent`
9
10
  - ドキュメントなどのみの変更の場合は省略可(pre-commitで実行されるため)
@@ -36,13 +37,11 @@ stdout占有は`jsonl` / `sarif` / `code-quality`かつ`--output-file`未指定
36
37
 
37
38
  ## 注意点
38
39
 
39
- - `uv run mkdocs build --strict`でリンク・nav整合性を検証(ただし日本語アンカーリンク`#見出し日本語`は
40
- MkDocs TOCで解決できずINFO通知のみで`--strict`でも検知されないため手動確認要)
41
- - 内部リンクは英数アンカーを優先する。MkDocs(Material)のslugifyは英数のみを採用してアンカー生成する。
40
+ - 内部リンクは英数アンカーを優先する。MkDocs(Material)のslugifyは英数のみを採用してアンカー生成するため、
41
+ 日本語アンカーリンク`#見出し日本語`はTOCで解決できずINFO通知のみで`--strict`でも検知されない(手動確認要)。
42
42
  markdownlint MD051は見出し原文を見るため、`{#id}`記法で明示併設する(例:「### jsonl形式の使い方 {#jsonl}」)
43
43
  - `docs/guide/index.md`の対応ツール一覧と`mkdocs.yml`内llmstxt `markdown_description`の「対応ツール」節は
44
44
  人手同期(SSOT化しない運用)
45
45
  - `mkdocs.yml`内llmstxt `markdown_description`にはLLMが利用する際に有用な情報のみ記載する
46
46
  (`run-for-agent`サブコマンド、主要オプションなど)。LLMにとって不要な情報はdocs側をSSOTとし、多重管理を避ける
47
- - ドキュメント構成変更時は`docs/development/development.md`の「READMEとdocsの役割分担」節を先に参照
48
47
  - JSONLスキーマの変更は破壊的変更扱いしない(LLMが読みやすいよう継続的に改善する)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyfltr
3
- Version: 3.3.0
3
+ Version: 3.3.2
4
4
  Summary: Python Formatters, Linters, and Testers Runner.
5
5
  Project-URL: Homepage, https://github.com/ak110/pyfltr
6
6
  Author-email: "aki." <mark@aur.ll.to>
@@ -34,22 +34,6 @@ CI/`make`などの自動実行環境で`uv sync`/`uv run`が依存解決を再
34
34
  `uv add`/`uv remove`/`uv lock --upgrade-package`を使えばよい。
35
35
  `make update`も内部で自動的にUV_FROZENを外すため、そのまま実行してよい。
36
36
 
37
- ## READMEとdocsの役割分担
38
-
39
- 本プロジェクトのドキュメントは以下の構成で配置している。
40
-
41
- - README.md: 概要・特徴・インストール手順・ドキュメントへのリンクを網羅する「玄関」。
42
- README.mdだけを読めばプロジェクトの目的と使い始めるための入口が把握できる状態を保つ
43
- - docs/guide/: 利用者向けの詳細情報(対応ツール一覧・コンセプト・設定リファレンス・使い方など)
44
- - docs/development/: 開発者向けの情報(セットアップ・リリース手順など)
45
-
46
- README.mdとdocs側で概要・特徴・インストール手順が部分的に重複する場合があるが、README.mdはGitHubトップとして、
47
- docs側は公開ドキュメントの入口としてそれぞれ自己完結する必要があるため、この重複は許容する。
48
- 本プロジェクトの`docs/index.md`はREADMEへの参照のみに留めており、インストール手順の重複は発生させていない。
49
-
50
- 変更頻度が低いため二重管理のコストより一貫性・可読性のメリットが上回ると判断した。
51
- 変更時は、docs側で同じ情報を再掲している箇所があれば同じコミット内で合わせて更新する。
52
-
53
37
  ## ドキュメント
54
38
 
55
39
  ドキュメントはMkDocsで管理し、GitHub Pagesでホスティングしている。
@@ -72,10 +56,6 @@ uv run mkdocs serve
72
56
 
73
57
  masterブランチへのpush時にdocs/配下やmkdocs.ymlの変更があると自動デプロイされる。
74
58
 
75
- ## コミットメッセージ(Conventional Commits)
76
-
77
- Conventional Commits形式に従う。ただし記述の方向性があまり変わらないような軽微な修正は`chore`などにしてよい。
78
-
79
59
  ## リリース手順
80
60
 
81
61
  事前に`gh`コマンドをインストールして`gh auth login`でログインしておき、以下のコマンドのいずれかを実行。
@@ -7,7 +7,7 @@ LLMエージェントとCIシステム向けに提供するJSONL出力(およ
7
7
 
8
8
  | `kind` | 役割 |
9
9
  | --- | --- |
10
- | `header` | 先頭1行。実行環境情報。`commands_count`/`schema_hints`は既定で短縮され、`-v`指定時にフル`commands`配列とフル`schema_hints`へ切り替わる |
10
+ | `header` | 先頭1行。実行環境情報。`commands`は実行対象(有効化された)コマンド名配列。`schema_hints`は既定で短縮版、`-v`指定時にフル版へ切り替わる |
11
11
  | `warning` | pyfltrが検出した設定・実行時の警告(`source`で発生元を識別) |
12
12
  | `diagnostic` | `(command, file)`単位で集約された診断。個別指摘は`messages[]`配列に格納 |
13
13
  | `command` | 1コマンド1レコードの実行メタ情報。rule→URL辞書`hint-urls`をオプションで含む |
@@ -29,10 +29,11 @@ JSONLはLLMエージェントが入力として読むケースが多いため、
29
29
  英語にするのはトークン効率(日本語より短くなりやすい)と汎用性(LLMの入力として標準的)のため。
30
30
 
31
31
  - `header.schema_hints`: 毎runに付与する英語の辞書。既定はLLMが読み違いやすい非自明フィールドのみに絞った短縮版を出す。
32
- 実装は`_SCHEMA_HINTS_COMPACT`で約420文字。
32
+ 実装は`_SCHEMA_HINTS_COMPACT`で約400文字。
33
33
  `-v` / `--verbose`指定時はフル版`_SCHEMA_HINTS`(約1,500文字)に切り替わり、
34
34
  `diagnostic.messages` / `diagnostic.messages.fix` / `diagnostic.messages.severity` /
35
- `command.hint-urls` / `command.retry_command` / `command.cached` / `command.truncated` / `header.run_id`
35
+ `command.hint-urls` / `command.retry_command` / `command.cached` / `command.cached_elapsed` /
36
+ `command.truncated` / `header.run_id`
36
37
  などフィールド単位の詳細を英文で説明する。
37
38
  短縮版には`_note`キーでフル版の取得方法を案内する。
38
39
  公開窓口は`pyfltr/llm_output.py`の`get_schema_hints(full=...)`
@@ -349,6 +349,25 @@ GitHub ActionsでCIを実行する場合は[jdx/mise-action](https://github.com/
349
349
 
350
350
  miseを使わず、PATH上のバイナリを直接使う場合は`bin-runner = "direct"`を設定する。
351
351
 
352
+ ### mise-auto-trust
353
+
354
+ `mise`モードでは、worktreeやdotfiles配下のディレクトリなど、`mise.toml`が未信頼扱いになっている場合に
355
+ 事前チェックが失敗することがある。
356
+ 既定では`mise-auto-trust = true`となっており、未信頼configを検出すると`mise trust --yes --all`を自動実行して
357
+ 信頼を確立してからリトライする。
358
+ `--all`オプションはcwdおよびその親ディレクトリにある全configを対象とするため、
359
+ プロジェクト外の親ディレクトリに`mise.toml`が存在する場合もまとめて信頼される点に注意する。
360
+
361
+ `mise-auto-trust`が不要な場合は無効化できる。
362
+
363
+ ```toml
364
+ [tool.pyfltr]
365
+ mise-auto-trust = false
366
+ ```
367
+
368
+ 無効化した場合、未信頼configが原因の失敗はmise由来のエラーメッセージとともに`failed`扱いとなる。
369
+ 手動で`mise trust`を実行して対処する。
370
+
352
371
  ### バージョン指定
353
372
 
354
373
  `{command}-version`でbin-runner対応ツールのバージョンを指定できる。既定は`"latest"`。
@@ -245,4 +245,4 @@ Claude Codeなど末尾だけを読み取るツールでも実行結果を把握
245
245
  ---
246
246
 
247
247
  個別のツール設定(2段階実行、ファイルパターン、直接実行 / js-runner / bin-runnerのカテゴリ別設定、
248
- カスタムコマンド等)の詳細は[ツール別設定](configuration-tools.md)を参照。
248
+ `mise-auto-trust`によるmise未信頼の自動対応、カスタムコマンド等)の詳細は[ツール別設定](configuration-tools.md)を参照。
@@ -222,6 +222,8 @@ pyfltr ci --commands=ruff-check,markdownlint [files and/or directories ...]
222
222
  ```
223
223
 
224
224
  カンマ区切りで実行するツールだけ指定する。全サブコマンドで使用可能。
225
+ `--commands`は複数回指定も可能で、カンマ区切りと併用できる。
226
+ 例えば`--commands=mypy --commands=pyright,ruff-check`は`--commands=mypy,pyright,ruff-check`と同じになる。
225
227
 
226
228
  以下のエイリアスも使用可能。(例: `--commands=format`)
227
229
 
@@ -330,10 +332,9 @@ CLIオプション`--output-format`が指定されている場合は環境変数
330
332
  出力は以下5種別のレコードからなる。`kind`フィールドでレコード種別を判別する。
331
333
 
332
334
  - `header`: 先頭1行。実行環境情報
333
- (`version` / `run_id` / `python` / `executable` / `platform` / `cwd` / `commands_count` / `files` / `schema_hints`)。
334
- 既定では`commands_count`(整数)と短縮版`schema_hints`(約400文字)のみを出してヘッダーサイズを抑える。
335
- `-v` / `--verbose`指定時はフル`commands`配列とフル`schema_hints`を出す。
336
- フル`commands`配列は実行アーカイブの`meta.json`に常時保存されるため、短縮時も`show-run <run_id>`で参照できる
335
+ (`version` / `run_id` / `python` / `executable` / `platform` / `cwd` / `commands` / `files` / `schema_hints`)。
336
+ `commands`は実行対象(有効化された、`--only-failed`適用後の)コマンド名配列。
337
+ 既定は短縮版`schema_hints`(約400文字)。`-v` / `--verbose`指定時は`schema_hints`をフル版(約1,500文字)へ切り替える
337
338
  - `warning`: pyfltrが検出した設定・実行時の警告(`source`で発生元を識別)
338
339
  - `diagnostic`: `(command, file)`単位で集約された診断。個別指摘は`messages[]`配列に格納
339
340
  - `command`: 1コマンド1レコードの実行メタ情報
@@ -343,7 +344,7 @@ CLIオプション`--output-format`が指定されている場合は環境変数
343
344
  [`pyfltr show-run`](#show-run) / [`pyfltr list-runs`](#list-runs)で該当runの詳細を参照できる。
344
345
 
345
346
  ```json
346
- {"kind":"header","version":"3.0.0","run_id":"01HXYZ...","python":"3.12.0 ...","executable":"/usr/bin/python3","platform":"linux","cwd":"/work","commands_count":2,"files":12,"schema_hints":{"header":"...","_note":"full schema available with -v / --verbose ..."}}
347
+ {"kind":"header","version":"3.0.0","run_id":"01HXYZ...","python":"3.12.0 ...","executable":"/usr/bin/python3","platform":"linux","cwd":"/work","commands":["mypy","ruff-format"],"files":12,"schema_hints":{"header":"...","_note":"full schema available with -v / --verbose ..."}}
347
348
  {"kind":"warning","source":"config","msg":"pre-commit が有効化されていますが、設定ファイルが見つかりません: .pre-commit-config.yaml"}
348
349
  {"kind":"diagnostic","command":"mypy","file":"src/a.py","messages":[{"line":42,"col":5,"msg":"Incompatible return value type"}]}
349
350
  {"kind":"command","command":"mypy","type":"linter","status":"failed","files":12,"elapsed":0.8,"diagnostics":1,"rc":1}
@@ -72,10 +72,10 @@ plugins:
72
72
 
73
73
  `--output-format=jsonl`でJSON Lines形式の構造化出力が得られる。レコードは5種類。
74
74
 
75
- - `header`: 先頭1行。実行環境情報(`version` / `run_id` / `python` / `executable` / `platform` / `cwd` / `commands_count` / `files` / `schema_hints`)。既定では `commands_count`(整数)と短縮版 `schema_hints` のみを出力してヘッダーサイズを抑え、`-v` / `--verbose` 指定時にフル `commands` 配列とフル `schema_hints` へ切り替わる。`schema_hints` は各フィールドの意味を英語で補足する LLM 向けガイド
75
+ - `header`: 先頭1行。実行環境情報(`version` / `run_id` / `python` / `executable` / `platform` / `cwd` / `commands` / `files` / `schema_hints`)。`commands` は実行対象(有効化された)コマンド名配列。既定では短縮版 `schema_hints` を出し、`-v` / `--verbose` 指定時にフル `schema_hints` へ切り替わる。`schema_hints` は各フィールドの意味を英語で補足する LLM 向けガイド
76
76
  - `warning`: pyfltr が検出した設定・実行時の警告(`source` で発生元を識別)
77
77
  - `diagnostic`: `(command, file)` 単位で1行に集約される診断。個別指摘は `messages[]` 配列(各要素は `line` / `col` / `rule` / `severity` / `fix` / `msg` / `hint`)として格納される。`fix` は `"safe"` / `"unsafe"` / `"suggested"` / `"none"` の4値を取り、ツールが自動修正情報を返さない場合はフィールドごと省略。`hint` は textlint の頻出ルールなど、pyfltr 側でヒントを事前登録したルールにのみ付与される
78
- - `command`: ツールごとの実行結果サマリ(`status` / `elapsed` / `diagnostics` / `rc` / `retry_command` / `truncated` / `cached` / `cached_from` / `hint-urls`)。`hint-urls` は rule→ドキュメントURLの辞書
78
+ - `command`: ツールごとの実行結果サマリ(`status` / `elapsed` / `diagnostics` / `rc` / `retry_command` / `truncated` / `cached` / `cached_from` / `cached_elapsed` / `hint-urls`)。`cached=true` のとき `elapsed` は省略され前回実行時の計測値を `cached_elapsed` として提示する。`hint-urls` は rule→ドキュメントURLの辞書
79
79
  - `summary`: 最終1行の全体集計(`succeeded` / `formatted` / `failed` / `skipped` / `diagnostics` / `exit`)。`failed > 0` のとき英語の `guidance` 配列を付与し、再実行コマンドや `show-run` の案内を返す。直接指定ファイルが `exclude` / `.gitignore` で全除外された場合は `fully_excluded_files` にパス一覧を付与する(非空時のみ)
80
80
 
81
81
  stdoutモード(`--output-file`未指定)の出力順は`header`→ツール完了順に`diagnostic`+`command`→`warning`→`summary`。stdoutへJSONLのみを書き、進捗ログやTUIは無音化される。`--output-file`指定時はファイルへJSONLを書き、stdoutには従来どおりのテキスト出力を並行出力する(ローカル実行で進捗を追える)。
@@ -300,15 +300,32 @@ def _resolve_bin_commandline(
300
300
  raise FileNotFoundError("mise")
301
301
  tool_name = spec.mise_backend or spec.bin_name
302
302
  tool_spec = f"{tool_name}@{version}"
303
- # バージョン指定込みでツールの利用可否を事前チェック
304
- check = subprocess.run(
305
- ["mise", "exec", tool_spec, "--", spec.bin_name, "--version"],
306
- capture_output=True,
307
- text=True,
308
- check=False,
309
- )
310
- if check.returncode != 0:
311
- raise FileNotFoundError(f"mise exec {tool_spec} -- {spec.bin_name}")
303
+ check_args = ["mise", "exec", tool_spec, "--", spec.bin_name, "--version"]
304
+ trusted = False
305
+ while True:
306
+ # バージョン指定込みでツールの利用可否を事前チェック
307
+ check = subprocess.run(
308
+ check_args,
309
+ capture_output=True,
310
+ text=True,
311
+ check=False,
312
+ )
313
+ if check.returncode == 0:
314
+ break
315
+ stderr = check.stderr
316
+ # config未信頼が原因かつ自動trust有効なら1回だけ信頼してリトライ
317
+ if not trusted and config["mise-auto-trust"] and "not trusted" in stderr:
318
+ trust = subprocess.run(
319
+ ["mise", "trust", "--yes", "--all"],
320
+ capture_output=True,
321
+ text=True,
322
+ check=False,
323
+ )
324
+ if trust.returncode == 0:
325
+ trusted = True
326
+ continue
327
+ raise FileNotFoundError(f"mise trust --yes --all: {trust.stderr.strip()}")
328
+ raise FileNotFoundError(f"mise exec {tool_spec} -- {spec.bin_name}: {stderr.strip()}")
312
329
  return "mise", ["exec", tool_spec, "--", spec.bin_name]
313
330
 
314
331
  raise ValueError(f"bin-runnerの設定値が正しくありません: {runner=}")
@@ -110,6 +110,11 @@ DEFAULT_CONFIG: dict[str, typing.Any] = {
110
110
  # - mise: mise exec <tool>@<version> -- <cmd>(既定)
111
111
  # - direct: PATH 上のバイナリを直接実行
112
112
  "bin-runner": "mise",
113
+ # mise 実行時に対象ディレクトリの config が未信頼だった場合、
114
+ # 自動で ``mise trust --yes --all`` を実行して再試行するか。
115
+ # worktree や dotfiles 配下など mise.toml が未信頼扱いになりやすい
116
+ # 環境での手動介入を不要にするための opt-out 設定(既定は有効)。
117
+ "mise-auto-trust": True,
113
118
  # コマンド毎に有効無効、パス、追加の引数を設定
114
119
  # 言語カテゴリ (python / javascript / rust / dotnet) に属するツールは v3.0.0 で
115
120
  # opt-in 化したため、既定値は False。preset で推奨ツールが True になり、
@@ -154,8 +154,8 @@ def build_lines(
154
154
  ``warnings``は``pyfltr.warnings_.collected_warnings()``の返り値を想定する。
155
155
  ``run_id``が指定されていればheaderレコードに埋め込む。
156
156
  ``launcher_prefix``が指定されていれば``summary.guidance``内の起動コマンド表記に反映する。
157
- ``verbose=True`` でheaderへフル``commands``配列とフル``schema_hints``を埋め込む
158
- (既定は``commands_count``と短縮``schema_hints``)。
157
+ ``verbose=True`` でheaderの``schema_hints``をフル版に切り替える
158
+ (既定は短縮版。``commands`` 配列は常に出力する)。
159
159
  """
160
160
  ordered = sorted(results, key=lambda r: _command_index(config, r.command))
161
161
 
@@ -198,8 +198,7 @@ def write_jsonl_header(commands: list[str], files: int, *, run_id: str | None =
198
198
  headerレコードに含める (アーカイブ参照時の識別キー)。
199
199
  出力先は ``pyfltr.cli.configure_structured_output()`` が設定した handler に従う
200
200
  (stdout もしくは `--output-file` の FileHandler)。
201
- ``verbose=True`` でフル``commands``配列とフル``schema_hints``を埋め込む
202
- (既定は``commands_count``と短縮``schema_hints``でヘッダーサイズを抑制)。
201
+ ``verbose=True`` でheaderの``schema_hints``をフル版に切り替える(既定は短縮版)。
203
202
  """
204
203
  with _write_lock:
205
204
  pyfltr.cli.structured_logger.info(_dump(_build_header_record(commands, files, run_id=run_id, verbose=verbose)))
@@ -258,8 +257,8 @@ def _dump(record: dict[str, typing.Any]) -> str:
258
257
 
259
258
  _SCHEMA_HINTS_COMPACT: dict[str, str] = {
260
259
  "_note": "non-obvious fields only; -v or get_schema_hints(full=True) for full schema",
261
- "header.commands_count": "integer count; -v emits the full commands array (also archived)",
262
260
  "command.retry_command": "shell to re-run only failing files; failure runs only",
261
+ "command.cached_elapsed": "previous-run elapsed seconds restored with cached=true; this run skipped execution",
263
262
  "command.hint-urls": "rule id -> docs URL map; omitted when no URLs resolved",
264
263
  "messages[].fix": "safe/unsafe/suggested = auto-fixable; none = no fix; omitted = no info",
265
264
  }
@@ -296,6 +295,10 @@ _SCHEMA_HINTS: dict[str, str] = {
296
295
  "shell command to re-run only this command on failing files; populated only when the command failed"
297
296
  ),
298
297
  "command.cached": "true = result restored from file-hash cache; rerun with --no-cache to force",
298
+ "command.cached_elapsed": (
299
+ "previous-run elapsed seconds (seconds) restored alongside cached=true; this run skipped execution."
300
+ " elapsed is omitted when cached=true so only this key represents timing"
301
+ ),
299
302
  "command.truncated": ("diagnostics or message were trimmed; full content is in the archive directory (see header.run_id)"),
300
303
  "header.run_id": "ULID identifying this run; use 'pyfltr show-run <run_id>' to fetch full output",
301
304
  "warning.hint": (
@@ -331,11 +334,9 @@ def _build_header_record(
331
334
  ) -> dict[str, typing.Any]:
332
335
  """実行環境の基本情報を header レコード dict として返す。
333
336
 
334
- 既定 (``verbose=False``) では ``commands`` フィールドを ``commands_count`` (整数) に
335
- 置換し、``schema_hints`` も短縮版にしてヘッダーサイズを抑える。
336
- ``verbose=True`` でフル ``commands`` 配列とフル ``schema_hints`` を埋め込む。
337
- フル ``commands`` 配列は実行アーカイブの ``meta.json`` に常時保存されるため、
338
- 短縮時も ``show-run`` 経由で参照できる。
337
+ ``commands`` は「実際に実行されるツール集合」(``--only-failed`` disabled ツール除外後)を
338
+ 前提とする。呼び出し側で絞り込み済みの配列を渡すこと。
339
+ ``verbose`` ``schema_hints`` のフル/短縮切替のみに効く。
339
340
  """
340
341
  record: dict[str, typing.Any] = {
341
342
  "kind": "header",
@@ -345,11 +346,8 @@ def _build_header_record(
345
346
  "platform": sys.platform,
346
347
  "cwd": os.getcwd(),
347
348
  "files": files,
349
+ "commands": commands,
348
350
  }
349
- if verbose:
350
- record["commands"] = commands
351
- else:
352
- record["commands_count"] = len(commands)
353
351
  if run_id is not None:
354
352
  record["run_id"] = run_id
355
353
  # LLM 向けフィールド補足。毎回出力する (header は各 run の先頭 1 行のみ)。
@@ -408,6 +406,8 @@ def _build_command_record(
408
406
  メッセージ切り詰めまたは diagnostic 切り詰めが発生した場合は ``truncated`` メタを
409
407
  添付する。retry_command は ``CommandResult.retry_command`` が設定されていれば含める。
410
408
  ``hint_urls`` が非空なら ``hint-urls`` キー(ハイフン区切り)で埋め込む。
409
+ ``result.cached`` が真のときは ``elapsed`` ではなく ``cached_elapsed`` キーに
410
+ リネームして出力する(実行をスキップした前回値である旨を LLM に明示するため)。
411
411
  """
412
412
  record: dict[str, typing.Any] = {
413
413
  "kind": "command",
@@ -415,9 +415,13 @@ def _build_command_record(
415
415
  "type": result.command_type,
416
416
  "status": result.status,
417
417
  "files": result.files,
418
- "elapsed": round(result.elapsed, 2),
419
418
  "diagnostics": diagnostics,
420
419
  }
420
+ # cached=True のときは実行をスキップしているため ``elapsed`` は出力せず
421
+ # 前回実行時の計測値を ``cached_elapsed`` として提示する。LLM が「今回の実行時間」と
422
+ # 誤解するのを避けるため両者を同時に出さない設計。
423
+ elapsed_key = "cached_elapsed" if result.cached else "elapsed"
424
+ record[elapsed_key] = round(result.elapsed, 2)
421
425
  if result.returncode is not None:
422
426
  record["rc"] = result.returncode
423
427
 
@@ -137,7 +137,10 @@ def _make_common_parent(custom_commands: collections.abc.Iterable[str] = ()) ->
137
137
  common.add_argument(
138
138
  "--commands",
139
139
  default=None,
140
- help="カンマ区切りのコマンド一覧を指定します。"
140
+ action="append",
141
+ help="対象のコマンド一覧を指定します。複数回指定可能で、各値はカンマ区切りも併用可能です。"
142
+ "例: --commands=mypy --commands=pyright,ruff-check は"
143
+ " --commands=mypy,pyright,ruff-check と同等です。"
141
144
  "(既定: ビルトイン + カスタムコマンドを含む、pyproject.toml で有効な全コマンド)",
142
145
  )
143
146
  common.add_argument("--ui", default=None, action="store_true", help="Textual UI を強制的に有効化します。")
@@ -358,7 +361,8 @@ def _apply_subcommand_defaults(args: argparse.Namespace) -> None:
358
361
  if subcommand in ("run", "fast", "run-for-agent"):
359
362
  args.exit_zero_even_if_formatted = True
360
363
  if subcommand == "fast" and args.commands is None:
361
- args.commands = "fast"
364
+ # ``--commands`` は ``action="append"`` 化によりリストで保持する。
365
+ args.commands = ["fast"]
362
366
  if subcommand == "run-for-agent" and args.output_format is None:
363
367
  args.output_format = "jsonl"
364
368
 
@@ -432,6 +436,26 @@ def run(sys_args: typing.Sequence[str] | None = None) -> int:
432
436
  os.chdir(original_cwd)
433
437
 
434
438
 
439
+ def _flatten_commands_arg(values: list[str] | None, config: pyfltr.config.Config) -> list[str]:
440
+ """``--commands`` で渡されたリスト(複数回指定の集合)をコマンド名配列に展開する。
441
+
442
+ 各要素にはカンマ区切りで複数のコマンドを含められるため、split した上で
443
+ 先頭出現を優先した重複除去を行う。``None`` の場合は設定上の全登録コマンド
444
+ (ビルトイン + custom-commands)を返す。
445
+ """
446
+ if values is None:
447
+ return list(config.command_names)
448
+ seen: set[str] = set()
449
+ result: list[str] = []
450
+ for raw in values:
451
+ for name in raw.split(","):
452
+ if name == "" or name in seen:
453
+ continue
454
+ seen.add(name)
455
+ result.append(name)
456
+ return result
457
+
458
+
435
459
  _OUTPUT_FORMAT_ENV = "PYFLTR_OUTPUT_FORMAT"
436
460
  _VALID_OUTPUT_FORMATS: frozenset[str] = frozenset(pyfltr.formatters.FORMATTERS.keys())
437
461
 
@@ -534,8 +558,9 @@ def _run_impl(
534
558
  # ビルトインのみの default を返すと custom-commands が常にスキップされる。
535
559
  # load_config 後に実体を決定することで、ユーザーが登録した custom-commands
536
560
  # (例: svelte-check) も `run` / `ci` サブコマンドのデフォルト動作で走るようにする。
537
- commands_arg: str = args.commands if args.commands is not None else ",".join(config.command_names)
538
- commands: list[str] = pyfltr.config.resolve_aliases(commands_arg.split(","), config)
561
+ # ``--commands`` ``action="append"`` によりリストで渡るため、各要素を
562
+ # カンマ区切りで再分割して平坦化する。重複は先出を優先して除去する。
563
+ commands: list[str] = pyfltr.config.resolve_aliases(_flatten_commands_arg(args.commands, config), config)
539
564
  for command in commands:
540
565
  if command not in config.values:
541
566
  parser.error(f"コマンドが見つかりません: {command}")
@@ -608,6 +633,11 @@ def run_pipeline(
608
633
  if only_failed_exit_early:
609
634
  return 0, None
610
635
 
636
+ # 実行対象として有効化されていないコマンドはパイプラインから除外する。
637
+ # split_commands_for_execution と同じ条件 (``config.values.get(cmd) is True``) で絞り込み、
638
+ # JSONL header・実行アーカイブ・formatter ctx へ渡す commands を「実際に実行されるもの」に統一する。
639
+ commands = [c for c in commands if config.values.get(c) is True]
640
+
611
641
  # retry_command 再構成用のベース情報を確定する。original_cwd は run() が保存した
612
642
  # --work-dir 適用前の cwd、original_sys_args は起動時の sys.argv[1:] のコピー。
613
643
  effective_cwd = original_cwd if original_cwd is not None else os.getcwd()
@@ -604,7 +604,7 @@ def test_resolve_bin_commandline_mise_not_installed(mocker) -> None:
604
604
 
605
605
 
606
606
  def test_resolve_bin_commandline_mise_tool_not_installed(mocker) -> None:
607
- """miseモードでツールが未インストールの場合、FileNotFoundErrorを送出する。"""
607
+ """miseモードでツールが未インストールの場合、FileNotFoundErrorをstderr付きで送出する。"""
608
608
  mocker.patch("shutil.which", return_value="/usr/local/bin/mise")
609
609
  mocker.patch(
610
610
  "subprocess.run",
@@ -619,10 +619,169 @@ def test_resolve_bin_commandline_mise_tool_not_installed(mocker) -> None:
619
619
  config = pyfltr.config.create_default_config()
620
620
  config.values["bin-runner"] = "mise"
621
621
 
622
- with pytest.raises(FileNotFoundError, match="mise exec"):
622
+ with pytest.raises(FileNotFoundError, match="tool not found"):
623
623
  pyfltr.command._resolve_bin_commandline("ec", config)
624
624
 
625
625
 
626
+ def test_resolve_bin_commandline_mise_untrusted_auto_trust_success(mocker) -> None:
627
+ """未信頼エラー → trust成功 → 再チェック成功の3段で最終的に通常成功扱いになる。"""
628
+ mocker.patch("shutil.which", return_value="/usr/local/bin/mise")
629
+ mock_run = mocker.patch(
630
+ "subprocess.run",
631
+ side_effect=[
632
+ # 1回目の事前チェック: config未信頼で失敗
633
+ subprocess.CompletedProcess(
634
+ ["mise", "exec"],
635
+ returncode=1,
636
+ stdout="",
637
+ stderr="mise ERROR Config files in /path/to/mise.toml are not trusted.",
638
+ ),
639
+ # mise trust --yes --all: 成功
640
+ subprocess.CompletedProcess(
641
+ ["mise", "trust", "--yes", "--all"],
642
+ returncode=0,
643
+ stdout="",
644
+ stderr="",
645
+ ),
646
+ # 2回目の事前チェック(リトライ): 成功
647
+ subprocess.CompletedProcess(
648
+ ["mise", "exec"],
649
+ returncode=0,
650
+ stdout="typos 1.0.0",
651
+ stderr="",
652
+ ),
653
+ ],
654
+ )
655
+
656
+ config = pyfltr.config.create_default_config()
657
+ config.values["bin-runner"] = "mise"
658
+ config.values["mise-auto-trust"] = True
659
+
660
+ path, prefix = pyfltr.command._resolve_bin_commandline("typos", config)
661
+
662
+ assert path == "mise"
663
+ assert prefix == ["exec", "typos@latest", "--", "typos"]
664
+ # 事前チェック → trust → リトライの3回が実際に発生したことを確認
665
+ assert mock_run.call_count == 3
666
+
667
+
668
+ def test_resolve_bin_commandline_mise_untrusted_auto_trust_disabled(mocker) -> None:
669
+ """mise-auto-trust=False のとき trust が呼ばれず、stderr含むエラーメッセージで失敗する。"""
670
+ mocker.patch("shutil.which", return_value="/usr/local/bin/mise")
671
+ mock_run = mocker.patch(
672
+ "subprocess.run",
673
+ return_value=subprocess.CompletedProcess(
674
+ ["mise", "exec"],
675
+ returncode=1,
676
+ stdout="",
677
+ stderr="mise ERROR Config files in /path/to/mise.toml are not trusted.",
678
+ ),
679
+ )
680
+
681
+ config = pyfltr.config.create_default_config()
682
+ config.values["bin-runner"] = "mise"
683
+ config.values["mise-auto-trust"] = False
684
+
685
+ with pytest.raises(FileNotFoundError, match="not trusted"):
686
+ pyfltr.command._resolve_bin_commandline("typos", config)
687
+
688
+ # trust コマンドは呼ばれていないことを確認(subprocess.run の呼び出しは1回のみ)
689
+ assert mock_run.call_count == 1
690
+
691
+
692
+ def test_resolve_bin_commandline_mise_other_error_no_retry(mocker) -> None:
693
+ """未信頼以外のエラー(plugin not found等)ではtrustを呼ばずそのまま失敗する。"""
694
+ mocker.patch("shutil.which", return_value="/usr/local/bin/mise")
695
+ mock_run = mocker.patch(
696
+ "subprocess.run",
697
+ return_value=subprocess.CompletedProcess(
698
+ ["mise", "exec"],
699
+ returncode=1,
700
+ stdout="",
701
+ stderr="mise ERROR plugin not found: typos",
702
+ ),
703
+ )
704
+
705
+ config = pyfltr.config.create_default_config()
706
+ config.values["bin-runner"] = "mise"
707
+ config.values["mise-auto-trust"] = True
708
+
709
+ with pytest.raises(FileNotFoundError, match="plugin not found"):
710
+ pyfltr.command._resolve_bin_commandline("typos", config)
711
+
712
+ # trust コマンドは呼ばれていないことを確認(subprocess.run の呼び出しは1回のみ)
713
+ assert mock_run.call_count == 1
714
+
715
+
716
+ def test_resolve_bin_commandline_mise_untrusted_auto_trust_retry_failure(mocker) -> None:
717
+ """trust後の再チェックも失敗する場合、リトライが1回で打ち切られ通常失敗扱いになる。"""
718
+ mocker.patch("shutil.which", return_value="/usr/local/bin/mise")
719
+ mocker.patch(
720
+ "subprocess.run",
721
+ side_effect=[
722
+ # 1回目の事前チェック: config未信頼で失敗
723
+ subprocess.CompletedProcess(
724
+ ["mise", "exec"],
725
+ returncode=1,
726
+ stdout="",
727
+ stderr="mise ERROR Config files in /path/to/mise.toml are not trusted.",
728
+ ),
729
+ # mise trust --yes --all: 成功
730
+ subprocess.CompletedProcess(
731
+ ["mise", "trust", "--yes", "--all"],
732
+ returncode=0,
733
+ stdout="",
734
+ stderr="",
735
+ ),
736
+ # 2回目の事前チェック(リトライ): 再度失敗
737
+ subprocess.CompletedProcess(
738
+ ["mise", "exec"],
739
+ returncode=1,
740
+ stdout="",
741
+ stderr="mise ERROR some other failure after trust",
742
+ ),
743
+ ],
744
+ )
745
+
746
+ config = pyfltr.config.create_default_config()
747
+ config.values["bin-runner"] = "mise"
748
+ config.values["mise-auto-trust"] = True
749
+
750
+ with pytest.raises(FileNotFoundError, match="some other failure after trust"):
751
+ pyfltr.command._resolve_bin_commandline("typos", config)
752
+
753
+
754
+ def test_resolve_bin_commandline_mise_untrusted_auto_trust_trust_failure(mocker) -> None:
755
+ """mise trust コマンド自体が失敗した場合、trust.stderr を含むエラーで即座に失敗する。"""
756
+ mocker.patch("shutil.which", return_value="/usr/local/bin/mise")
757
+ mocker.patch(
758
+ "subprocess.run",
759
+ side_effect=[
760
+ # 事前チェック: config未信頼で失敗
761
+ subprocess.CompletedProcess(
762
+ ["mise", "exec"],
763
+ returncode=1,
764
+ stdout="",
765
+ stderr="mise ERROR Config files in /path/to/mise.toml are not trusted.",
766
+ ),
767
+ # mise trust --yes --all: 失敗(権限不足等)
768
+ subprocess.CompletedProcess(
769
+ ["mise", "trust", "--yes", "--all"],
770
+ returncode=1,
771
+ stdout="",
772
+ stderr="mise ERROR permission denied: /path/to/mise.toml",
773
+ ),
774
+ ],
775
+ )
776
+
777
+ config = pyfltr.config.create_default_config()
778
+ config.values["bin-runner"] = "mise"
779
+ config.values["mise-auto-trust"] = True
780
+
781
+ with pytest.raises(FileNotFoundError, match="permission denied"):
782
+ pyfltr.command._resolve_bin_commandline("typos", config)
783
+
784
+
626
785
  def test_failed_resolution_result() -> None:
627
786
  """_failed_resolution_resultが失敗用のCommandResultを返す。"""
628
787
  command_info = pyfltr.config.CommandInfo(type="linter")
@@ -852,6 +852,26 @@ bin-runner = "bogus"
852
852
  pyfltr.config.load_config(config_dir=tmp_path)
853
853
 
854
854
 
855
+ def test_mise_auto_trust_default() -> None:
856
+ """mise-auto-trust の既定値は True。"""
857
+ config = pyfltr.config.create_default_config()
858
+ assert config["mise-auto-trust"] is True
859
+
860
+
861
+ def test_mise_auto_trust_disable(tmp_path: pathlib.Path) -> None:
862
+ """pyproject.toml で mise-auto-trust を False に設定できる。"""
863
+ (tmp_path / "pyproject.toml").write_text("[tool.pyfltr]\nmise-auto-trust = false\n")
864
+ config = pyfltr.config.load_config(config_dir=tmp_path)
865
+ assert config["mise-auto-trust"] is False
866
+
867
+
868
+ def test_mise_auto_trust_invalid_type_rejected(tmp_path: pathlib.Path) -> None:
869
+ """mise-auto-trust に bool 以外の値を指定するとエラーになる。"""
870
+ (tmp_path / "pyproject.toml").write_text('[tool.pyfltr]\nmise-auto-trust = "yes"\n')
871
+ with pytest.raises(ValueError, match="mise-auto-trust"):
872
+ pyfltr.config.load_config(config_dir=tmp_path)
873
+
874
+
855
875
  def test_preset_latest_suppresses_language_categories(tmp_path: pathlib.Path) -> None:
856
876
  """preset = "latest" 単独 (カテゴリキー全 False) では全言語のツールが False に押し戻される。"""
857
877
  (tmp_path / "pyproject.toml").write_text('[tool.pyfltr]\npreset = "latest"\n')