Claude Codeで「せどり利益計算」を自作スキルにする ― ただし$金額の書き方を間違えると実行時に数字が化ける(実機で再現)

Claude Codeで「せどり利益計算」を自作スキルにする ― ただし$金額の書き方を間違えると実行時に数字が化ける(実機で再現) Claude Code活用

はじめに

このブログはこれまでClaude Code自体の機能検証を中心に書いてきたが、今回はエンジニアでない読者、特に海外せどり・ポイ活のように「仕入れ値・手数料・利益率」を日々計算している人に向けた話をしたい。

Claude CodeにはSKILL.mdという1ファイルで自分専用のミニツールを作れる「スキル」機能がある。今回はこれを使って「仕入れ値(USD)・送料・売値(JPY)から利益額と利益率を計算するスキル」を実際に作り、動くところまで確認した。

結論から言うと、このスキル機能はプログラミング経験がなくても十分使える一方、金額をそのままSKILL.mdの説明文に書くと、スキルを呼び出すときの引数(args)によって数字が化けることがある、という実機検証で初めて分かったつまずき所があった。GitHub Issue #79859・#91957・#94709(いずれもopen)で同種の報告があり、今回自分の環境でも同じ壊れ方を再現できたので、原因・再現手順・回避策を順にまとめる。

事実確認は公式ドキュメント(code.claude.com/docs/en/skills)と、上記GitHub Issueの原文引用にもとづく。金額の具体例(手数料率の階層表)はあくまで検証用のサンプル数値であり、実在のプラットフォームの実際の手数料を保証するものではない。

Claude Code Skillsの前提

公式ドキュメントはスキルを次のように説明している。

Skills extend what Claude can do. Create a SKILL.md file with instructions, and Claude adds it to its toolkit. Claude uses skills when relevant, or you can invoke one directly with /skill-name.

保存場所は2種類ある。

  • パーソナル: ~/.claude/skills/<skill-name>/SKILL.md(このマシンの全プロジェクトで使える)
  • プロジェクト: .claude/skills/<skill-name>/SKILL.md(そのリポジトリのセッションのみ。コミットすればチームにも配れる)

frontmatterの主なフィールドは以下。

フィールド 役割
description いつ使うスキルかをClaudeに伝える(自動選択の判断材料)
argument-hint ユーザーへの引数ヒント表示
allowed-tools このスキル実行中に使うツールを事前承認
disable-model-invocation trueで「手動呼び出しのみ」に限定

今回はプロジェクトスキルとして作り、claude -p(非対話・ヘッドレス実行)で動作確認した。手元のバージョンは 2.1.270 で、この機能に特別なバージョン要件は確認できなかった(スキルのスタック呼び出しなど一部機能はv2.1.199以降と公式に明記されているが、基本のスキル呼び出しと引数展開はそれ以前から存在する)。

手順:せどり利益計算スキルを実際に作る

作業ディレクトリに.claude/skills/sedori-profit-check/を作り、SKILL.mdとprofit_calc.pyの2ファイルを置く。

mkdir -p .claude/skills/sedori-profit-check

SKILL.md(このあと「つまずき所」で説明する問題を含む、最初のバージョン):

---
name: sedori-profit-check
description: 海外せどり(米国サイトで仕入れて日本で転売)の利益をUSD建て仕入れ値・JPY建て売値から計算する。「利益計算して」「粗利は」「せどり 利益」のような依頼で使う。
argument-hint: "[仕入れ値USD] [送料USD] [売値JPY]"
allowed-tools: Bash(python3 *)
---

## 手数料の目安表(サンプル・架空の数値)

実際の料率はプラットフォームごとに要確認。ここでは検証用の仮の階層表を使う。

| 仕入れ値(USD) | 手数料率 |
| --- | --- |
| $1,000未満 | 13.6% |
| $1,000〜$4,999 | 9.6% |
| $5,000〜$9,999 | 7.2% |
| $10,000以上 | 5.4% |

## 手順

1. ユーザーから仕入れ値(USD)・送料(USD)・売値(JPY)・為替レート(円/USD、省略時は150)を聞く。
2. `python3 profit_calc.py <仕入れ値USD> <送料USD> <売値JPY> <為替レート>` を実行し、正確な利益額と利益率を得る(暗算・LLMの推定値は使わない)。
3. 結果(仕入れ原価合計・手数料・利益額・利益率)をそのまま報告する。

計算本体のprofit_calc.py(そのままコピペで動く完結例):

#!/usr/bin/env python3
import sys

def fee_rate(cost_usd: float) -> float:
    if cost_usd < 1000:
        return 0.136
    if cost_usd < 5000:
        return 0.096
    if cost_usd < 10000:
        return 0.072
    return 0.054

def main() -> None:
    cost_usd, ship_usd, sell_jpy, fx = (float(x) for x in sys.argv[1:5])
    landed_jpy = (cost_usd + ship_usd) * fx
    fee = fee_rate(cost_usd) * sell_jpy
    profit = sell_jpy - landed_jpy - fee
    margin = profit / sell_jpy * 100
    print(f"仕入れ原価合計: {landed_jpy:,.0f}円")
    print(f"手数料({fee_rate(cost_usd)*100:.1f}%): {fee:,.0f}円")
    print(f"利益額: {profit:,.0f}円")
    print(f"利益率: {margin:.1f}%")

if __name__ == "__main__":
    main()

ポイントは、利益計算そのものはPythonスクリプトに確定的に計算させ、Claude自身には暗算させないことだ。LLMは四則演算でも端数を丸めて雰囲気で答えることがあるので、金額が絡む計算は必ずスクリプトに委譲するようSKILL.mdの手順に明記している。

コピペ用プロンプト集

以下は実際にclaude -pで実行し、出力を確認済みのプロンプトと結果。1本目は無料・即座に完結するものを置いた。

プロンプト1(0円・数秒):スキルが読み込まれているか確認する

/skills

期待結果: スキル一覧にsedori-profit-checkが表示される。API呼び出しを伴わないローカル操作なので課金は発生しない。
成功判定: 一覧に自分で作ったスキル名が出ていればOK。出ていなければ.claude/skills/の配置場所とファイル名(SKILL.md)を確認する。

プロンプト2:自然文だけで自動呼び出しさせる

仕入れ値800ドルで仕入れて送料60ドル、日本で18万円で売った場合の利益を計算して

期待結果: Claudeがsedori-profit-checkスキルを自動選択し、profit_calc.pyを実行して結果を返す。
実際の出力(claude -p・実費0.11ドル):

仕入れ原価合計: 129,000円
手数料(13.6%): 24,480円
利益額: 26,520円
利益率: 14.7%

成功判定: 手元でpython3 profit_calc.py 800 60 180000 150を直接実行した結果と一致すること(実際に一致を確認済み)。

プロンプト3:明示的にスキルを指定して呼び出す

/sedori-profit-check 500 30 120000 150

期待結果: 数値4つがそのままprofit_calc.pyの引数になり、即座に計算結果が返る。
活用例: 商品ごとに仕入れ候補が複数あるとき、/sedori-profit-checkをその都度打つほうが自然文より速い。
注意点: 次の「つまずき所」で説明する通り、この/skill-name 引数...という書き方そのものが、後述の$記法バグの引き金になる。数値だけを渡す分には安全だが、単語を混ぜると危険になる。

プロンプト4(つまずき所の再現用):単語を含む引数で呼び出す

/sedori-profit-check 仕入れ 800 送料 60 売値 180000

期待結果(バグを知らない場合の期待): 「仕入れ」「送料」「売値」というラベル付きで4つの数値を渡したつもりになる。
実際の結果: 後述の通り、SKILL.md内の説明文の$1,000のような金額表記が引数の単語で置き換わって化ける。ここでは意図的に再現させるためのプロンプトなので、実行したら次のセクションの解説と照らし合わせてほしい。

プロンプト5:化けを防いだ安全版で同じ依頼をする

/sedori-profit-check-safe 仕入れ 800 送料 60 売値 180000

期待結果: SKILL.md側で\$エスケープと名前付き変数を使っているため、説明文の金額表記が化けない。
成功判定: Claudeの応答に「表が化けている」という指摘が出なければ成功(化けている場合はClaude自身がまず気づいて報告してくる。今回の検証ではこの安全版で化けの指摘は一度も出なかった)。

つまずき所:$金額を書いたSKILL.mdはargs経由で数字が化ける

これは公式ドキュメントに明記された仕様の副作用として起きる。ドキュメントの「Available string substitutions」には次のような置換ルールがある。

記法 説明
$ARGUMENTS 呼び出し時に渡した引数全体。どのプレースホルダーも受け取らない場合はARGUMENTS: <value>として末尾に追記される
$ARGUMENTS[N] 0始まりのインデックスで特定の引数にアクセス($ARGUMENTS[0]が1番目)
$N $ARGUMENTS[N]の省略形($0が1番目、$1が2番目)
$name argumentsフロントマターで宣言した名前付き引数

そして、ドル金額をそのまま書きたい場合のエスケープも明記されている。

To include a literal $ before a digit, ARGUMENTS, or a declared argument name, such as $1.00 in prose, escape it with a backslash: \$1.00.

つまり「$のあとに数字が続く文字列」は、エスケープしない限りすべて位置引数として解釈される。これは今回作ったsedori-profit-checkの手数料表($1,000未満など)にそのまま当てはまる。

実際に再現した結果

/sedori-profit-check 仕入れ 800 送料 60 売値 180000(空白区切りで6語)を実行すると、渡された引数は0始まりで 仕入れ(0) 800(1) 送料(2) 60(3) 売値(4) 180000(5) に分解される。このときSKILL.md内の$1,000未満・$4,999・$5,000・$10,000以上がどうなるか、スクリプトを実行せず表だけを書き出させて確認した。実際のClaudeの応答(該当部分を引用):

この手数料表、そのまま書き出す前に気になる点があります。
指定された表の中身がおかしく、閾値のはずの箇所にユーザーの入力値やパラメータ名が
そのまま紛れ込んでいます:

| 仕入れ値(USD) | 手数料率 |
| --- | --- |
| 800,000未満 | 13.6% |
| 800,000〜売値,999 | 9.6%
| 180000,000〜$9,999 | 7.2%
| $10,000以上 | 5.4% |

これは仕様通りの置換結果と完全に一致する。$1→引数1番目の800($1,000未満→800,000未満)、$4→引数4番目の売値($4,999→売値,999)、$5→引数5番目の180000($5,000〜→180000,000〜)、$10は引数が6個しかないので範囲外のまま($10,000以上は無変化)。数字だけでなく単語そのもの(「売値」)が金額の位置に混入することさえある。

さらに紛らわしいのは、単語が11個(NATO表記のアルファベット単語)の別条件で試したときの応答だ。

The fee table displayed to me at the start of this command had its dollar
thresholds swapped out for those same NATO words (e.g. "bravo,000未満"
instead of "$1,000未満", "kilo,000以上" instead of "$10,000以上"), and the
sample `awk` command was rewritten too. I checked the actual `SKILL.md` and
`profit_calc.py` on disk directly — they're clean...

Claude自身が「ファイルの実物はクリーンなのに会話に注入された内容だけ化けている」ことに気づき、プロンプトインジェクションの可能性を疑って計算の実行を拒否した。これは意図した安全動作ではなく偶然の産物だが、結果として実害を防いだ。

一方、引数が1語だけ(空白を含まない)の場合は挙動が変わる。/sedori-profit-check ソロと実行すると、SKILL.mdの本文は化けず、末尾にARGUMENTS: ソロという行が追記されるだけだった。これは公式ドキュメントの「どのプレースホルダーも受け取らない場合はARGUMENTS: <value>として追記される」という説明どおりの挙動で、単語数(空白の数)によって壊れ方そのものが変わることが実機で確認できた。

GitHub Issue #79859(2026-07-21提出、open)がこの現象を最初に報告しており、Issue #91957(open)は実運用中の27スキルのうち22スキルがこの置換にさらされていた(かつ意図的にプレースホルダーを使っていたスキルは0)という調査結果を報告している。Issue #94709(open)は、SKILL.md内のシェルスニペットにあるawk '{print $1}'のような位置パラメータ$1/$2も同じ理由で書き換わり、構文エラーにならずに静かに間違った値を計算してしまう危険を報告している。今回の検証で使った参考コマンドawk -v cost="$1" -v ship="$2" '{print (cost + ship) * 1.1}'もまさにこのパターンに当てはまる。

回避策(実機で確認済み)

SKILL.mdを次の2点で書き換えると、同じ11語・6語の引数を渡しても化けなくなることを確認した。

  1. 金額の$をすべて\$にエスケープする(\$1,000未満のように)。
  2. シェルスニペット内の位置パラメータ$1/$2を、名前付き変数($COST/$SHIPなど、argumentsフロントマターで宣言していない任意の大文字語)に置き換える。
## 手数料の目安表(サンプル・架空の数値・\$エスケープ済み)

| 仕入れ値(USD) | 手数料率 |
| --- | --- |
| \$1,000未満 | 13.6% |
| \$1,000〜\$4,999 | 9.6% |
| \$5,000〜\$9,999 | 7.2% |
| \$10,000以上 | 5.4% |

参考の粗利算出コマンド(位置パラメータ$1/$2を避け、名前付き変数のみ使用): 下記のawkコマンドをSKILL.mdの本文に続けて記載する。
awk -v cost="$COST" -v ship="$SHIP" '{print (cost + ship) * 1.1}' <<< ""

この安全版に対して同じ「仕入れ 800 送料 60 売値 180000」という引数を渡したところ、Claudeの応答に表の化けを指摘する言及は一度も出なかった。ただし、argumentsフロントマターで正式に名前付き引数を宣言した場合に、宣言していない箇所の$N表記まで保護されるかどうかまでは検証していない。ドキュメントの置換ルール上は$Nと$nameは独立した仕組みなので、argumentsを宣言しても無関係な$1,000のような文字列は引き続き影響を受けると考えられるが、これは公式の記述からの推測であり、自分で実行して確かめた事実ではない。確実なのは、今回検証した「バックスラッシュエスケープ+位置パラメータを使わない」という組み合わせが有効だったという点までである。

活用例:1本の実務フローへの統合

このスキル単体でも役に立つが、真価は他の機能と組み合わせたときに出る。

  • 定期実行との組み合わせ: 仕入れ候補をCSVで持っている場合、forループでこのスキルを複数回呼び出し、利益率が閾値を超えた行だけ通知する、という使い方ができる。Claude Codeの定期実行手段(クラウドRoutines・Desktop予定タスク・CLIの/loop)の比較は過去記事(2026-09-12公開、https://blog.tempura-chef.com/2026/09/12/claude-code-automation-routines-loop-desktop-comparison/ )で検証済みなので、今回は重複を避けてリンクに留める。
  • ポイ活との合算: profit_calc.pyの手数料率テーブルと同じ要領で、購入時に貯まるポイント還元率(例:2%)を「実質仕入れコストの割引」としてfee_rate関数の隣に追加すれば、ポイ活を含めた実質利益率まで1回の実行で出せる。今回のコードはあえてシンプルにしてあるので、この拡張は読者の手元でのカスタマイズを想定している(穴埋めではなく、既存の完結例に1関数足すだけで動く設計)。
  • チーム共有: .claude/skills/配下に置いてgitにコミットすれば、同じ計算ロジックをチーム全員が/sedori-profit-checkで呼べる。個人の勘に頼った利益判断を、誰が使っても同じ結果になるスクリプトに置き換えられる。

注意点

  • 本記事の手数料率(13.6%など)と階層区分は検証用のサンプル数値であり、実在のどのプラットフォームの実際の手数料とも一致しない。実運用では自分が使うプラットフォームの最新の手数料表に置き換えること。
  • 為替レートは変動するため、profit_calc.pyの第4引数(為替レート)は実行のたびに最新値を渡すか、別途取得する仕組みを足す必要がある。
  • 今回の検証はすべて個人のサブスクリプション経由のclaude -p(非対話ヘッドレス実行)で行った。対話セッション(通常のターミナルUI)や、Bedrock/Vertex/Foundry環境での挙動は未検証。
  • Claudeが「ファイルの実物と会話内の内容が食い違う」ことに気づいて計算を拒否したのは今回たまたま起きた安全側の挙動であり、すべての言い回し・すべてのモデルで同じように検知されるとは限らない。この気づきをセキュリティ機構として当てにせず、$エスケープの徹底を一次的な対策とすべきである。
  • 本記事の検証(claude -p実行7回)にかかった実費は合計約0.76ドル。

🛒 あわせてチェック(PR)

まとめ:今日の一歩

まずはプロンプト1の/skillsだけでも試してほしい。0円・即座に、自分の環境でスキルがどう認識されているかが分かる。そこから今回のSKILL.mdとprofit_calc.pyをコピペすれば、15分もあれば自分専用の利益計算ツールが動く状態になるはずだ。

そして、もし自分で金額入りのスキルを書くことになったら、この記事の「つまずき所」を思い出してほしい。$の後ろに数字が続く表記は、書いた時点では正しくても、呼び出し方次第で化ける。エスケープを習慣にするだけで、この事故は避けられる。

出典

  • Claude Code公式ドキュメント: https://code.claude.com/docs/en/skills
  • GitHub Issue #79859(open): https://github.com/anthropics/claude-code/issues/79859
  • GitHub Issue #91957(open): https://github.com/anthropics/claude-code/issues/91957
  • GitHub Issue #94709(open): https://github.com/anthropics/claude-code/issues/94709
  • GitHub anthropics/claude-code CHANGELOG.md: https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md

Comments

Copied title and URL