【cuePaste】
macOS用のコマンド式の定型文ペース
トツールを作った
(Swift, Xcode)

関連記事

1. cuePasteアプリを作った

CuePasteは、ショートカットキーからコマンドをタイプして、入力欄に保存されているプロンプトを出力するツールです。

1. cuePasteアプリを作った

一応、appを置いておきますが、まったくの無保証です。
起動には、ソースコードを確認して、自己責任でご利用ください。
(自分で、CodexやClaude Codeなどに渡して、カスタマイズしてください)

開発者署名がないので起動には、セキュリティ上の許可が必要です。

1.1. 定型プロンプトをすぐに出したい

チャットAIを日常的に使うようになってから、プロンプトの管理に困るようになりました。

1.1. 定型プロンプトをすぐに出したい

長い指示文をそのつど書くのは手間なので、クリップボード管理アプリに定型文として登録していました。
ところが、登録数が増えると目的のものをリストから探す作業も増えてきます。
マウスでスクロールして、目で追って、クリックする。
この動作が積み重なると、地味に気になってきます。

欲しいのは、EmacsのコマンドやRaycastのような体験でした1
ショートカットで入力画面を開き、名前の一部を打てば候補が絞り込まれ、Enterで貼り付けられる。
それだけでいい。

そのイメージで作ったのがCuePasteです。

1.2. ファイルシステムをプロンプトの置き場にする

設計の中心にした考え方は、シェルのPATHに近いものです。

1.2. ファイルシステムをプロンプトの置き場にする

コマンドをパスの通ったディレクトリに置けば名前で呼べるように、プロンプトをフォルダに置いてファイル名をキーワードにします。

~/Documents/prompts/summarize.md
~/Documents/prompts/review-code.md
~/Documents/prompts/translate-ja.mdCode language: JavaScript (javascript)
1.2. ファイルシステムをプロンプトの置き場にする

summarize と打てば summarize.md の中身が前面アプリへ貼り付けられます。
拡張子は .md を優先し、なければ .txt にフォールバックします。
それだけの仕組みです。

ファイルとして管理することには、もうひとつ利点があります。
Gitが使えます2
プロンプトは「前のバージョンの方がよかった」という場面が意外と多く、指示の言い回しをチューニングしていくうちにどこかで精度が落ちることがあります。
ファイルならば差分を確認して戻せます。

2. ローカルにXcodeがなくてもビルドできた

macOS向けのアプリですが、コーディング自体は手元のLinuxマシンでCodexを使って進めました。
Swiftのコードをプライベートリポジトリへpushし、ビルドと実行確認はGitHub Actionsに任せます。

2. ローカルにXcodeがなくてもビルドできた

メイン機はMacBook Air 2018なのですが、内蔵ストレージの空きが約10GBしかありません。
Xcodeは圧縮ファイル、展開時の一時領域、本体、ビルドデータを合わせると40GB以上を必要とします。
インストールすら始められない状態でした。

しかし、GitHubのリポジトリを使えば、ターゲット環境用のビルドができます。
ただ、macOS向けの .app バンドルは、Swift Packageのビルドだけでは生成されません。
Contents/MacOSContents/ResourcesInfo.plist をシェルスクリプトで組み立て、hdiutil でDMG化する処理を scripts/build-dmg.sh にまとめました。
GitHub ActionsのmacOS runnerがこのスクリプトを実行し、Intel Mac向けの未署名DMGをGitHub Releasesへアップロードします3

# workflowの概要
- uses: actions/checkout@v4
- name: Build
  run: swift build -c release
- name: Make DMG
  run: bash scripts/build-dmg.sh
- name: Upload Release Asset
  uses: softprops/action-gh-release@v2
  with:
    files: CuePaste.dmgCode language: YAML (yaml)

GitHubアカウントがあれば、コンパイラも開発環境もローカルに持たずに、ターゲットプラットフォーム向けのバイナリを出力できます。
これはとても助かりました。

2.1. macOS固有処理の壁

アプリの動作は単純ですが、実装はmacOSに深く依存しています。

2.1. macOS固有処理の壁
  • グローバルショートカットで入力画面を開く
  • ショートカットを押した時点の前面アプリを記録しておく
  • 入力後、そのアプリへフォーカスを戻す
  • NSPasteboard にプロンプト本文を書き込む
  • Core Graphicsの CGEventCommand + V のキーイベントを送る

「前面アプリへ貼り付ける」という一言の裏に、これだけの処理が並びます。
CGEvent でキーイベントを送るにはアクセシビリティ権限が必要で4、権限がなければクリップボードへのコピーだけに留める動作も考慮しなければなりません。

UIも一筋縄ではいきませんでした。
メニューバー常駐にはSwiftUIの MenuBarExtra が使えるものの5、入力画面は「画面中央付近に表示する」仕様なので MenuBarExtra のポップオーバーでは位置が合いません。
NSPanel で独自のウィンドウを作り、中身をSwiftUIで構築する形にしました。

Tab補完の実装でも詰まりました。
NSTextField は実際の入力を内部の NSTextView(field editorと呼ばれる)へ委譲するため6keyDown だけではReturnやTabを安定して捕捉できません。
NSTextFieldDelegatecontrol(_:textView:doCommandBy:)insertNewline:insertTab: を処理する形に変更して解決しました。
補完の仕様は前方一致のみとし、候補が複数あれば共通prefixまで補完、さらにTabを押すと候補を順に巡回します。

@mainmain.swift の衝突にも一度はまりました。
Swift Package Managerでは main.swift がトップレベルコード用として扱われるため、同じターゲット内で @main を使うとコンパイルエラーになります。
アプリの入口を CuePasteApp.swift に移して解決しました。

2.2. 自動ペーストとApp Sandboxの問題

クリップボードを使うアプリは、セキュリティとプライバシーの許可が必要です。

2.2. 自動ペーストとApp Sandboxの問題

App StoreではApp Sandboxが必須で、AppleはアクセシビリティAPIを使うアプリのSandbox互換性について制約を示しています。
CuePasteの中心機能である自動 Command + V は、この制約と相性が悪い可能性があります7

また、MVPでは未署名DMGの直接配布として割り切りました8。。
Developer ID署名と公証があればGatekeeperは通せるため、Mac App Storeを経由しなくても配布できます。

未署名DMGは初回起動時にGatekeeperの警告が出ます。
ターミナルから xattr -d com.apple.quarantine CuePaste.app で回避できますが9、一般的な導線ではないため、署名・公証は初期リリース後の対象として先送りにしました。

3. 使ってみると

実機での確認を終えた現時点で、グローバルショートカットの呼び出し、Tab補完による候補巡回、前面アプリへの自動貼り付けは動いています。

3. 使ってみると

プロンプトフォルダは設定画面で変更でき、ショートカットキーもSettingsのRecordボタンで任意のキーに登録できます。

3. 使ってみると

マウスに手を伸ばさずにプロンプトを呼び出せるのは、地味ですが確実に手数が減ります。
プロンプトファイルをGitで管理するようになって、「前の表現に戻したい」ときも git loggit checkout で対応できるようになりました。

  1. Raycastのスニペット機能は、キーワードを入力するだけで登録済みの定型文を前面アプリへ直接貼り付けられます。検索して選ぶUIとキーワードによる自動展開の両方に対応しています。 – Text Expander: Snippet Management for Mac | Raycast
  2. プロンプトをテキストファイルとして管理すると、git diff で変更前後の表現を比較でき、git checkout で任意の時点に戻せます。プロンプトエンジニアリングの試行錯誤には、この差分管理が実用的です。 – Git – Recording Changes to the Repository
  3. GitHub ActionsのIntel macOS runner(macos-13)は2025年12月に廃止されました。Intel向けビルドを継続するには macos-15-intel ラベルへの移行が必要です。このラベルはFall 2027まで提供予定とされています。 – GitHub Actions: macOS 13 runner image is closing down | GitHub Changelog
  4. CGEvent.post でキーイベントを送信するには、システム設定のプライバシーとセキュリティでアクセシビリティの許可が必要です。権限確認には AXIsProcessTrustedWithOptions を使います。権限がない状態で送信を試みてもイベントは無視されます。 – Accessibility Permission in macOS | jano.dev
  5. MenuBarExtra はmacOS 13 Venturaで追加されたSwiftUIのシーン型です。それ以前はAppKitのNSStatusItemを直接扱う必要がありました。 – Creating Menu Bar Apps in SwiftUI for macOS Ventura | blog.schurigeln.com
  6. field editorはウィンドウ内の全テキスト入力コントロールが共有する単一のNSTextViewインスタンスです。NSTextFieldが軽量に見えるのは、実際の入力処理をfield editorに任せているためです。 – Working With the Field Editor | Apple Developer Documentation Archive
  7. App Storeに提出されたクリップボード管理アプリが CGEvent.post による Command+V 送信を理由にGuideline 2.4.5違反として却下された事例がApple Developer Forumsで報告されています。アクセシビリティ機能を非アクセシビリティ目的で使用しているとの指摘でした。 – Clipboard manager rejected under Guideline 2.4.5 for using CGEvent.post | Apple Developer Forums
  8. 公証(notarization)はAppleが提供する自動マルウェアスキャンのプロセスで、macOS 10.15 Catalina以降はApp Store外で配布するアプリに必須となっています。署名と公証を通過したアプリにはAppleがチケットをステープル(添付)し、Gatekeeperが信頼できるアプリとして扱います。 – Code Signing on macOS: What Developers Need to Know | Xojo Programming Blog
  9. macOS Sequoia(15)以降はControl+クリックで起動を許可できた従来の方法が廃止され、システム設定のプライバシーとセキュリティから「このまま開く」を選ぶ手順に変わりました。xattr コマンドによる隔離属性の削除は引き続き有効です。 – Open unsigned applications on macOS Sequoia and newer | Hacks Guide Wiki