From a931143c1ebd428a37a89865030725d3cdb6fc68 Mon Sep 17 00:00:00 2001 From: Joywayer Date: Mon, 6 Jul 2026 07:57:25 +0800 Subject: [PATCH] =?UTF-8?q?=E5=BC=BA=E5=88=B6=E9=81=B5=E5=AE=88=E4=B8=89?= =?UTF-8?q?=E5=A5=97=E5=BC=80=E5=8F=91=E6=96=87=E6=A1=A3=EF=BC=9AREADME=20?= =?UTF-8?q?@import=20+=20PreToolUse=20=E6=8F=90=E9=86=92=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 把"必须严格遵守 docs 三套文档"从软指令变成硬机制,同时消除 SSOT 重复: - CLAUDE.md 用 @import 把三份 README(含各自红线速查/一页纸总则) 常驻每次会话上下文,删掉手写的红线摘要——红线以 README 权威源为准, 不再在 CLAUDE.md 另抄一份 - 新增 .claude/hooks/remind-docs.js:PreToolUse 钩子,编辑 client/**、 server/** 前按路径把"必须遵守的对应编号文档 + 红线"注入上下文(非阻塞) - 新增 .claude/settings.json 注册该 hook(matcher Edit|Write|MultiEdit); 已 pipe-test 各路径分支 + sentinel 验证 hook 实际触发 Co-Authored-By: Claude Opus 4.8 (1M context) --- .claude/hooks/remind-docs.js | 55 ++++++++++++++++++++++++++++++++++++ .claude/settings.json | 17 +++++++++++ CLAUDE.md | 21 ++++++-------- 3 files changed, 80 insertions(+), 13 deletions(-) create mode 100644 .claude/hooks/remind-docs.js create mode 100644 .claude/settings.json diff --git a/.claude/hooks/remind-docs.js b/.claude/hooks/remind-docs.js new file mode 100644 index 0000000..c89cd07 --- /dev/null +++ b/.claude/hooks/remind-docs.js @@ -0,0 +1,55 @@ +#!/usr/bin/env node +// PreToolUse hook:编辑 client/** 或 server/** 前,按路径把「必须遵守的文档」 +// 注入到上下文,作为硬提醒。非阻塞——只追加 additionalContext,不拦截操作。 +// 权威说明见根目录 CLAUDE.md「文档」一节与各 README 红线速查。 + +var chunks = []; +process.stdin.on('data', function (c) { chunks.push(c); }); +process.stdin.on('end', function () { + var reminder = ''; + try { + var input = JSON.parse(Buffer.concat(chunks).toString('utf8') || '{}'); + var p = (input.tool_input && input.tool_input.file_path) || ''; + p = String(p).replace(/\\/g, '/'); + + if (/\/docs\//.test(p)) { + // 改文档本身不提醒 + reminder = ''; + } else if (/\/codes\/shared\//.test(p)) { + reminder = + '你在改 codes/shared/(服务端 shared/ 的只读同步副本)。红线:前端侧不改,' + + '只改服务端权威源再跑同步脚本。详见 client 05 §「shared 只读」/ server 04 §8。'; + } else if (/\/client\/js\/gameabc-framework\//.test(p)) { + reminder = + '你在改 gameabc-framework/(可复用框架)。必须遵守:框架保持游戏中立,' + + '禁渗入任何具体玩法逻辑/常量;依赖严格单向。权威见 client 01、client 05 红线速查,' + + '设计通则见 games/engineering。动手前请先读对应编号文档。'; + } else if (/\/client\/js\/01_subgame\//i.test(p) || /\/client\/js\//.test(p)) { + reminder = + '你在改客户端子游戏/前端代码。必须遵守 client development-guide:' + + '架构与加载顺序(01)、渲染与组件(02)、事件/动画/音频/Spine(03)、网络对接(04)、' + + '红线速查(05)、接入模式(06);工程通则见 games/engineering。' + + '红线:严格 ES5、精灵只走 SpriteManager、常量集中禁硬编码、成败只认 data.success、' + + '服务端权威前端只展示、组件走 BaseComponent、数据优先表现延后。动手前先读对应编号文档。'; + } else if (/\/server\//.test(p)) { + reminder = + '你在改服务端代码。必须遵守 server development-guide:环境与框架(01)、' + + '子游戏接入(02)、通信协议(03)、红线速查(04);工程通则见 games/engineering。' + + '红线:严格 ES5、require 只在文件头守卫块、成败标志 data.success(推送包自带)、' + + '数据权威缺失显式报错、模块职责单一、房间隔离(房间+seat 为 key)、' + + '自动操作复用真人链路、下发包携带前端所需全部核心数据。动手前先读对应编号文档。'; + } + } catch (e) { + reminder = ''; + } + + if (reminder) { + process.stdout.write(JSON.stringify({ + hookSpecificOutput: { + hookEventName: 'PreToolUse', + additionalContext: reminder + } + })); + } + process.exit(0); +}); diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..42ba05e --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,17 @@ +{ + "hooks": { + "PreToolUse": [ + { + "matcher": "Edit|Write|MultiEdit", + "hooks": [ + { + "type": "command", + "command": "node \"$CLAUDE_PROJECT_DIR/.claude/hooks/remind-docs.js\"", + "timeout": 10, + "statusMessage": "校验开发文档红线…" + } + ] + } + ] + } +} diff --git a/CLAUDE.md b/CLAUDE.md index a4c1d1d..f8f1648 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -1,6 +1,6 @@ # CLAUDE.md -本文件为 Claude Code(claude.ai/code)在本仓库中工作时提供指导。**它只是入口与红线缓存,不复述规范细节**——权威、完整的规范在 `docs/` 三套文档里(见下「文档」一节),改动前先读对应文档。 +本文件为 Claude Code(claude.ai/code)在本仓库中工作时提供指导。**它是入口与强制指引**:三套文档的 README(含各自「红线速查 / 一页纸总则」)已通过 `@import` 常驻上下文(见下「文档」一节),**红线以它们为权威、始终在上下文里,本文件不再另抄一份**;规范的完整细节在 `docs/` 编号文档里,改动前先读对应文档。 ## 这是一个什么仓库 @@ -44,26 +44,21 @@ client/scripts/build_spine_data.cmd # 内部调用 build_spine_data.ps1 | `01_SubGame/codes/`(除 `shared/`)、`server/<游戏容器目录>/<游戏>/` | 子游戏自由开发区 | | `01_SubGame/codes/shared/` | 服务端 `shared/` 的只读同步副本;改服务端一侧后再同步 | -## 红线摘要 - -> always-on 缓存,**权威与完整清单见各 README 红线速查(client 05 / server 04)与 games/engineering 一页纸总则**;每条都对应过真实事故,冲突时一律以文档为准。 - -- **严格 ES5**:`var`/`function`、继承用 `Object.create`;禁 `let/const`/箭头函数/模板字符串/`class`/解构/`Promise`。服务端 `require` 只能写在文件开头 `if (typeof require !== 'undefined') { ... }` 守卫块内,禁止函数体内中途 `require`。 -- **成败只认推送包 `data.success`**(布尔值,主动推送的 `data` 必须自带)——绝不用 `status`/`code`,不写两者都判的兼容兜底。 -- **权威数据缺失要显式报错**:影响发牌/庄家/手牌/计分/重连的权威数据禁止 `|| 0`/`|| []`/`|| ''` 兜底掩盖;默认值仅允许用于纯展示/日志字段。 -- **房间隔离**:对局状态挂 `o_room.o_desk.data.*`,缓存/定时器/决策表以 `房间 + seat` 为 key(非仅 `seat`);禁止模块级单例/全局变量存对局态,小局结束/解散/开新局时清理干净。 -- **一个职能只在一个模块实现**:需要某能力时调用其所属模块,而不是在别处重造一份。 -- **自动操作复用真人链路**:服务端代玩家执行的操作(AI 托管、超时等)必须走与真人完全相同的 handler → 广播链路,客户端不为其单开解析分支。 - ## 文档(改动前先读——这三套是权威源) -本仓库开发**必须遵守**以下三套文档;改哪块就先读对应 README 与编号文档,而不是从代码反推约定: +本仓库开发**必须严格遵守**以下三套文档;改哪块就先读对应 README 与编号文档,而不是从代码反推约定: - `docs/client/development-guide/`(01→06)、`docs/server/development-guide/`(01→04)——项目专属的平台接入规范与红线。 - `docs/games/engineering/`(01→03)——位于两者之上、与平台无关的工程方法论(SSOT、单向依赖、职责单一、OCP、配置优先于硬编码、显式失败优于隐式兜底)。 三者互补不冲突:engineering 讲「该怎么设计、怎么长久演进」,dev-guide 讲「平台怎么接、红线是什么」;硬红线冲突时以 dev-guide 为准。 +以下三份 README(含各自「红线速查 / 一页纸总则」)通过 `@import` **自动加载进每次会话的上下文**——红线以它们为权威,无需在本文件另抄一份;改动具体区域时再深入读对应编号文档: + +@docs/client/development-guide/README.md +@docs/server/development-guide/README.md +@docs/games/engineering/README.md + 各编号对应主题(改哪块查哪篇): | 文档 | 编号与主题 |