From 9d9abf1fb4413386e496fa3866581c480b5dc968 Mon Sep 17 00:00:00 2001 From: rin2yh Date: Wed, 1 Jul 2026 22:56:50 +0900 Subject: [PATCH 01/17] feat(claude): notify macOS Notification Center on Stop/Notification hooks Add a Claude Code hook that surfaces assistant responses in Notification Center with the Claude for Desktop icon. Home-manager activation builds a minimal AppleScript .app (icon swapped from Claude.app, ad-hoc signed) so notifications appear from an app the notification system recognizes; a CC_INVOCATION sentinel guards against macOS spuriously re-launching the sender bundle with empty env. --- home/claude/hooks/notify.applescript | 15 ++++++++++ home/claude/hooks/notify.sh | 41 ++++++++++++++++++++++++++++ home/claude/settings.json | 20 +++++++++++++- home/home.nix | 30 ++++++++++++++++++++ 4 files changed, 105 insertions(+), 1 deletion(-) create mode 100644 home/claude/hooks/notify.applescript create mode 100755 home/claude/hooks/notify.sh diff --git a/home/claude/hooks/notify.applescript b/home/claude/hooks/notify.applescript new file mode 100644 index 0000000..6f7fdc1 --- /dev/null +++ b/home/claude/hooks/notify.applescript @@ -0,0 +1,15 @@ +on run + if (system attribute "CC_INVOCATION") is "" then return + 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..6a073bc --- /dev/null +++ b/home/claude/hooks/notify.sh @@ -0,0 +1,41 @@ +#!/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) + fi + body=${body:-応答完了} + ;; + Notification) body=${message:-入力待ち} ;; + "") exit 0 ;; + *) body=${message:-$event} ;; + esac + + 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..9dc50f7 100644 --- a/home/claude/settings.json +++ b/home/claude/settings.json @@ -180,5 +180,23 @@ "inputNeededNotifEnabled": false, "agentPushNotifEnabled": true, "skipAutoPermissionPrompt": true, - "voiceEnabled": false + "voiceEnabled": false, + "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" } + ] + } + ] + } } 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; } From cfbb8c4b38596eaaf250a9f98302bc6e60418df3 Mon Sep 17 00:00:00 2001 From: rin2yh Date: Wed, 1 Jul 2026 23:06:35 +0900 Subject: [PATCH 02/17] fix(claude): route CC_INVOCATION check through shell + rotate notify log Reading CC_INVOCATION via `system attribute` was flipping AppleScript into MacRoman text mode, mojibake-ing UTF-8 bodies read via later `do shell script` calls. Route the sentinel through `do shell script` too so the whole applet stays in one encoding. Log each hook fire to ~/.cache/claude-notify.log and drop the file when it crosses 1 MiB. --- home/claude/hooks/notify.applescript | 6 +++++- home/claude/hooks/notify.sh | 6 ++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/home/claude/hooks/notify.applescript b/home/claude/hooks/notify.applescript index 6f7fdc1..d00527a 100644 --- a/home/claude/hooks/notify.applescript +++ b/home/claude/hooks/notify.applescript @@ -1,5 +1,9 @@ on run - if (system attribute "CC_INVOCATION") is "" then return + 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 diff --git a/home/claude/hooks/notify.sh b/home/claude/hooks/notify.sh index 6a073bc..f764b6b 100755 --- a/home/claude/hooks/notify.sh +++ b/home/claude/hooks/notify.sh @@ -33,6 +33,12 @@ payload=$(cat) *) 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 From 3f2546ab1d95cb47f0a080414c6477d6c649a35a Mon Sep 17 00:00:00 2001 From: rin2yh Date: Thu, 2 Jul 2026 00:30:12 +0900 Subject: [PATCH 03/17] fix(claude): strip broken UTF-8 tail from notify body head -c 200 could split a multi-byte char, leaving invalid bytes that made AppleScript's do shell script fall back to MacRoman and mangle the entire notification body. Pipe through iconv -c to drop the incomplete tail; tolerate its non-zero exit under set -e. --- home/claude/hooks/notify.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/home/claude/hooks/notify.sh b/home/claude/hooks/notify.sh index f764b6b..526d0db 100755 --- a/home/claude/hooks/notify.sh +++ b/home/claude/hooks/notify.sh @@ -24,7 +24,7 @@ payload=$(cat) | 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) + ' 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:-応答完了} ;; From fa73925dfdfe2604a502a9497086b014d261d0f0 Mon Sep 17 00:00:00 2001 From: rin2yh Date: Thu, 2 Jul 2026 00:30:57 +0900 Subject: [PATCH 04/17] chore(nvim): pin mini.nvim, nvim-treesitter, ts-comments.nvim --- home/nvim/nvim-pack-lock.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/home/nvim/nvim-pack-lock.json b/home/nvim/nvim-pack-lock.json index 833b00b..e98ddd4 100644 --- a/home/nvim/nvim-pack-lock.json +++ b/home/nvim/nvim-pack-lock.json @@ -4,6 +4,14 @@ "rev": "a04ad0dbc725134edbee3a5eea29290976695357", "src": "https://github.com/kdheepak/lazygit.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.nvim": { "rev": "0f38894d50ad5a02aae8f3df14a24576acc304f1", "src": "https://github.com/pcolladosoto/tinygo.nvim" @@ -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" From eee7b8ca0694f48aad9c8ee29a003c9293994979 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 17:04:02 +0000 Subject: [PATCH 05/17] chore(deps): bump nix-darwin from `56c666e` to `a1fa429` Bumps [nix-darwin](https://github.com/nix-darwin/nix-darwin) from `56c666e` to `a1fa429`. - [Commits](https://github.com/nix-darwin/nix-darwin/compare/56c666e108467d87d13508936aade6d567f2a501...a1fa429e945becaf60468600daf649be4ba0350c) --- updated-dependencies: - dependency-name: nix-darwin dependency-version: a1fa429e945becaf60468600daf649be4ba0350c dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index cbac198..7d788aa 100644 --- a/flake.lock +++ b/flake.lock @@ -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": { From fcd810665ee9f6661f4ee8bd185f75e135d62d55 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 17:04:07 +0000 Subject: [PATCH 06/17] chore(deps): bump homebrew-cask from `2a6d45e` to `5033334` Bumps [homebrew-cask](https://github.com/homebrew/homebrew-cask) from `2a6d45e` to `5033334`. - [Release notes](https://github.com/homebrew/homebrew-cask/releases) - [Commits](https://github.com/homebrew/homebrew-cask/compare/2a6d45ee3fbc2694a12efbb19860b9b1803f31d1...5033334b5f70e65638d4a41c012181965da549c8) --- updated-dependencies: - dependency-name: homebrew-cask dependency-version: 5033334b5f70e65638d4a41c012181965da549c8 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index cbac198..fde26cd 100644 --- a/flake.lock +++ b/flake.lock @@ -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": { From b2891a0be10dbbb878c4c6d59c376da8b7ba3ba9 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 17:04:11 +0000 Subject: [PATCH 07/17] chore(deps): bump home-manager from `4c5c1e8` to `a4d410d` Bumps [home-manager](https://github.com/nix-community/home-manager) from `4c5c1e8` to `a4d410d`. - [Commits](https://github.com/nix-community/home-manager/compare/4c5c1e8ba14f1c7475fa31ff11bc1c19cd220974...a4d410db95a6416d1008049330bd86b85b5db45a) --- updated-dependencies: - dependency-name: home-manager dependency-version: a4d410db95a6416d1008049330bd86b85b5db45a dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index cbac198..03e3b91 100644 --- a/flake.lock +++ b/flake.lock @@ -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": { From d23d7e169dc3556f7b0b454f0a86ec6b1bfad2d3 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 17:04:14 +0000 Subject: [PATCH 08/17] chore(deps): bump nix-homebrew from `aeb2069` to `de7953a` Bumps [nix-homebrew](https://github.com/zhaofengli/nix-homebrew) from `aeb2069` to `de7953a`. - [Commits](https://github.com/zhaofengli/nix-homebrew/compare/aeb2069920742d0d6570089e8b3b8620050bacf2...de7953a08ed4bb9245be043e468561c17b89130d) --- updated-dependencies: - dependency-name: nix-homebrew dependency-version: de7953a08ed4bb9245be043e468561c17b89130d dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- flake.lock | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/flake.lock b/flake.lock index cbac198..c52d2d6 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" } @@ -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": { From 55a98f3477bb520e6940358ab72079d372d57a43 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 3 Jul 2026 17:04:20 +0000 Subject: [PATCH 09/17] chore(deps): bump homebrew-core from `458fc34` to `b420e2d` Bumps [homebrew-core](https://github.com/homebrew/homebrew-core) from `458fc34` to `b420e2d`. - [Commits](https://github.com/homebrew/homebrew-core/compare/458fc34e680f3049f36f11478c27ac73b8b146bf...b420e2d9ad6c2c1c9b3bb5e2f859e4608bc0cb82) --- updated-dependencies: - dependency-name: homebrew-core dependency-version: b420e2d9ad6c2c1c9b3bb5e2f859e4608bc0cb82 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- flake.lock | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/flake.lock b/flake.lock index cbac198..143bff9 100644 --- a/flake.lock +++ b/flake.lock @@ -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": { From 5153947f7c7e5e7b0078479d9cd37aa63e299677 Mon Sep 17 00:00:00 2001 From: rin2yh Date: Sun, 5 Jul 2026 19:47:30 +0900 Subject: [PATCH 10/17] fix(nvim/gopls): resolve TinyGo machine import via mise shim bypass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wrap gopls cmd with `env PATH=:...` so gopls internal `go env` sees the TinyGo overlay GOROOT — the mise `go` shim silently overrides GOROOT env, which was masking `machine` package resolution. Also add `go.mod` to root_markers so tinygobook (no .git) is picked up as workspace root. For target switching, swap pcolladosoto/tinygo.nvim (monkey-patched to stop attached gopls) with sago35 さんの tinygo.vim (full GOROOT/GOOS/GOARCH/GOFLAGS support, actual client:stop on switch). Provide `:Tinygo ` wrapper that re-fires FileType after TinygoTarget so gopls re-attaches, and auto-detect `.tinygo.json` on FileType go to invoke it. --- home/nvim/lua/lsp/gopls.lua | 45 ++++++++++++++++++++++++++++++++++- home/nvim/lua/lsp/init.lua | 36 ---------------------------- home/nvim/nvim-pack-lock.json | 4 ++++ 3 files changed, 48 insertions(+), 37 deletions(-) diff --git a/home/nvim/lua/lsp/gopls.lua b/home/nvim/lua/lsp/gopls.lua index cc8d266..31bc1ab 100644 --- a/home/nvim/lua/lsp/gopls.lua +++ b/home/nvim/lua/lsp/gopls.lua @@ -1,7 +1,11 @@ local M = {} -M.cmd = { 'gopls' } +-- 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,43 @@ M.settings = { } } +-- TinyGo 用の target 切替。sago35 さんの tinygo.vim が `:TinygoTarget ` +-- で GOROOT/GOOS/GOARCH/GOFLAGS を LSP config に流し込むが、Neovim native LSP +-- 分岐で edit を叩かないため、FileType 再発火で gopls を再 attach させる +-- ラッパー `:Tinygo` を用意する。 +vim.pack.add({ 'https://github.com/sago35/tinygo.vim' }) + +vim.api.nvim_create_user_command('Tinygo', function(opts) + vim.cmd.TinygoTarget(opts.args) + local buf = vim.api.nvim_get_current_buf() + vim.schedule(function() + vim.api.nvim_exec_autocmds('FileType', { buffer = buf }) + end) +end, { + nargs = 1, + complete = function(arglead) return vim.fn['tinygo#TinygoTargets'](arglead, '', 0) end, +}) + +-- Go バッファを開いた時、上位に .tinygo.json があれば自動で :Tinygo を叩く。 +-- 同一ファイル + 同一 target は 1 セッション 1 回だけ。 +local tinygo_applied = {} +vim.api.nvim_create_autocmd('FileType', { + pattern = 'go', + group = vim.api.nvim_create_augroup('tinygo-auto', {}), + callback = function(args) + local search = vim.fs.dirname(vim.api.nvim_buf_get_name(args.buf)) + local found = vim.fs.find({ '.tinygo.json' }, { upward = true, path = search }) + if vim.tbl_isempty(found) then return end + local f = io.open(found[1], 'r') + if not f 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 + if tinygo_applied[found[1]] == cfg.target then return end + tinygo_applied[found[1]] = cfg.target + vim.cmd({ cmd = 'Tinygo', 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 e98ddd4..8929c2e 100644 --- a/home/nvim/nvim-pack-lock.json +++ b/home/nvim/nvim-pack-lock.json @@ -16,6 +16,10 @@ "rev": "0f38894d50ad5a02aae8f3df14a24576acc304f1", "src": "https://github.com/pcolladosoto/tinygo.nvim" }, + "tinygo.vim": { + "rev": "b95273ec07487cd59deed241b77d40e520fa08e7", + "src": "https://github.com/sago35/tinygo.vim" + }, "toggleterm.nvim": { "rev": "9a88eae817ef395952e08650b3283726786fb5fb", "src": "https://github.com/akinsho/toggleterm.nvim" From 5d1ac7de31787c7c98d4c66370344b4f9bd8cbc7 Mon Sep 17 00:00:00 2001 From: rin2yh Date: Sun, 5 Jul 2026 19:53:59 +0900 Subject: [PATCH 11/17] chore(claude): lower default effort level to high hooks block is reformatted by Claude Code on save (multi-line vs single-line) but content is unchanged. --- home/claude/settings.json | 46 ++++++++++++++++++++++----------------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/home/claude/settings.json b/home/claude/settings.json index 9dc50f7..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", @@ -180,23 +204,5 @@ "inputNeededNotifEnabled": false, "agentPushNotifEnabled": true, "skipAutoPermissionPrompt": true, - "voiceEnabled": false, - "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" } - ] - } - ] - } + "voiceEnabled": false } From e8303b884c6a76f7ed19456c02916db4bfc0199f Mon Sep 17 00:00:00 2001 From: rin2yh Date: Mon, 6 Jul 2026 17:38:14 +0900 Subject: [PATCH 12/17] refactor(nvim/gopls): drop :Tinygo wrapper; call :TinygoTarget directly MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit sago35/tinygo.vim's `:TinygoTarget` already invokes vim.lsp.enable('gopls', false) -> sleep 100m -> vim.lsp.enable('gopls', true) on native LSP. From Neovim 0.11.2 onward, enable(false) calls client:stop() and enable(true) fires doautoall('nvim.lsp.enable FileType'), so gopls restarts and re-attaches to open buffers on its own — the extra FileType re-fire in the `:Tinygo` wrapper is redundant. Verified headless on the tinygobook (wioterminal target) with Neovim 0.12.3: gopls attaches with the TinyGo overlay GOROOT + GOOS/GOARCH/GOFLAGS, and `machine` resolves without the wrapper. Claude-Session: https://claude.ai/code/session_01MpL4ZtCkUDDxdAGqEHUMUP --- home/nvim/lua/lsp/gopls.lua | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/home/nvim/lua/lsp/gopls.lua b/home/nvim/lua/lsp/gopls.lua index 31bc1ab..9d6f25c 100644 --- a/home/nvim/lua/lsp/gopls.lua +++ b/home/nvim/lua/lsp/gopls.lua @@ -29,24 +29,13 @@ M.settings = { } } --- TinyGo 用の target 切替。sago35 さんの tinygo.vim が `:TinygoTarget ` --- で GOROOT/GOOS/GOARCH/GOFLAGS を LSP config に流し込むが、Neovim native LSP --- 分岐で edit を叩かないため、FileType 再発火で gopls を再 attach させる --- ラッパー `:Tinygo` を用意する。 +-- sago35/tinygo.vim の `:TinygoTarget` が GOROOT/GOOS/GOARCH/GOFLAGS を LSP config +-- に流し込む。Neovim 0.11.2 以降は `vim.lsp.enable('gopls', true)` が +-- `doautoall('nvim.lsp.enable FileType')` を発火して gopls を再 attach するため、 +-- 追加のラッパーは不要。 vim.pack.add({ 'https://github.com/sago35/tinygo.vim' }) -vim.api.nvim_create_user_command('Tinygo', function(opts) - vim.cmd.TinygoTarget(opts.args) - local buf = vim.api.nvim_get_current_buf() - vim.schedule(function() - vim.api.nvim_exec_autocmds('FileType', { buffer = buf }) - end) -end, { - nargs = 1, - complete = function(arglead) return vim.fn['tinygo#TinygoTargets'](arglead, '', 0) end, -}) - --- Go バッファを開いた時、上位に .tinygo.json があれば自動で :Tinygo を叩く。 +-- Go バッファを開いた時、上位に .tinygo.json があれば自動で :TinygoTarget を叩く。 -- 同一ファイル + 同一 target は 1 セッション 1 回だけ。 local tinygo_applied = {} vim.api.nvim_create_autocmd('FileType', { @@ -64,7 +53,7 @@ vim.api.nvim_create_autocmd('FileType', { if not ok or type(cfg) ~= 'table' or cfg.target == nil then return end if tinygo_applied[found[1]] == cfg.target then return end tinygo_applied[found[1]] = cfg.target - vim.cmd({ cmd = 'Tinygo', args = { cfg.target } }) + vim.cmd({ cmd = 'TinygoTarget', args = { cfg.target } }) end, }) From 9a402a880ad037857f9de6f9bf1da379d4f56260 Mon Sep 17 00:00:00 2001 From: rin2yh Date: Mon, 6 Jul 2026 17:44:29 +0900 Subject: [PATCH 13/17] refactor(nvim): split TinyGo autocmd into plugin/tinygo.lua MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to dropping the :Tinygo wrapper. The TinyGo autocmd is not gopls configuration — home/nvim/lua/lsp/ is a registry that lua/lsp/init.lua feeds to vim.lsp.config, so TinyGo tooling being there muddled the boundary. - Move vim.pack.add + FileType autocmd + apply cache to home/nvim/plugin/tinygo.lua (mirrors home/nvim/plugin/oplog.lua) - Use vim.fs.root(bufnr, marker) instead of vim.fs.dirname + vim.fs.find pair (matches home/nvim/lua/lsp/rust_ls.lua idiom, correctly handles unnamed buffers) - Read .tinygo.json with vim.fn.readfile instead of manual io.open dance - Drop tinygo.nvim from nvim-pack-lock.json (removed after prior commit) - Fix cosmetic missing space in M.filetypes Verified headless on tinygobook (wioterminal): gopls attaches with GOROOT/GOOS/GOARCH/GOFLAGS overlay, `machine` resolves. Claude-Session: https://claude.ai/code/session_01MpL4ZtCkUDDxdAGqEHUMUP --- home/nvim/lua/lsp/gopls.lua | 30 +----------------------------- home/nvim/nvim-pack-lock.json | 4 ---- home/nvim/plugin/tinygo.lua | 23 +++++++++++++++++++++++ 3 files changed, 24 insertions(+), 33 deletions(-) create mode 100644 home/nvim/plugin/tinygo.lua diff --git a/home/nvim/lua/lsp/gopls.lua b/home/nvim/lua/lsp/gopls.lua index 9d6f25c..2037be2 100644 --- a/home/nvim/lua/lsp/gopls.lua +++ b/home/nvim/lua/lsp/gopls.lua @@ -4,7 +4,7 @@ local M = {} -- 置いてから 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.filetypes = { 'go', 'gomod' } M.root_markers = { 'go.mod' } M.settings = { @@ -29,32 +29,4 @@ M.settings = { } } --- sago35/tinygo.vim の `:TinygoTarget` が GOROOT/GOOS/GOARCH/GOFLAGS を LSP config --- に流し込む。Neovim 0.11.2 以降は `vim.lsp.enable('gopls', true)` が --- `doautoall('nvim.lsp.enable FileType')` を発火して gopls を再 attach するため、 --- 追加のラッパーは不要。 -vim.pack.add({ 'https://github.com/sago35/tinygo.vim' }) - --- Go バッファを開いた時、上位に .tinygo.json があれば自動で :TinygoTarget を叩く。 --- 同一ファイル + 同一 target は 1 セッション 1 回だけ。 -local tinygo_applied = {} -vim.api.nvim_create_autocmd('FileType', { - pattern = 'go', - group = vim.api.nvim_create_augroup('tinygo-auto', {}), - callback = function(args) - local search = vim.fs.dirname(vim.api.nvim_buf_get_name(args.buf)) - local found = vim.fs.find({ '.tinygo.json' }, { upward = true, path = search }) - if vim.tbl_isempty(found) then return end - local f = io.open(found[1], 'r') - if not f 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 - if tinygo_applied[found[1]] == cfg.target then return end - tinygo_applied[found[1]] = cfg.target - vim.cmd({ cmd = 'TinygoTarget', args = { cfg.target } }) - end, -}) - return M diff --git a/home/nvim/nvim-pack-lock.json b/home/nvim/nvim-pack-lock.json index 8929c2e..951d57c 100644 --- a/home/nvim/nvim-pack-lock.json +++ b/home/nvim/nvim-pack-lock.json @@ -12,10 +12,6 @@ "rev": "4916d6592ede8c07973490d9322f187e07dfefac", "src": "https://github.com/nvim-treesitter/nvim-treesitter" }, - "tinygo.nvim": { - "rev": "0f38894d50ad5a02aae8f3df14a24576acc304f1", - "src": "https://github.com/pcolladosoto/tinygo.nvim" - }, "tinygo.vim": { "rev": "b95273ec07487cd59deed241b77d40e520fa08e7", "src": "https://github.com/sago35/tinygo.vim" diff --git a/home/nvim/plugin/tinygo.lua b/home/nvim/plugin/tinygo.lua new file mode 100644 index 0000000..3e8b117 --- /dev/null +++ b/home/nvim/plugin/tinygo.lua @@ -0,0 +1,23 @@ +-- sago35/tinygo.vim の `:TinygoTarget` を .tinygo.json 検出時に自動実行する。 +-- `:TinygoTarget` 内部で `vim.lsp.enable('gopls', false → true)` を呼び、 +-- Neovim 0.11.2 以降はこの流れで gopls が新しい cmd_env で再 attach する。 +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 config_path = vim.fs.joinpath(root, '.tinygo.json') + local ok, cfg = pcall(function() + return vim.json.decode(table.concat(vim.fn.readfile(config_path), '\n')) + end) + 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, +}) From 17b8206fa2fd8147038da1511b84e9ab23213238 Mon Sep 17 00:00:00 2001 From: rin2yh Date: Mon, 6 Jul 2026 18:59:21 +0900 Subject: [PATCH 14/17] rename: plugin/tinygo.lua -> plugin/apply-tinygo.lua `tinygo.lua` alone doesn't convey what the file does; multiple things could be TinyGo-related. Rename to a verb-object form that names the responsibility: applying the target from `.tinygo.json` to the running gopls. Claude-Session: https://claude.ai/code/session_01MpL4ZtCkUDDxdAGqEHUMUP --- home/nvim/plugin/{tinygo.lua => apply-tinygo.lua} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename home/nvim/plugin/{tinygo.lua => apply-tinygo.lua} (100%) diff --git a/home/nvim/plugin/tinygo.lua b/home/nvim/plugin/apply-tinygo.lua similarity index 100% rename from home/nvim/plugin/tinygo.lua rename to home/nvim/plugin/apply-tinygo.lua From 0a8c249eee1adc3facede8810679a409fa4fbf27 Mon Sep 17 00:00:00 2001 From: rin2yh Date: Mon, 6 Jul 2026 19:15:21 +0900 Subject: [PATCH 15/17] revert: keep TinyGo autocmd in lsp/gopls.lua MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Splitting the ~20 line TinyGo autocmd into plugin/apply-tinygo.lua added navigation cost without payoff — the autocmd exists to reconfigure gopls, and moving it away from the gopls spec made the relationship harder to see. Merge it back next to the M.cmd / M.settings it drives. Claude-Session: https://claude.ai/code/session_01MpL4ZtCkUDDxdAGqEHUMUP --- home/nvim/lua/lsp/gopls.lua | 24 ++++++++++++++++++++++++ home/nvim/plugin/apply-tinygo.lua | 23 ----------------------- 2 files changed, 24 insertions(+), 23 deletions(-) delete mode 100644 home/nvim/plugin/apply-tinygo.lua diff --git a/home/nvim/lua/lsp/gopls.lua b/home/nvim/lua/lsp/gopls.lua index 2037be2..b75b78f 100644 --- a/home/nvim/lua/lsp/gopls.lua +++ b/home/nvim/lua/lsp/gopls.lua @@ -29,4 +29,28 @@ 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 config_path = vim.fs.joinpath(root, '.tinygo.json') + local ok, cfg = pcall(function() + return vim.json.decode(table.concat(vim.fn.readfile(config_path), '\n')) + end) + 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/plugin/apply-tinygo.lua b/home/nvim/plugin/apply-tinygo.lua deleted file mode 100644 index 3e8b117..0000000 --- a/home/nvim/plugin/apply-tinygo.lua +++ /dev/null @@ -1,23 +0,0 @@ --- sago35/tinygo.vim の `:TinygoTarget` を .tinygo.json 検出時に自動実行する。 --- `:TinygoTarget` 内部で `vim.lsp.enable('gopls', false → true)` を呼び、 --- Neovim 0.11.2 以降はこの流れで gopls が新しい cmd_env で再 attach する。 -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 config_path = vim.fs.joinpath(root, '.tinygo.json') - local ok, cfg = pcall(function() - return vim.json.decode(table.concat(vim.fn.readfile(config_path), '\n')) - end) - 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, -}) From ffc1780a95be93922afd3cc3162fb9c1419e72e6 Mon Sep 17 00:00:00 2001 From: rin2yh Date: Mon, 6 Jul 2026 19:21:18 +0900 Subject: [PATCH 16/17] Simplify: drop pcall closure wrapper, inline config_path MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit pcall forwards args to its target, so pcall(vim.json.decode, str) is enough — no need for an inner closure. Fold config_path (used once) into the readfile call. Claude-Session: https://claude.ai/code/session_01MpL4ZtCkUDDxdAGqEHUMUP --- home/nvim/lua/lsp/gopls.lua | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/home/nvim/lua/lsp/gopls.lua b/home/nvim/lua/lsp/gopls.lua index b75b78f..ada039c 100644 --- a/home/nvim/lua/lsp/gopls.lua +++ b/home/nvim/lua/lsp/gopls.lua @@ -42,10 +42,8 @@ vim.api.nvim_create_autocmd('FileType', { callback = function(args) local root = vim.fs.root(args.buf, '.tinygo.json') if not root then return end - local config_path = vim.fs.joinpath(root, '.tinygo.json') - local ok, cfg = pcall(function() - return vim.json.decode(table.concat(vim.fn.readfile(config_path), '\n')) - 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 From 3434ebfcc17fc436c44415960f8f9c005e116e9e Mon Sep 17 00:00:00 2001 From: rin2yh Date: Mon, 6 Jul 2026 21:02:11 +0900 Subject: [PATCH 17/17] docs: add notes on when darwin-switch is required --- README.md | 10 ++++++++++ 1 file changed, 10 insertions(+) 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`。