mac-dev-switcher
WIP(現在進行中)
Work In Progress
このプロジェクトで現在進行中の作業と、過去のスナップショットを記録する。
現在の状況
進捗: v0.1 +「起動中のみ」フィルタまで実装、/Applications/ にインストール済み。./build.sh で再ビルド & 自動入れ替え。
直近のキー機能:
~/cdev/自動スキャン →.gitのあるディレクトリだけ表示- 128×128 アイコン(
.devnotes/icon.png等を優先、無ければテキスト) - クリックで Cursor を新ウィンドウで起動
- 最終クリック日時の降順で並び替え(
UserDefaults永続化) - 「起動中のみ」トグル: Cursor で開いてるプロジェクトだけ表示
⌘Rで再スキャン
開発環境(プロジェクト外):
- Claude Code の Stop / Notification フックが
~/.claude/notify-if-bg.sh経由で動作 - 通知クリックで
raise-cursor-window.shが当該 Cursor ウィンドウを最前面化(terminal-notifier 経由)
次にやること:
- 「起動中のみ」トグルの実機確認、アクセシビリティ権限の初回挙動チェック
- 実機での使用感チェック(アイコンサイズ・グリッド余白)
未決事項:
- メニューバー常駐モードの是非
- 検索ボックスの必要性(プロジェクト数が増えてきたら)
過去のWIPアーカイブ
(新しい「現在の状況」を書く前に、古いものをここに追記でアーカイブする。新しいものが上)
2026-05-21 15:00 時点のスナップショット
進捗: v0.1 が動く状態で /Applications/ にインストール済み。./build.sh で再ビルド & 自動入れ替えできる。コード本体には今回変更なし。
直近のキー機能:
~/cdev/自動スキャン →.gitのあるディレクトリだけ表示- 128×128 アイコン(
.devnotes/icon.png等を優先、無ければテキスト) - クリックで Cursor を新ウィンドウで起動
- 最終クリック日時の降順で並び替え(
UserDefaults永続化) ⌘Rで再スキャン
開発環境(プロジェクト外):
- Claude Code の Stop / Notification フックが
~/.claude/notify-if-bg.sh経由で動作 - 当該プロジェクトの Cursor ウィンドウがフロントなら通知抑制、それ以外なら本文付きで通知
- ミラーリング時の通知抑制は OS 設定でオフにする必要あり
次にやること:
- 実機での使用感チェック(アイコンサイズ・グリッド余白・通知挙動)
- 気になる箇所があれば改善着手(候補は ROADMAP.md)
未決事項:
- メニューバー常駐モードの是非
- 検索ボックスの必要性(プロジェクト数が増えてきたら)
2026-04-27 15:39 時点のスナップショット
進捗: v0.1 が動く状態で /Applications/ にインストール済み。./build.sh で再ビルド & 自動入れ替えできる。
直近のキー機能:
~/cdev/自動スキャン →.gitのあるディレクトリだけ表示- 128×128 アイコン(
.devnotes/icon.png等を優先、無ければテキスト) - クリックで Cursor を新ウィンドウで起動
- 最終クリック日時の降順で並び替え(
UserDefaults永続化) ⌘Rで再スキャン
次にやること:
- 実機での使用感チェック(アイコンサイズ・グリッド余白・通知挙動)
- 気になる箇所があれば改善着手(候補は ROADMAP.md)
未決事項:
- メニューバー常駐モードの是非
- 検索ボックスの必要性(プロジェクト数が増えてきたら)
ROADMAP(計画)
ロードマップ
今週
- v0.1 初期実装(スキャン / アイコングリッド / Cursor 起動 /
/Applications/自動配置) - 並び順を最終クリック降順に変更
- 「起動中のみ」フィルタトグル追加
- 実機で使用感を確認しフィードバック反映
今月
- 各プロジェクトに
.devnotes/icon.pngを追加していく(手作業)
今四半期
(今四半期の目標)
いつか
- メニューバー常駐モード(Dock 非表示で常駐)
- プロジェクト検索ボックス(プロジェクト数が増えたら)
- アイコンサイズの可変化(96/128/160 切替)
- 右クリックメニュー(Finder で開く / パスをコピー / 履歴削除 など)
- 他 IDE 対応(VS Code / Xcode 切替)
- プロジェクトのピン留め機能
DECISIONS(意思決定)
意思決定記録
このプロジェクトで下した重要な意思決定を記録する。 最新が上に来る。
2026-05-21: 「起動中」判定は Cursor ウィンドウタイトルの — 区切り照合
背景: 「起動中のみ」トグルのため、各プロジェクトが Cursor で開かれているか判定する必要があった。Cursor の全ウィンドウタイトルは取得できるが、素朴な部分一致だと別プロジェクトで開いているファイル名にプロジェクト名と同じ文字列が含まれると誤判定する。
決定: ウィンドウタイトルを —(前後スペース付き em dash)で分割し、その要素のいずれかがプロジェクト名と完全一致したら「起動中」とみなす。
理由: Cursor のウィンドウタイトルは「ファイル名 — プロジェクト名」形式で、プロジェクト名は独立した要素として現れる。区切り照合なら部分一致の誤判定を避けられる。ファイル未オープン時はタイトルがプロジェクト名のみになるが、分割結果1要素として同じロジックで拾える。
2026-05-21: デプロイメントターゲットを macOS 13 → 14 に引き上げ
背景: 「起動中のみ」トグルの状態変化を検知するのに onChange を使いたかったが、macOS 13 で使える単一引数版は 14 以降では deprecated。
決定: ビルド対象を macOS 14 に引き上げ、2引数版 onChange(of:_:) などモダンな API を素直に使う。
理由: 利用者環境は macOS 26 で 13 を対象に残す実利用上の意味がない。deprecated API による警告を抱えるより、最低要件を上げてクリーンな API を使う方が保守的に健全。
2026-04-27: ビルド方式は swiftc + .app バンドル化(Xcode プロジェクトを使わない)
背景: SwiftUI の Mac アプリを作るにあたって、Xcode プロジェクト / SwiftPM / 直コンパイル のどれで管理するか選ぶ必要があった。
決定: swiftc で直接コンパイルし、Info.plist を含む .app バンドルをシェルスクリプトで組み立てる方式を採用。
理由: project.pbxproj を手で書く負担を避けつつ、ビルド時間が短く、CLI のみで完結する。Xcode を開かずに iterate できる。SwiftPM は .app バンドルを直接吐けないため不採用。将来 Xcode を使いたくなったら Sources/ をそのまま import すれば移行可能。
2026-04-27: Cursor 起動は同梱 CLI を優先、open はフォールバック
背景: クリック時に「新しいウィンドウで開く」挙動を確実にしたかった。open -na Cursor は新インスタンスを立ち上げてしまい、open -a Cursor は Cursor 側設定次第で既存ウィンドウに入る可能性がある。
決定: /Applications/Cursor.app/Contents/Resources/app/bin/cursor -n <path> を第一優先で叩き、見つからない場合のみ /usr/local/bin/cursor → /opt/homebrew/bin/cursor → open -a Cursor <path> の順でフォールバック。
理由: 同梱 CLI は Cursor 自身が提供しているため確実に存在する。-n フラグで新ウィンドウが保証される。ユーザー側が cursor コマンドを PATH に通していなくても動く。
2026-04-27: アイコンファイルの規約は .devnotes/icon.{png,jpg,jpeg,icns}
背景: プロジェクト固有のアイコンをどこに置くかを決める必要があった。プロジェクトルート直下に置くと公開時に「公式ロゴ」と誤解されやすい。
決定: <project>/.devnotes/icon.png(または .jpg / .jpeg / .icns)を順に探索し、最初に見つかったものを使用。無ければイニシャル + ハッシュベースの自動カラーでテキストアイコンを生成。
理由: ユーザーレベル CLAUDE.md の .devnotes/ 規約に揃えることで、開発メモ系のファイルが同じディレクトリにまとまる。OSS 公開時に .devnotes/ ごと .gitignore できる構造とも整合。
2026-04-27: 並び順は最終クリック日時の降順(永続化は UserDefaults)
背景: 当初はアルファベット順だったが、よく使うプロジェクトを毎回探すのが手間。
決定: LaunchHistory に最終クリック日時を [String: Date] で UserDefaults 保存。表示時は (a) 最近クリックされたもの順 → (b) 一度もクリックされていないものは末尾でアルファベット順、にソート。
理由: 最近触ったプロジェクトが常に左上に来るのが直感的。UserDefaults は構造が単純で十分、専用ファイル管理は過剰。
2026-04-27: build.sh で /Applications/ への自動インストールを既定動作にする
背景: 改良のたびに手動で cp していると面倒。
決定: ./build.sh 一発で「ビルド → 起動中インスタンス終了 → /Applications/Mac Dev Switcher.app 上書き → quarantine 属性除去」まで完了する。オプション化せず常に実行。
理由: 個人利用で他用途が無いため、デフォルトで配置する方がシンプル。Gatekeeper 警告も xattr -dr で抑制できるので毎回手で許可する必要がない。
DEVLOG(作業ログ)
開発日誌
このプロジェクトでの作業を時系列で記録する。 最新のエントリが上に来る。
2026-05-21
15:00 - 「起動中のみ」フィルタトグル追加 + 通知クリックで Cursor 窓を前面化
やったこと:
~/cdev/に増えたプロジェクト(dev-timer)をアプリ再起動で一覧に反映- 通知クリックで Cursor を前面化する機能を追加(プロジェクト外・
~/.claude/配下)terminal-notifierを導入し、-executeでraise-cursor-window.shを呼ぶ構成にraise-cursor-window.shが当該プロジェクトの Cursor ウィンドウをAXRaiseで最前面化
- アプリに「起動中のみ」トグルを追加
- Sources/CursorWindows.swift 新規: Cursor の全ウィンドウタイトルを
osascriptで取得 - タイトルを
—で分割し、プロジェクト名と一致する要素があれば「起動中」と判定 - ContentView.swift にトグル(
.toggleStyle(.switch)= iOS スイッチ風)を配置、ON で起動中プロジェクトのみ表示 - 判定更新タイミング: トグルON時 /
⌘R/ アプリ再アクティブ時
- Sources/CursorWindows.swift 新規: Cursor の全ウィンドウタイトルを
- デプロイメントターゲットを macOS 13 → 14 に引き上げ
決めたこと:
- 起動中プロジェクトの判定方式、デプロイメントターゲット引き上げ(DECISIONS.md 参照)
気づき:
- Cursor のウィンドウタイトルは「ファイル名 — プロジェクト名」形式。素朴な部分一致だと別プロジェクトのファイル名に同名文字列が含まれると誤判定するため、
—区切りで要素照合する方が堅い - ウィンドウタイトル列挙にはアクセシビリティ権限が必要。osascript を呼ぶアプリ本体(Mac Dev Switcher)に対して許可が要る
次回やること:
- 「起動中のみ」トグルの実機確認、アクセシビリティ権限の初回挙動チェック
2026-04-27
16:18 - Claude Code 通知の改善(ミラーリング問題対処 + 入力待ち通知 + 文字化け修正)
やったこと:
- ミラーリング中にバナー通知が出ない問題を解決
- macOS のプライバシー機能「ミラーリング/画面共有中は通知を出さない」が原因
- システム設定 → 通知 のディスプレイ関連トグルをオンにすることで解決(Script Editor 側の設定は元から正しかった)
Notificationフックを追加し、許可待ち / 入力待ち時にも通知が出るようにした- 通知本文に Claude Code 側の
messageフィールドを含めるよう拡張python3で JSON を安全にパース(grep + sedの素朴方式から切替)- 改行はスペース化、120 字でカット
- 文字化け問題を修正
system attribute経由で env var を AppleScript に渡すと UTF-8 が壊れることを発見osascript -eにインライン渡し(\と"だけエスケープ)に切替
気づき:
- macOS には「ミラーリング/画面共有中は通知を出さない」プライバシー機能があり、これがオンだとどんな設定をいじっても無音
- AppleScript の
system attributeは ASCII 範囲を超える文字でエンコーディングが壊れる。-e直接渡しの方が安全 - Claude Code のフックは stdin に JSON を流す。
hook_event_nameでイベント種別を分岐できる Notificationイベントは「許可待ち」と「アイドル60秒」両方を拾う
次回やること:
- mac-dev-switcher 本体の使用感確認とフィードバック反映(前回からの引き継ぎ)
15:39 - mac-dev-switcher v0.1 初期実装 + Claude Code 通知フック
やったこと:
- SwiftUI ベースの Mac アプリ初期実装
~/cdev/配下を自動スキャンして.gitを持つディレクトリだけ拾う (Sources/ProjectScanner.swift)- 128×128 のアイコンを
LazyVGridで表示 (Sources/ProjectIcon.swift) - アイコン画像が無い場合はプロジェクト名のイニシャル + 名前ハッシュから決定論的にカラーを生成
- クリックで Cursor を新ウィンドウで起動 (Sources/CursorLauncher.swift)
- Xcode プロジェクトを使わず、
swiftc直コンパイル + Info.plist で.appバンドル化するbuild.shを整備 build.sh実行で/Applications/まで自動配置するよう拡張(起動中インスタンスの自動終了 + quarantine 属性除去まで)- 並び順を「最後にクリックした日時の降順 → 未クリックは末尾アルファベット順」に変更(Sources/LaunchHistory.swift、
UserDefaultsで永続化) - Claude Code の Stop フックで応答完了時に macOS 通知を出すスクリプト
~/.claude/notify-if-bg.shを作成- フロントが Cursor かつ「現プロジェクト名がウィンドウタイトルに含まれる」場合のみ通知を抑制
- 別 Cursor ウィンドウや他アプリにフォーカス移動しているときは通知が出る
~/.claude/settings.jsonのhooks.Stopに登録
決めたこと:
- ビルド方式・Cursor 起動方式・アイコン規約・並び順・自動インストール(DECISIONS.md 参照)
気づき:
- Cursor は
/Applications/Cursor.app/Contents/Resources/app/bin/cursorに CLI が同梱されている。-nフラグで確実に新ウィンドウが開く - macOS から見ると複数の Cursor ウィンドウは「同じ Cursor.app」なので、
frontmost is trueだけでは区別不可。process "Cursor" の front window のタイトルまで見て CWD basename で照合する必要がある - Stop フックは stdin で JSON を受け取る(
cwdフィールドにプロジェクトパスが入る)
次回やること:
- 実機で各機能の使い心地を確認(アイコンサイズ、グリッド間隔、通知の出方)
- 必要に応じて ROADMAP.md の項目に着手
最近のコミット
README
mac-dev-switcher
~/cdev/ 配下の開発プロジェクトをアイコン一覧から Cursor で開く Mac アプリ。
概要
- 起動すると
~/cdev/配下の.gitを持つディレクトリを自動スキャン - 各プロジェクトを 128×128 のアイコンとしてグリッド表示
- アイコンをクリックすると Cursor.app が新しいウィンドウでそのプロジェクトを開く
⌘Rまたは右上の更新ボタンで再スキャン
アイコンの設定
各プロジェクトの以下のいずれかに画像を置くと、それがアイコンとして使われる:
<project>/.devnotes/icon.png
<project>/.devnotes/icon.jpg
<project>/.devnotes/icon.jpeg
<project>/.devnotes/icon.icns
ファイルが無い場合は、プロジェクト名から生成したテキストアイコン(イニシャル + 自動カラー)が表示される。
ビルド & インストール
./build.sh
これだけで以下まで自動で行われる:
build/Mac Dev Switcher.appを生成- 起動中のインスタンスがあれば終了
/Applications/Mac Dev Switcher.appに上書きコピー- quarantine 属性を除去
ad-hoc 署名のみなので、初回起動時に Gatekeeper の警告が出たら「システム設定 → プライバシーとセキュリティ」から許可する。
動作要件
- macOS 13 以降
- Apple Silicon または Intel
- Cursor.app が
/Applications/Cursor.appにインストール済み
構成
| パス | 役割 |
|---|---|
Sources/MacDevSwitcherApp.swift |
アプリエントリ |
Sources/ContentView.swift |
グリッド画面 |
Sources/Project.swift |
プロジェクトモデル |
Sources/ProjectScanner.swift |
~/cdev/ のスキャン |
Sources/ProjectIcon.swift |
アイコン描画(画像 / テキスト) |
Sources/CursorLauncher.swift |
Cursor 起動 |
Resources/Info.plist |
バンドル設定 |
build.sh |
swiftc コンパイル + .app 生成 |