diff --git a/README.md b/README.md index 51ecaeb..e360aab 100644 --- a/README.md +++ b/README.md @@ -33,3 +33,13 @@ make help # Show available targets ├── home/ # home-manager 配下の各ツール設定 └── Makefile ``` + +## Notes + +- `home/` 配下のファイルは `mkOutOfStoreSymlink` でリポジトリ実体への symlink として配置されている。既存ファイル(`.zshrc` / `nvim/` / `claude/CLAUDE.md` など)の内容だけを編集する場合、`darwin-rebuild switch` は不要で保存すれば即反映される。 +- 以下のケースでは `make darwin-switch` が必要: + - `home/home.nix` の `home.packages` にパッケージを追加/削除 + - `home.file` / `xdg.configFile` に新しい symlink エントリを追加 + - `darwin/` 配下 (`configuration.nix` / `homebrew.nix` など) の変更 + - `flake.nix` / `flake.lock` の更新 +- `flake.lock` を更新する場合は `nix flake update` 後に `make darwin-switch`。 diff --git a/flake.lock b/flake.lock index cbac198..5635197 100644 --- a/flake.lock +++ b/flake.lock @@ -3,16 +3,16 @@ "brew-src": { "flake": false, "locked": { - "lastModified": 1776478798, - "narHash": "sha256-ERStG27tf83VbCfYMxtDSs+sa8FUMJ/3jSu/QfX9rKE=", + "lastModified": 1781226006, + "narHash": "sha256-w4ZTuOnhYiDxjaynrMTASzp802QblBWmo3wpB8wVN4Y=", "owner": "Homebrew", "repo": "brew", - "rev": "3aae056b8d072624255bc8fd27febb7f327b2265", + "rev": "109191be4988470b51a60a5ef1998520aa24c01b", "type": "github" }, "original": { "owner": "Homebrew", - "ref": "5.1.7", + "ref": "6.0.1", "repo": "brew", "type": "github" } @@ -24,11 +24,11 @@ ] }, "locked": { - "lastModified": 1780515920, - "narHash": "sha256-8KX2hEeOX6KP3hBBJJI8dGWVrzbOOf1rBPmg/GUG24U=", + "lastModified": 1783024095, + "narHash": "sha256-oE+TNK98Pl7nHW4VU1oBvUKD5JR45U+py/NJmLoTNHI=", "owner": "nix-community", "repo": "home-manager", - "rev": "4c5c1e8ba14f1c7475fa31ff11bc1c19cd220974", + "rev": "a4d410db95a6416d1008049330bd86b85b5db45a", "type": "github" }, "original": { @@ -40,11 +40,11 @@ "homebrew-cask": { "flake": false, "locked": { - "lastModified": 1778259240, - "narHash": "sha256-rBzxFpjkE8qqmlYymbKOwkbv+AxRRKdAFQRuDJp9RFw=", + "lastModified": 1783097849, + "narHash": "sha256-Juod2/yFVbhBTR7aFOzj2LDOKOyriO+CVnwQLtNGTnE=", "owner": "homebrew", "repo": "homebrew-cask", - "rev": "2a6d45ee3fbc2694a12efbb19860b9b1803f31d1", + "rev": "5033334b5f70e65638d4a41c012181965da549c8", "type": "github" }, "original": { @@ -56,11 +56,11 @@ "homebrew-core": { "flake": false, "locked": { - "lastModified": 1778259077, - "narHash": "sha256-G4PTVqzmwtF9K0mtnM7w2QEB2ta2v0UeKqf9uHeF71M=", + "lastModified": 1783094439, + "narHash": "sha256-QtoD9sj5B0mfOmxZ1HAFXrAO76GCqTJAQTcKMGXBxv0=", "owner": "homebrew", "repo": "homebrew-core", - "rev": "458fc34e680f3049f36f11478c27ac73b8b146bf", + "rev": "b420e2d9ad6c2c1c9b3bb5e2f859e4608bc0cb82", "type": "github" }, "original": { @@ -76,11 +76,11 @@ ] }, "locked": { - "lastModified": 1779036909, - "narHash": "sha256-zXcwYQGCT6pzinK+1dBB2ekTVtfxGZAapb3Evdcu4fY=", + "lastModified": 1781761792, + "narHash": "sha256-rCPytmKNjctLloB6UgK5CRrHSwV4b0ygxtJLPPp8R14=", "owner": "nix-darwin", "repo": "nix-darwin", - "rev": "56c666e108467d87d13508936aade6d567f2a501", + "rev": "a1fa429e945becaf60468600daf649be4ba0350c", "type": "github" }, "original": { @@ -94,11 +94,11 @@ "brew-src": "brew-src" }, "locked": { - "lastModified": 1777250621, - "narHash": "sha256-WynkkG0hdZ5niYPJUbVg7oMfu8MVwGGzKZ6lKmfa+O8=", + "lastModified": 1781389246, + "narHash": "sha256-ORqLAo/hoJdsZC7UPAuEHev6S0+XIqKEC7vjo5prz1k=", "owner": "zhaofengli", "repo": "nix-homebrew", - "rev": "aeb2069920742d0d6570089e8b3b8620050bacf2", + "rev": "de7953a08ed4bb9245be043e468561c17b89130d", "type": "github" }, "original": { diff --git a/home/claude/hooks/notify.applescript b/home/claude/hooks/notify.applescript new file mode 100644 index 0000000..d00527a --- /dev/null +++ b/home/claude/hooks/notify.applescript @@ -0,0 +1,19 @@ +on run + try + do shell script "test -n \"${CC_INVOCATION-}\"" + on error + return + end try + try + set b to do shell script "printf %s \"${CC_BODY-}\"" + on error + set b to "" + end try + try + set t to do shell script "printf %s \"${CC_TITLE-}\"" + if t is "" then set t to "Claude Code" + on error + set t to "Claude Code" + end try + display notification b with title t +end run diff --git a/home/claude/hooks/notify.sh b/home/claude/hooks/notify.sh new file mode 100755 index 0000000..526d0db --- /dev/null +++ b/home/claude/hooks/notify.sh @@ -0,0 +1,47 @@ +#!/usr/bin/env bash +set -euo pipefail + +payload=$(cat) +( + sleep 0.5 + IFS=$'\x1f' read -r event message cwd transcript <<< "$(jq -r '[.hook_event_name // "", .message // "", .cwd // "", .transcript_path // ""] | join("")' <<< "$payload")" + + if [[ $cwd == */*/* ]]; then + parent=${cwd%/*} + title="${parent##*/}/${cwd##*/}" + else + title=${cwd##*/} + fi + : "${title:=Claude Code}" + + case "$event" in + Stop) + body="" + if [ -f "$transcript" ]; then + body=$(tail -n 500 "$transcript" | tail -r | jq -r ' + select(.type=="assistant" and .message.role=="assistant") + | .message.content + | if type=="string" then . else (map(select(.type=="text") | .text) | join(" ")) end + | gsub("^\\s+|\\s+$"; "") + | select(length > 0) + ' 2>/dev/null | head -n 1 | tr '\n\r\t' ' ' | head -c 200 | { iconv -c -f UTF-8 -t UTF-8 2>/dev/null || true; }) + fi + body=${body:-応答完了} + ;; + Notification) body=${message:-入力待ち} ;; + "") exit 0 ;; + *) body=${message:-$event} ;; + esac + + LOG=~/.cache/claude-notify.log + [ -f "$LOG" ] && [ "$(stat -f%z "$LOG" 2>/dev/null || echo 0)" -gt 1048576 ] && rm -f "$LOG" + printf '[%s] ppid=%s event=%s title=%s\n' \ + "$(date +%Y-%m-%dT%H:%M:%S)" "$PPID" "${event:-}" "$title" \ + >> "$LOG" 2>/dev/null || true + + APP=$HOME/Applications/ClaudeCodeNotifier.app + if [ -x "$APP/Contents/MacOS/applet" ]; then + CC_INVOCATION=1 CC_BODY=$body CC_TITLE=$title "$APP/Contents/MacOS/applet" >/dev/null 2>&1 + fi +) & +disown diff --git a/home/claude/settings.json b/home/claude/settings.json index 920e788..5893250 100644 --- a/home/claude/settings.json +++ b/home/claude/settings.json @@ -92,6 +92,30 @@ "defaultMode": "plan" }, "model": "claude-opus-4-7[1m]", + "hooks": { + "Notification": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "/Users/yuuki/.claude/hooks/notify.sh" + } + ] + } + ], + "Stop": [ + { + "matcher": "", + "hooks": [ + { + "type": "command", + "command": "/Users/yuuki/.claude/hooks/notify.sh" + } + ] + } + ] + }, "statusLine": { "type": "command", "command": "~/.claude/statusline.js" @@ -171,7 +195,7 @@ "gh" ] }, - "effortLevel": "xhigh", + "effortLevel": "high", "showClearContextOnPlanAccept": true, "autoUpdatesChannel": "latest", "tui": "fullscreen", diff --git a/home/home.nix b/home/home.nix index 281c310..9035eee 100644 --- a/home/home.nix +++ b/home/home.nix @@ -1,5 +1,6 @@ { config, + lib, pkgs, username, ... @@ -67,6 +68,7 @@ in config.lib.file.mkOutOfStoreSymlink "${dotfiles}/claude/statusline.js"; ".claude/rules".source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/claude/rules"; ".claude/skills".source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/claude/skills"; + ".claude/hooks".source = config.lib.file.mkOutOfStoreSymlink "${dotfiles}/claude/hooks"; }; xdg.configFile = { @@ -100,6 +102,34 @@ in # EDITOR = "emacs"; }; + home.activation.claudeCodeNotifier = lib.hm.dag.entryAfter [ "writeBoundary" ] '' + APP="$HOME/Applications/ClaudeCodeNotifier.app" + ICON="/Applications/Claude.app/Contents/Resources/electron.icns" + SRC="${dotfiles}/claude/hooks/notify.applescript" + + if [ ! -f "$ICON" ] || [ ! -f "$SRC" ]; then + exit 0 + fi + + if [ -x "$APP/Contents/MacOS/applet" ] \ + && [ ! "$SRC" -nt "$APP/Contents/MacOS/applet" ] \ + && [ ! "$ICON" -nt "$APP/Contents/Resources/applet.icns" ]; then + exit 0 + fi + + mkdir -p "$HOME/Applications" + rm -rf "$APP" + /usr/bin/osacompile -o "$APP" "$SRC" + rm -f "$APP/Contents/Resources/Assets.car" + cp "$ICON" "$APP/Contents/Resources/applet.icns" + /usr/bin/plutil -replace CFBundleIdentifier -string "dev.yuuki.claude-code-notifier" "$APP/Contents/Info.plist" + /usr/bin/plutil -replace CFBundleName -string "Claude Code" "$APP/Contents/Info.plist" + /usr/bin/plutil -remove CFBundleIconName "$APP/Contents/Info.plist" 2>/dev/null || true + /usr/bin/codesign --sign - --force --deep "$APP" >/dev/null 2>&1 || true + /System/Library/Frameworks/CoreServices.framework/Versions/A/Frameworks/LaunchServices.framework/Versions/A/Support/lsregister -f "$APP" >/dev/null 2>&1 || true + /usr/bin/touch "$APP" + ''; + # Let Home Manager install and manage itself. programs.home-manager.enable = true; } diff --git a/home/nvim/lua/lsp/gopls.lua b/home/nvim/lua/lsp/gopls.lua index cc8d266..ada039c 100644 --- a/home/nvim/lua/lsp/gopls.lua +++ b/home/nvim/lua/lsp/gopls.lua @@ -1,7 +1,11 @@ local M = {} -M.cmd = { 'gopls' } -M.filetypes = { 'go', 'gomod'} +-- mise の go shim は GOROOT env を無視するため、直接の go binary を PATH 先頭に +-- 置いてから gopls を起動する(gopls 内部の `go env` に TinyGo overlay を反映)。 +local go_dir = vim.fs.dirname(vim.trim(vim.fn.system('mise which go'))) +M.cmd = { 'env', 'PATH=' .. go_dir .. ':' .. vim.env.PATH, 'gopls' } +M.filetypes = { 'go', 'gomod' } +M.root_markers = { 'go.mod' } M.settings = { gopls = { @@ -25,4 +29,26 @@ M.settings = { } } +-- sago35/tinygo.vim の `:TinygoTarget` を .tinygo.json 検出時に自動実行する。 +-- `:TinygoTarget` は内部で GOROOT/GOOS/GOARCH/GOFLAGS を gopls の cmd_env に書き込み、 +-- `vim.lsp.enable('gopls', false → true)` で gopls を再起動する。 +vim.pack.add({ 'https://github.com/sago35/tinygo.vim' }) + +-- キャッシュしないと FileType go の度に `:TinygoTarget` が走り、gopls が毎回再起動する。 +local applied = {} +vim.api.nvim_create_autocmd('FileType', { + pattern = 'go', + group = vim.api.nvim_create_augroup('tinygo-auto', {}), + callback = function(args) + local root = vim.fs.root(args.buf, '.tinygo.json') + if not root then return end + local ok, cfg = pcall(vim.json.decode, + table.concat(vim.fn.readfile(vim.fs.joinpath(root, '.tinygo.json')), '\n')) + if not ok or type(cfg) ~= 'table' or not cfg.target then return end + if applied[root] == cfg.target then return end + applied[root] = cfg.target + vim.cmd({ cmd = 'TinygoTarget', args = { cfg.target } }) + end, +}) + return M diff --git a/home/nvim/lua/lsp/init.lua b/home/nvim/lua/lsp/init.lua index eb468d8..25dc363 100644 --- a/home/nvim/lua/lsp/init.lua +++ b/home/nvim/lua/lsp/init.lua @@ -1,41 +1,5 @@ -- config of lsp -local safely = MiniMisc.safely - -safely('now', function() - vim.pack.add({ 'https://github.com/pcolladosoto/tinygo.nvim' }) - local tinygo = require("tinygo") - tinygo.setup({}) - - -- tinygo.nvim の applyConfigFile は .tinygo.json を相対パスで開くため、 - -- nvim の cwd がプロジェクト外だと検出漏れする。バッファから上向き探索し - -- 絶対パスで読み取って TinyGoSetTarget を直接呼ぶ。 - tinygo.applyConfigFile = function() - local bufname = vim.api.nvim_buf_get_name(0) - local search_path = nil - if bufname ~= "" then - search_path = vim.fs.dirname(bufname) - end - local found = vim.fs.find(tinygo.config_file, { - upward = true, - path = search_path, - }) - if vim.tbl_isempty(found) then return end - local f = io.open(found[1], "r") - if f == nil then return end - local raw = f:read("a") - f:close() - local ok, cfg = pcall(vim.json.decode, raw) - if not ok or type(cfg) ~= "table" or cfg.target == nil then return end - vim.cmd.TinyGoSetTarget(cfg.target) - end - - -- applyConfigFile は vim.lsp.enable(lsp_names) より後に走らせる必要がある。 - -- tinygo.setup() で登録される LspAttach autocmd が gopls attach 時に拾って - -- くれるが、取りこぼし保険として main 完了後にも明示実行する。 - vim.schedule(function() pcall(tinygo.applyConfigFile) end) -end) - vim.diagnostic.config({ virtual_text = true, }) diff --git a/home/nvim/nvim-pack-lock.json b/home/nvim/nvim-pack-lock.json index 833b00b..951d57c 100644 --- a/home/nvim/nvim-pack-lock.json +++ b/home/nvim/nvim-pack-lock.json @@ -4,9 +4,17 @@ "rev": "a04ad0dbc725134edbee3a5eea29290976695357", "src": "https://github.com/kdheepak/lazygit.nvim" }, - "tinygo.nvim": { - "rev": "0f38894d50ad5a02aae8f3df14a24576acc304f1", - "src": "https://github.com/pcolladosoto/tinygo.nvim" + "mini.nvim": { + "rev": "dc8a9918c3b1bc18a5fcd04d39b6d658074c0886", + "src": "https://github.com/echasnovski/mini.nvim" + }, + "nvim-treesitter": { + "rev": "4916d6592ede8c07973490d9322f187e07dfefac", + "src": "https://github.com/nvim-treesitter/nvim-treesitter" + }, + "tinygo.vim": { + "rev": "b95273ec07487cd59deed241b77d40e520fa08e7", + "src": "https://github.com/sago35/tinygo.vim" }, "toggleterm.nvim": { "rev": "9a88eae817ef395952e08650b3283726786fb5fb", @@ -16,6 +24,10 @@ "rev": "cdc07ac78467a233fd62c493de29a17e0cf2b2b6", "src": "https://github.com/folke/tokyonight.nvim" }, + "ts-comments.nvim": { + "rev": "a59d6092213447450191122c9346f309161504cb", + "src": "https://github.com/folke/ts-comments.nvim" + }, "vimdoc-ja": { "rev": "ee16ecb8f802287302ff0317e52e27c274c16194", "src": "https://github.com/vim-jp/vimdoc-ja"