Prefixes session titles with this machine's emoji tag (๐ป MBP, ๐ชถ Air, ๐ฎ Rig, ๐ฅ 5090, โก 4090), so pinned and listed sessions show which computer they run on

Private Claude Code marketplace for Tuncer's mods: function-hook plugins that run inside every Claude Code session on his machines (MacBook Pro, MacBook Air, therig, the 5090 PC, the 4090 work PC).
| Plugin | What it does |
|---|---|
memory-guards | Refuses the footgun commands Claude's memory notes warn about (for example impeccable update --help, routing shiftyeyegames.com DNS through cloudflared, /logout before cswap add) and says why, with the safe alternative. |
kb-health | Shows KB health in the status line on the machine that runs the KB. It stays quiet on machines without the KB snapshot job. |
machine-tag | Prefixes session titles with the machine's emoji tag (๐ป MBP, ๐ชถ Air, ๐ฎ Rig, ๐ฅ 5090, โก 4090), so pinned sessions show which computer they run on. /machine-tag <emoji> <word> overrides it per machine. |
pin-everywhere | Pinning a session turns on its Remote Control once machine-tag has put the machine icon in the title, so the session shows by name and icon on every other computer and the phone. Unpinning turns it back off when this mod turned it on. A title is published when Remote Control connects, so after renaming a pinned session, unpin and pin it again to publish the new name. |
claude plugin marketplace add Shifty-Eye-Games/claude-mods
claude plugin install memory-guards@claude-mods
claude plugin install kb-health@claude-mods
claude plugin install machine-tag@claude-mods
claude plugin install pin-everywhere@claude-mods
Then turn on auto-update: in /plugin, open Marketplaces, pick claude-mods and enable auto-update, or set it in ~/.claude/settings.json:
"extraKnownMarketplaces": {
"claude-mods": {
"source": { "source": "github", "repo": "Shifty-Eye-Games/claude-mods" },
"autoUpdate": true
}
}
Each session start then pulls main. No versions are set, so every commit is an update.
The repo is private, so each machine needs GitHub access without a prompt: an SSH key GitHub knows (Claude Code tries ssh -T git@github.com first), or stored HTTPS credentials (gh auth login then gh auth setup-git, or Git Credential Manager on Windows). Without them the clone and the updates fail silently and the machine keeps its last copy.
Machines that loaded these mods from local folders before must drop those folders from CLAUDE_CODE_PLUGIN_DIRS in ~/.claude/settings.json, or each mod loads twice.
plugins/<name>/.claude plugin validate plugins/<name> and claude plugin test plugins/<name>.main. Versions are left unset on purpose, so each commit reaches every machine at its next session start.How mods work, and the engine rules that bite, are in Claude's memory note on Claude Code mods.
Auto-update is on, so whatever lands on main runs in every session on all four machines. Only org admins can push here (the org's default member permission is read). Keep it that way: never give anyone else write access.
hooks/register.ts 139 lines1import type { EngineInterface, Register } from 'claude-code'
2
3// Prefixes each session's title with this machine's tag, an emoji and one word ("๐ป MBP ยท Fix the nav"), so
4// pinned and listed sessions show which computer they run on. A new session gets its tag at the prompt after
5// its title appears. `/machine-tag ๐ธ Box` sets this machine's tag (kept in this machine's store).
6const TAG = /^\p{Extended_Pictographic}(?:\uFE0F|\u200D\p{Extended_Pictographic}|[\u{1F3FB}-\u{1F3FF}])* \S+$/u
7const KINDS: [RegExp, string][] = [
8 [/macbook pro/i, '๐ป MBP'],
9 [/macbook air/i, '๐ชถ Air'],
10 [/therig/i, '๐ฎ Rig'],
11 [/tuncerhomepc/i, '๐ฅ 5090'],
12 [/tuncer-pc-work/i, 'โก 4090'],
13 [/mac studio/i, '๐ฅ๏ธ Studio'],
14 [/mac mini/i, '๐ฆ mini'],
15]
16// Tags that may sit in front of a title and get replaced: every machine's default, the colored dots the first
17// version used, and this machine's earlier custom tags. Anything else (the person's own "๐ Bug ยท") is kept.
18const OLD_TAGS = ['๐ต MBP', '๐ข Air', '๐ด Rig', '๐ก 5090', '๐ Studio', '๐ฃ mini']
19const GET = 'mcp__ccd_session_mgmt__get_session'
20const SET = 'mcp__ccd_session_mgmt__set_session_title'
21
22// asked: titles already handled in this load. Renaming a title the person set asks them first; asking once
23// per title means a "no" (or nobody there to answer) is never asked again.
24const state = { derived: undefined as string | undefined, asked: new Set<string>() }
25
26async function machineTag($: EngineInterface): Promise<string> {
27 const saved = await $.store.get('tag') // read each time, so /machine-tag reaches every open session
28 if (typeof saved === 'string' && TAG.test(saved)) {
29 return saved
30 }
31 if (state.derived === undefined) {
32 const mac = await $.process.run(['/usr/sbin/scutil', '--get', 'ComputerName']).catch(() => undefined)
33 const host = mac?.exitCode === 0 ? mac : await $.process.run(['hostname']).catch(() => undefined)
34 const name = host?.stdout.trim() || 'this-machine'
35 const kind = KINDS.find(([pattern]) => pattern.test(name))
36 state.derived = kind ? kind[1] : `๐ฅ๏ธ ${name.split(/[\s.]/)[0].slice(0, 12)}`
37 }
38 return state.derived
39}
40
41function tagged(title: string, tag: string, known: readonly string[]): string | undefined {
42 if (title.startsWith(`${tag} ยท `)) {
43 return undefined
44 }
45 let bare = title
46 for (let found = true; found; ) {
47 const old = known.find(t => bare.startsWith(`${t} ยท `))
48 found = old !== undefined
49 bare = old === undefined ? bare : bare.slice(old.length + 3)
50 }
51 bare = bare.trim()
52 return bare ? `${tag} ยท ${bare}` : undefined
53}
54
55async function knownTags($: EngineInterface): Promise<string[]> {
56 const earlier = await $.store.get('earlier')
57 return [...KINDS.map(([, t]) => t), ...OLD_TAGS, ...(Array.isArray(earlier) ? earlier.map(String) : [])]
58}
59
60// Only with an allow rule: in other modes the call would raise a dialog or a classifier check nobody asked for.
61async function isAllowed($: EngineInterface, tool: string, input: Record<string, string>): Promise<boolean> {
62 const verdict = await $.tool.check({ tool, input } as never).catch(() => undefined)
63 return verdict?.decision === 'allow'
64}
65
66// The desktop app keeps its own copy of the title and pushes it back each turn, so rename it there too,
67// reading it first so a title that already matches costs nothing. Elsewhere (the CLI) these tools don't exist.
68async function renameInApp($: EngineInterface, title: string) {
69 if (!(await isAllowed($, GET, { session_id: 'self' })) || !(await isAllowed($, SET, { session_id: 'self', title }))) {
70 return
71 }
72 const info = await $.tool.call({ tool: GET, session_id: 'self' } as never).catch(() => undefined)
73 let current: unknown
74 try {
75 current = (JSON.parse(info?.text ?? '') as { title?: unknown }).title
76 } catch {
77 return
78 }
79 if (current !== title) {
80 await $.tool.call({ tool: SET, session_id: 'self', title } as never).catch(() => undefined)
81 }
82}
83
84// Returns the engine title to set, if any, and schedules the app's rename outside the prompt's dispatch.
85async function onTitle($: EngineInterface, title: string | undefined): Promise<string | undefined> {
86 if (!title || state.asked.has(title)) {
87 return undefined
88 }
89 state.asked.add(title)
90 const wanted = tagged(title, await machineTag($), await knownTags($))
91 const inApp = wanted ?? title
92 $.clock.after(0, () => void renameInApp($, inApp))
93 return wanted
94}
95
96export const register: Register = on => {
97 on('session.start', async ($, e, next) => {
98 const ran = await next(e)
99 await $.command.register({
100 name: 'machine-tag',
101 description: "Show or set this machine's session tag, e.g. /machine-tag ๐ธ Box",
102 argumentHint: '[emoji word]',
103 immediate: true,
104 })
105
106 return ran
107 })
108
109 on('command.run', { command: 'machine-tag' }, async ($, e) => {
110 const wanted = e.args.trim()
111 if (!wanted) {
112 return { text: `This machine tags its sessions "${await machineTag($)}". Change it with /machine-tag <emoji> <word>, e.g. /machine-tag ๐ธ Box` }
113 }
114 if (!TAG.test(wanted)) {
115 return { text: 'A tag is one emoji and one word, e.g. /machine-tag ๐ธ Box' }
116 }
117 const earlier = await $.store.get('earlier')
118 await $.store.set('earlier', [...(Array.isArray(earlier) ? earlier : []), await machineTag($)].slice(-10))
119 await $.store.set('tag', wanted)
120 state.asked.clear()
121
122 return { text: `This machine now tags its sessions "${wanted}". Each open session picks it up at its next prompt.` }
123 })
124
125 on('classic.UserPromptSubmit', async ($, e, next) => {
126 const ran = await next(e)
127 const wanted = ran.sessionTitle === undefined ? await onTitle($, e.session_title) : undefined
128
129 return wanted === undefined ? ran : { ...ran, sessionTitle: wanted }
130 })
131
132 on('classic.SessionStart', async ($, e, next) => {
133 const ran = await next(e)
134 const wanted = ran.sessionTitle === undefined ? await onTitle($, e.session_title) : undefined
135
136 return wanted === undefined ? ran : { ...ran, sessionTitle: wanted }
137 })
138}
139