Claude CodeでSalesforce Code Analyzerの指摘を直す・抑える・保留に振り分ける
sf code-analyzer runのJSON出力をclaude -pに読ませ、修正・抑制・要判断の3分類にする手順。jqでの絞り込みと--json-schemaの固定、抑制マーカーの落とし穴までまとめます。
Salesforce Code Analyzerを既存のorgのソースに初めて当てると、指摘は数百件単位で並びます。全部を直す必要はありません。直すもの、理由を残して抑えるもの、人が決めるものに分けて、初めて次の作業が決まります。
この仕分けは、sf code-analyzer runのJSON出力をclaude -pに読ませれば自動化できます。ただし一括で「直して」と頼むと、抑制マーカーが大量に付くか、判断が要る指摘まで書き換えられます。そこで、仕分けと適用を2回の呼び出しに分けます。1回目は分類だけを返させ、人が確認してから2回目で手を入れる流れです。
先に結論: 分類と適用を別の呼び出しにする
流れは4段です。
指摘を仕分けて直すまでの流れ
- 1
JSONで結果を出す
sf code-analyzer run --output-file results.jsonで、結果をJSONファイルに書き出します。 - 2
jqで必要な項目だけに絞る
違反1件ごとの規則名・重大度・場所・メッセージ・自動修正の有無だけを残し、番号を振ります。
- 3
claude -pに分類だけをさせる
--json-schemaで出力の形を固定し、番号ごとに修正・抑制・要判断のどれかを返させます。この段では何も書き換えません。 - 4
人が確認してから適用する
修正と抑制の対象を別々に渡し、再度
sf code-analyzer runで件数が減ったことを確かめます。
分類を構造化出力で返させる理由は、結果を機械で扱えるからです。「要判断」の件数だけ数える、修正対象だけを次の呼び出しに渡す、といった後続の処理が、本文の文章を読み解かずに書けます。
Code AnalyzerのJSONに入っている項目
sf code-analyzer runは、既定ではRecommendedのルールで、カレントフォルダ全体を対象にします。結果をファイルにするには--output-fileを使い、拡張子で形式が決まります。.jsonのほか、.csv、.html、.sarif、.xmlが指定できます。出力先のフォルダが無いとエラーになり、同名のファイルは確認なしで上書きされます。
sf code-analyzer run \
--rule-selector Recommended \
--workspace . \
--include-fixes \
--output-file results.json--include-fixesを付けると、自動修正が用意されている違反に、置換する場所とコードが付きます。この有無が、後で「直す」に倒す根拠の1つになります。--include-suggestionsは、修正の方向を文章で示す提案を足すフラグです。どちらも、付けると解析時間が延びる場合があります。
JSONの中身は、コアのソースにある型定義で確認できます。トップレベルはrunDir、violationCounts、versions、violationsです。violationsの1件が持つ項目は次のとおりです。
| 項目 | 内容 |
|---|---|
rule / engine | 内容規則名と、それを実行したエンジン名 |
severity | 内容1(Critical)から5(Info)までの数値 |
tags | 内容規則に付いたタグ(Recommendedなど) |
locations / primaryLocationIndex | 内容違反の位置の配列と、主たる位置の添字 |
message / resources | 内容違反メッセージと、関連するURLの配列 |
fixes / suggestions | 内容自動修正と提案(出力に含めた場合のみ) |
位置のfileはrunDirからの相対パスです。violationCountsには重大度ごとの件数が入るので、まずjq '.violationCounts' results.jsonで全体の規模を見ておくと、次の絞り込みの判断がしやすくなります。
jqで入力を絞り、番号を振る
JSONの全文をそのまま渡すと、メッセージ以外の項目も大量に載り、claude -pが受け取れる入力の上限にも近づきます。標準入力で渡せるのは10MBまでで、超えるとエラー終了します。上限の扱いはclaude -pにログを渡すと10MB上限で止まるにまとめています。
絞り込みでは、番号を振ることが肝心です。分類の結果を元の違反に結び付けるキーが無いと、同じ規則名の指摘が複数ある場面で、どれのことか分からなくなります。
jq '[.violations | to_entries[] | {
id: .key,
rule: .value.rule,
engine: .value.engine,
severity: .value.severity,
tags: .value.tags,
file: .value.locations[.value.primaryLocationIndex].file,
line: .value.locations[.value.primaryLocationIndex].startLine,
message: .value.message,
hasFix: ((.value.fixes // []) | length > 0)
}]' results.json > violations-slim.jsonto_entriesで配列の添字をidとして取り出し、位置は主たる位置の1か所だけに畳んでいます。fixesが無い違反は// []で空配列に置き換えているので、hasFixは真偽値で揃います。
3分類の基準を先に決める
分類の基準は、Claudeに推測させず、先に文章で決めます。次はその一例です。組織のリスクの取り方に合わせて書き換えてください。
修正(fix):
- hasFixがtrueの指摘
- 違反メッセージだけで直し方が一意に決まり、動作が変わらない指摘
抑制(suppress):
- テストクラスや生成コードなど、指摘の前提が当てはまらない場所の指摘
- 意図した実装であることがコードから読み取れる指摘
要判断(review):
- 権限・共有・入力検証など、セキュリティに関わる指摘
- 直すと挙動やデータの扱いが変わりうる指摘
- 上のどれにも確信が持てない指摘最後の行が要です。迷ったものを「要判断」に倒す規則を明示しておくと、確信のない抑制が混ざりにくくなります。「抑制」も「修正」も、人が見たときに理由を説明できる指摘に限ります。
claude -pで分類だけを返させる
分類の出力は、--json-schemaで形を固定します。--output-format jsonと組み合わせると、応答のメタ情報とは別に、スキーマに沿った結果がstructured_outputに入ります。スキーマに合う出力が得られなかった場合、このフィールドは付きません。後続の処理は、存在を確かめてから使います。
次のスキーマは、各指摘にdecisionとreasonを返させる例です。decisionは列挙に限定しています。
{
"type": "object",
"properties": {
"items": {
"type": "array",
"items": {
"type": "object",
"properties": {
"id": { "type": "integer" },
"decision": { "type": "string", "enum": ["fix", "suppress", "review"] },
"reason": { "type": "string" }
},
"required": ["id", "decision", "reason"]
}
}
},
"required": ["items"]
}これをtriage-schema.jsonとして保存し、絞り込んだJSONを標準入力から渡します。基準の文章はtriage-rules.txtに置いた想定です。
cat violations-slim.json | claude -p \
"$(cat triage-rules.txt)
上のJSONは静的解析の指摘です。基準に従い、全件をfix・suppress・reviewに分類してください。ファイルは変更しないでください。" \
--allowedTools "Read" \
--output-format json \
--json-schema "$(cat triage-schema.json)" \
| jq '.structured_output' > triage.json許可するツールはReadだけにしています。分類の段階では、ソースを読んで根拠を確かめられれば足り、書き換えは要りません。権限の待ちで止まらないように、使わせるツールを--allowedToolsで先に決めておくのが、非対話実行の基本です。
スクリプトやCIに組み込むなら、--bareの追加が推奨されています。フックやプラグイン、CLAUDE.mdを読まないので、どのマシンでも同じ結果になりやすくなります。その代わり、サブスクリプションのログインは使われず、ANTHROPIC_API_KEYが必要です。基準がCLAUDE.mdに頼らずtriage-rules.txtに入っていれば、この構成でも動きます。
分類が返ったら、件数を数えます。
jq '.items | group_by(.decision) | map({decision: .[0].decision, count: length})' triage.json「要判断」が多すぎれば基準が厳しすぎ、「抑制」が過半ならば基準が緩いというのが、見直しの目安です。ここで基準を直し、分類をやり直すのは安く済みます。ソースには一切触れていないからです。
抑制は範囲と理由に気をつける
「抑制」と判断した指摘は、ソースにマーカーを書くか、設定ファイルにまとめて書くかのどちらかで抑えます。どちらも、Claudeに任せる前に挙動を知っておく必要があります。
行内マーカーは、閉じるまで効く
ソースのコメントにcode-analyzer-suppressを書くと、そのコメントから後の違反が抑制されます。括弧でルールを指定でき、eslint:no-consoleのように、エンジンと規則名をコロンでつなぎます。括弧を省いた形も書けますが、その場合は全ルールが対象です。
// code-analyzer-suppress(pmd:RuleName)
// ...抑制したい範囲...
// code-analyzer-unsuppress(pmd:RuleName)コアのソースを読むと、code-analyzer-unsuppressが現れなかった場合、抑制の範囲はファイルの末尾まで続きます。1行だけのつもりで書いたマーカーが、その後ろの同じ規則の違反を、すべて隠してしまう形です。
--no-suppressionsの説明には、code-analyzer-suppress-lineとcode-analyzer-suppress-next-lineという名前も載っています。行単位で閉じる形式があるなら、そちらを使わせるのが安全です。お使いの版のドキュメントで書式を確かめ、使う形を指示に書いておきます。
一括抑制は、設定ファイルに理由と上限を書く
レガシーなフォルダ全体の指摘は、code-analyzer.ymlのsuppressionsに書けます。キーはファイルやフォルダのパスで、規則の選び方、抑制する件数の上限、理由を並べます。
suppressions:
disable_suppressions: false
"force-app/legacy":
- rule_selector: "pmd:RuleName"
max_suppressed_violations: 5
reason: "旧システム連携のため。移行完了後に外す"上限を超えた分は抑制されず、指摘として残ります。「5件までは許す」という線引きができるので、同じ規則の指摘が増えたときに気づけます。
抑制した件数を、あとから見られるようにする
--no-suppressionsを付けて実行すると、ソースのマーカーも設定ファイルの抑制も無視した結果が得られます。通常の結果と件数を比べれば、抑制がどれだけ効いているかが分かります。四半期ごとに一度回して、抑制の棚卸しをする運用に向きます。
修正と抑制を、別々に適用する
人がtriage.jsonを確認したら、適用です。ここでは対象を分けて、2回に分けて呼びます。
jq '[.items[] | select(.decision == "fix") | .id]' triage.json > fix-ids.json修正の呼び出しでは、Editを許可し、渡すのはfixの違反だけにします。
jq --slurpfile ids fix-ids.json \
'[.[] | select(.id as $i | $ids[0] | index($i))]' \
violations-slim.json \
| claude -p "この指摘を1件ずつ修正してください。指摘の範囲外のコードは変更せず、終わったらgit diffの要約だけを返してください。" \
--allowedTools "Read,Edit"抑制の呼び出しも同じ形ですが、指示に「マーカーは必ず対になるcode-analyzer-unsuppressで閉じる」「理由をコメントに1行添える」を入れます。reasonの文面をtriage.jsonから引いて渡せば、分類で書かせた根拠がそのままコードに残ります。
最後に、同じ条件でsf code-analyzer runを再実行します。--severity-threshold 3を付けると、重大度が3(Moderate)以上の違反が残っているときに、終了コードが0以外になります。数値はHighのような文字列でも指定できます。CIでの合否判定にもそのまま使えます。
つまずきやすい場面
落とし穴
分類の根拠は、実行するまで確かめられない
reasonは、モデルが書いた説明です。正しいかどうかは、ソースを見た人が判断するしかありません。特に「抑制」と書かれた指摘は、理由が自然に読めても、サンプルを抜き出して確認してから適用します。
- リモート接続が要るエンジンがある:
--target-orgは、ApexGuruのような、組織への接続が必要なエンジン向けのフラグです。認証済みのorgを指定するので、Claudeのsf実行権限の切り方はClaude CodeでSalesforce CLIの権限を切るを先に読んでおくと安全です。 - 件数が多すぎる: 数千件になると、分類の呼び出し1回では入力が収まらないことがあります。
jqで規則名ごとにグループ化し、規則ごとの代表例と件数だけを渡す分類から始めると、基準の見直しも速く済みます。 - 同じ流れの別ツール: 修正・抑制・保留への振り分けは、他の静的解析にもそのまま使えます。PlatformIOの例はpio checkの指摘を修正と抑制に仕分けるにあります。
- テストの修正まで任せない: 指摘を直した結果、Apexのテストが落ちることがあります。修正のあとにテストを回して失敗を直す手順は、CIのテスト失敗を自動修正に分けて書いています。
まとめ
Code Analyzerの指摘が多いときは、一括修正に飛びつかず、まず分類だけをclaude -pに出させます。JSONへの番号付与と--json-schemaによる形の固定で、結果は機械で数えられ、人が確認しやすくなります。抑制マーカーは閉じ忘れると範囲がファイル末尾まで伸びるので、閉じる形を指示に含めます。迷ったものを「要判断」に倒す一行が、仕分け全体の安全弁です。
参照した一次ソース
この記事の記述は、次の一次ソースと照合しています。更新時に参照先が変わった場合は一覧も更新します。
関連する記事
Claude Code をもっと見る →Claude Codeとは — できること・料金・始め方と使い方の全体像
Claude CodeでABAP Cloud移行のATC指摘を仕分ける — 置換APIとラッパーの分け方
Claude CodeでIAMの未使用アクセスキーを棚卸しする — credential reportで削除候補を分ける
Claude CodeでCognitoのカスタムクレームを書く — pre token generationを単体テスト
Claude CodeでYouTube Data APIの消費量を見積もり全動画を取る
ShopifyのGraphQLレート制限をClaude Codeで守る — 待ち時間の計算とbulk切替