SLOPSHOPPER

scratchpad-open

現セッションの scratchpad を Finder で開くボタンと /scratchpad コマンド

newbandcommandpromptprocesstimer
★ 1v0.1.0no licenseupdated 2026-09-22void2610/dotfiles/.claude/mods/scratchpad-open
A shopper browsing a rack in a slop shop
Preview · a replayed session in a sandbox
claude · ~/work/app · scratchpad-open
› fix the failing auth test and add an audit log call ⏺ Read(src/auth.ts) ⎿ Read 6 lines ⏺ Update(src/auth.ts) ⎿ Added 2 lines, removed 1 line ⏺ Bash(bun test) ⎿ 3 pass, 1 fail ● Done. refresh now rejects expired claims and logs an audit event. ✻ Worked for 42s · done 4:20 PM › /scratchpad ⎿ scratchpad-open: 開いた: /tmp/claude-/-work-app/preview-session/scratchpad ────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── › ? for shortcuts
README

dotfiles

個人用のdotfiles設定リポジトリです。 パッケージ管理と Homebrew 管理は別の nix-config リポジトリで行い、このリポジトリは主にシェルやアプリ設定を管理します。

セットアップ手順

1. リポジトリのクローン

git clone https://github.com/yourusername/dotfiles.git ~/dotfiles
cd ~/dotfiles

2. Nix / nix-darwin 設定の適用

cd ~/nix-config
darwin-rebuild switch --flake .#<nix-configで定義したホスト名>

3. dotfilesのシンボリックリンク作成

./install.sh

既存の設定ファイルは~/backupディレクトリにバックアップされます。

ファイル構成

  • install.sh: dotfilesのシンボリックリンク作成スクリプト
  • MANUAL_APPS.md: 手動インストールが必要なアプリケーション一覧
  • .config/: 各種アプリケーションの設定ファイル
  • .zshrc: Zshシェルの設定
  • .gitconfig: Git設定
  • .tmux.conf: tmux設定
  • .claude/: Claude Code の設定
  • .codex/: Codex の設定
Source 1 files
hooks/index.tsx 77 lines
1import type { EngineInterface, Register } from "claude-code";
2
3let fileCount = 0;
4let watcherStarted = false;
5
6async function scratchpadDir($: EngineInterface): Promise<string> {
7  const id = await $.session.id();
8  const root = await $.session.root();
9  const uid = (await $.process.run(["id", "-u"])).stdout.trim();
10  // scratchpad の実体パス規約 (ハーネス内部実装依存): /tmp/claude-<uid>/<root の / を - に置換>/<sessionId>/scratchpad
11  return `/tmp/claude-${uid}/${root.replaceAll("/", "-")}/${id}/scratchpad`;
12}
13
14async function openScratchpad($: EngineInterface): Promise<string> {
15  const dir = await scratchpadDir($);
16  await $.process.run(["mkdir", "-p", dir]);
17  await $.process.run(["open", dir]);
18  return dir;
19}
20
21async function refreshCount($: EngineInterface): Promise<void> {
22  const dir = await scratchpadDir($);
23  const r = await $.process.run(["find", dir, "-type", "f"]);
24  const n =
25    r.exitCode === 0 ? r.stdout.split("\n").filter((l) => l !== "").length : 0;
26  if (n !== fileCount) {
27    fileCount = n;
28    $.ui.invalidate("ui.render");
29  }
30}
31
32function ensureWatcher($: EngineInterface): void {
33  if (watcherStarted) return;
34  watcherStarted = true;
35  $.clock.every(5000, () => refreshCount($));
36}
37
38export const register: Register = (on) => {
39  on("session.start", async ($, e, next) => {
40    await $.command.register({
41      name: "scratchpad",
42      description: "現セッションの scratchpad を Finder で開く",
43    });
44    ensureWatcher($);
45    return next(e);
46  });
47
48  // リロード時は session.start が再発火しないため他フックからも起動できるようにする
49  on("prompt.submit", ($, e, next) => {
50    ensureWatcher($);
51    return next(e);
52  });
53
54  on("command.run", { command: "scratchpad" }, async ($) => {
55    return { text: `開いた: ${await openScratchpad($)}` };
56  });
57
58  on("ui.render", { component: "AbovePrompt" }, async ($, e, next) => {
59    if (fileCount === 0) return next(e);
60    const { Box, Button } = $.ui.resolve(e);
61    // pr-watch 等が同じ帯を使うため、チェーンの結果に自分のボタンを併記する
62    const rest = await next(e);
63    return (
64      <Box flexDirection="column">
65        {rest}
66        <Button
67          key="scratchpad"
68          label={`scratchpad (${fileCount})`}
69          plain
70          dimColor
71          onPress={() => void openScratchpad($)}
72        />
73      </Box>
74    );
75  });
76};
77