SLOPSHOPPER

human-tone

Pin a house style into the system prompt: direct, human answers with no filler praise or over-formatting.

newprompt
v0.1.1no licenseupdated 2026-10-06boxiaolanya2008/claudecode-plus-mod/mods/human-tone
A shopper browsing a rack in a slop shop
README

human-tone

Claude Code Mod:给模型的 system prompt 里钉一段“说话风格宪法”,让它 说人话,而不是无脑吐字。

机理

Mod 够不着模型的嘴,只能改它看到的指令。本 Mod 在 prompt.compose 处追加一个静态 section(plus:human-tone),放在 shared 阵营末尾, 引擎自己的缓存分界保持有效。文本静态 → 不花任何人的 prompt cache。

默认风格六条:先结论后细节;禁空洞吹捧;不确定直说;跟用户语言走; 格式克制;代码先给能跑的。mode 可切 terse(极简)/warm(多点 人情味),extra 追加自己的行。

这是全局审美干预,装上后所有会话都生效,介意就别装。

使用

claude --plugin-dir mods/human-tone
claude plugin test mods/human-tone
claude plugin install plus@claudecode-plus-mod --scope user
Source 2 files
hooks/register.ts 43 lines
1import type { On } from 'claude-code'
2
3import { SECTION_ID, insertShared, resolveMode, styleGuide } from './guide'
4
5type ToneOptions = {
6  mode?: string
7  extra?: string
8  verbose?: boolean
9}
10
11function report($: unknown, message: string, verbose: boolean): void {
12  const ui = ($ as { ui: { log: (text: string, opts?: object) => void } }).ui
13  if (verbose) {
14    ui.log(message)
15  } else {
16    ui.log(message, { to: 'debug' })
17  }
18}
19
20/**
21 * Appends one static style section to the system prompt, last among the
22 * shared sections so the engine's cache boundary stays valid. Static text
23 * costs no prompt cache; `extra` is the only varying part, keep it stable.
24 *
25 * @param on the engine's registrar
26 * @param options `mode` picks default, terse or warm; `extra` appends lines
27 */
28export function register(on: On, options: ToneOptions = {}): void {
29  const text = styleGuide(resolveMode(options.mode), options.extra)
30  const verbose = options.verbose === true
31
32  on('session.start', ($, e, next) => {
33    report($, '[human-tone] active, style section armed for prompt.compose', verbose)
34    return next(e)
35  })
36
37  on('prompt.compose', async (_$, e, next) => {
38    const out = await next(e)
39    const mine = { id: SECTION_ID, text, scope: 'shared' as const }
40    return { sections: insertShared(out.sections, mine) }
41  })
42}
43
hooks/guide.ts 70 lines
1/**
2 * House style texts: one static section pinned into the system prompt.
3 * Static on purpose, so it sits on the shared side of the cache boundary
4 * without spending anyone's prompt cache.
5 */
6
7export const SECTION_ID = 'plus:human-tone'
8
9export type ToneMode = 'default' | 'terse' | 'warm'
10
11const DEFAULT_GUIDE = [
12  '说话像人,不像客服。先给结论,再给细节。',
13  '禁止空洞吹捧("很好的问题"、"你问得太对了"之类一律不许)。',
14  '不确定的直说不确定,不编;能查的先查再下结论。',
15  '用户用什么语言,就用什么语言回。',
16  '格式克制:不用 emoji,不滥用表格和标题,短回答优先。',
17  '代码先给能跑的,再解释为什么;不贴没验证过的写法。',
18].join('\n')
19
20const TERSE_GUIDE = [
21  '极简模式:能一句话不说两句,能给代码不给散文。',
22  '禁止寒暄、吹捧、总结陈词,直接上干货。',
23  '不确定的用一句标出,不展开。',
24].join('\n')
25
26const WARM_GUIDE = [
27  '在默认风格上多一点人情味:共情先行,但不说废话。',
28  '夸只夸具体的,不夸空泛的;安慰给方案,不给鸡汤。',
29  '其余与默认一致:先结论后细节,不确定直说。',
30].join('\n')
31
32/**
33 * @param raw the `mode` option as received, anything unexpected falls back
34 * @returns a known tone mode, never throws on user input
35 */
36export function resolveMode(raw: unknown): ToneMode {
37  if (raw === 'terse' || raw === 'warm' || raw === 'default') {
38    return raw
39  }
40  return 'default'
41}
42
43/**
44 * @param mode which built-in guide to use
45 * @param extra user-supplied lines appended verbatim, absent when empty
46 * @returns the full section text
47 */
48export function styleGuide(mode: ToneMode, extra?: string): string {
49  const base = mode === 'terse' ? TERSE_GUIDE : mode === 'warm' ? WARM_GUIDE : DEFAULT_GUIDE
50  const tail = extra === undefined || extra.trim() === '' ? '' : `\n${extra.trim()}`
51  return `${base}${tail}`
52}
53
54export type ComposableSection = {
55  id: string
56  text: string
57  scope: 'shared' | 'session'
58}
59
60/**
61 * @param sections the engine's composed list, every shared one ahead of every session one
62 * @param mine the style section to add
63 * @returns list with the style section last among shared, so the engine's own boundary stays valid
64 */
65export function insertShared(sections: readonly ComposableSection[], mine: ComposableSection): ComposableSection[] {
66  const shared = sections.filter((section) => section.scope === 'shared')
67  const session = sections.filter((section) => section.scope !== 'shared')
68  return [...shared, mine, ...session]
69}
70