SLOPSHOPPER

rules-personal

kawaz 個人環境のルール層に付随する hook 群 (context 管理の自動化)

newprompt
v0.9.12MITupdated 2026-10-26kawaz/claude-rules-personal
A shopper browsing a rack in a slop shop
README

claude-rules-personal

kawaz の Claude Code 用ルール / スキルの central リポジトリ。 claude-rules-* overlay 群を束ね、just setup で各 CLAUDE_CONFIG_DIR に配備する。

リポジトリ群の構成

claude-rules-* は 1 つの central + 複数の overlay で構成される。

リポ役割専用環境 (CLAUDE_CONFIG_DIR)
kawaz/claude-rules-personal (これ)central。全 overlay を束ね、配備 recipe (justfile + scripts/) を持つ。環境一覧 repos_mapping.local.json は gitignore 対象で各マシンに置く (雛形は repos_mapping.example.json)~/.claude-personal
業務用 overlay (private)業務面のルール差分専用環境
kawaz/claude-rules-zunsystemzunsystem 識別子 overlay (private)(専用環境なし)
業務識別子 overlay (private)特定業務面の識別子差分(専用環境なし)
  • 配備 recipe は この personal リポにのみ置く (2 重管理しない)。repos_mapping.local.json は追跡しない
  • どの overlay のルール/スキルを変更しても、反映は personal で just setup を実行する
  • 整理方法・設計判断などの詳細ドキュメントは personal の docs/ に集約する

レイアウト

各リポ共通:

  • for-all/rules/ — 全環境向けルール (全 ~/.claude*/rules/ に注入)
  • for-all/plugins.json — 全環境に入れる Claude Code plugin の宣言 (just plugins-setup が install)
  • for-me/rules/ — その面の専用環境にのみ注入
  • for-me/plugins.json — その面の専用環境にのみ install する plugin の宣言。 plugin の skill / agent description は全セッションの context に載るので、 面固有の plugin はこちらに置く (for-all に置くと他の面にも語彙が漏れる)
  • for-others/rules/ — 他環境から参照される情報 (固有名詞リスト等のサニタイズ規定)

skill と agent は リポ自体を Claude Code plugin として配布する (.claude-plugin/plugin.json + リポ直下の skills/<slug>/ agents/ hooks/)。 各リポの for-all/plugins.json に自リポの plugin を宣言し、just plugins-setup が install することで配備される。Skill tool からは <plugin名>:<slug> (例: rules-personal:eli5) で呼ぶ。hooks/ には command hook (*.sh) と mod (*.ts、hooks.json の modules。$.state の契約は types/index.d.ts) を置き、just test-hooks が両方を検査する。skill はユーザが /名前 で起動する実行系だけを置き、 読むだけの手順書は reference/ に置く (判定は for-all/rules/rule-writing-guidelines.md)。 AI に自動起動させたくないユーザ専用の skill は frontmatter に disable-model-invocation: true を付け (AI の一覧から消える)、 本文は reference の該当ファイルを読ませる 1〜2 行に留める。

読むだけの参照知識はこのリポの直下に平置きする (索引と本文の 2 段構成、本文は必要時にだけ Read):

  • reference/ — 公開してよい体系知識。索引は reference/_index.md
  • memory/ — rule にするほど一般化していない横断メモ。索引は memory/_index.md

個人情報系 (本人の表記・アカウント名・連絡先) は public 候補のこのリポではなく private リポ kawaz/privacy-personal に同じ構造で置き、その索引だけを reference/_index.md の「private 層」節に写す (業務面でしか効かないものは各 overlay の for-me/)。 書き先の判定は for-all/rules/knowledge-guide.md。

for-me の "me" は「個人 vs 他者」ではなく、kawaz が持つ複数の面 (個人開発 / emrd 業務 / ...) のうちの その overlay の面を指す。

personal リポ固有:

  • scripts/rules.sh / scripts/plugins.sh — 配備スクリプト (subject ごと。justfile の recipe から呼ぶ)
  • repos_mapping.local.json — 全 overlay リポと各 home (CLAUDE_CONFIG_DIR) の定義
  • docs/ — 設計判断・課題 (issue/)、運用手順 (runbooks/) 等

セットアップ

配備は just の recipe で行う。subject (rules / plugins) ごとに <subject>-{setup,check,update} があり、setup / check / update がそれらを束ねる。home 引数を省略すると repos_mapping.local.json に宣言された全面が対象:

just setup                      # 全面: rules の symlink + plugin の install
just check                      # 全面: 配備状態の検査 (変更しない)
just update                     # 全面: plugin の update
just rules-setup ~/.claude-emrd # 1 面だけ
  • rules-setup: for-*/rules/ を $HOME_DIR/rules/ 配下にディレクトリ symlink し、dangling link を掃除 (rules-check は期待する link が揃って実体を指しているかを検査)
  • plugins-setup: for-all/plugins.json (全 repo) と自面の for-me/plugins.json に宣言された plugin を marketplace add + install。bare 面 (~/.claude-bare) は repos_mapping.local.json の pluginOnlyHomes に列挙した ccmsg だけを入れる
  • plugins-check: rules 面は宣言の不足だけ、bare 面は ccmsg 以外が入っていないことも検査。宣言外に手で入れた plugin と enable / disable 状態は触らない

ドキュメント

  • docs/issue/ — TODO・課題
  • docs/runbooks/ — 運用・セットアップ手順

サニタイズ

業務固有名詞は public 候補の personal には置かず、各 overlay リポ (private) で管理。 共通サニタイズの仕組みは for-all/rules/sanitize-work-identifiers.md を参照。

Source 2 files
hooks/thinking-guard.ts 35 lines
1import type { On } from 'claude-code'
2
3// 2025-12〜2026-10 の両面 241,077 thinking block の実測 (194 ヒットを 3 lens で分類) から採用。
4// ユーザの感情を話題にしている率が高い語だけを残し、frustrated は一人称 (model 自身) を除外。
5// 詰問 / 萎縮 / 責め は model 自身の rule 適用文に当たるだけなので入れない。
6const ASSUMED_EMOTION = /(?<!\bI(?:'m| am| was|'ve been) )frustrated|怒っ|苛立|叱責|不満|\b(?:upset|angry|annoyed|impatient)\b/iu
7const REMINDER = '(reminder) 直前の thinking でユーザの感情・意図を決めつけていた。no-excessive-apology rule を思い出す'
8const STATE = { plugin: 'rules-personal', key: 'thinkingGuard' } as const
9
10export function register(on: On): void {
11  on('session.start', async ($, e, next) => {
12    await $.state.set(STATE, { isInteractive: e.isInteractive, reminderPending: false })
13    return next(e)
14  })
15
16  on('session.append', { door: 'response' }, async ($, e, next) => {
17    if (e.agentId !== undefined) return next(e)
18    const { value } = await $.state.get(STATE)
19    if (value?.isInteractive && e.message.content.some(block =>
20      block.type === 'thinking' && typeof block.thinking === 'string' && ASSUMED_EMOTION.test(block.thinking)
21    )) {
22      await $.state.set(STATE, { ...value, reminderPending: true })
23    }
24    return next(e)
25  })
26
27  on('prompt.submit', async ($, e, next) => {
28    const { value } = await $.state.get(STATE)
29    if (!value?.isInteractive || !value.reminderPending) return next(e)
30    const result = await next({ ...e, context: [...(e.context ?? []), REMINDER] })
31    await $.state.set(STATE, { ...value, reminderPending: false })
32    return result
33  })
34}
35
types/index.d.ts 16 lines
1// rules-personal plugin が $.state に持つ値の契約 (engine が validate 時に照合する)
2export interface ThinkingGuardState {
3  /** session.start の isInteractive。false (-p 等) なら mod は何もしない */
4  isInteractive: boolean
5  /** 直前の turn の thinking で決めつけ語が出た。次の prompt.submit で reminder を足して false に戻す */
6  reminderPending: boolean
7}
8
9declare module 'claude-code' {
10  interface PluginState {
11    'rules-personal': {
12      thinkingGuard: ThinkingGuardState
13    }
14  }
15}
16