rulemason 0.8.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. rulemason/__init__.py +3 -0
  2. rulemason/_shared/lint/eslint-base.mjs +10 -0
  3. rulemason/_shared/lint/ruff-base.toml +23 -0
  4. rulemason/_shared/openspec-base/artifact-rules.yaml +16 -0
  5. rulemason/_shared/openspec-base/operations.yaml +8 -0
  6. rulemason/_shared/rules/code-style.md +18 -0
  7. rulemason/_shared/rules/copy-voice.md +43 -0
  8. rulemason/_shared/rules/python.md +11 -0
  9. rulemason/_shared/rules/react.md +27 -0
  10. rulemason/_shared/rules/workflow-gates.md +75 -0
  11. rulemason/_shared/skills/rulemason/SKILL.md +88 -0
  12. rulemason/banner.py +98 -0
  13. rulemason/check.py +733 -0
  14. rulemason/cli.py +70 -0
  15. rulemason/init.py +773 -0
  16. rulemason/judge/__init__.py +34 -0
  17. rulemason/judge/base/copy-lexicon.yaml +24 -0
  18. rulemason/judge/base/judgments.yaml +22 -0
  19. rulemason/judge/judge.py +584 -0
  20. rulemason/judge/templates/judge.yaml +6 -0
  21. rulemason/judge/templates/judge_cases.yaml +7 -0
  22. rulemason/judge/templates/judgments.yaml +17 -0
  23. rulemason/key.py +32 -0
  24. rulemason/migrate.py +97 -0
  25. rulemason/remove.py +240 -0
  26. rulemason/shared.py +354 -0
  27. rulemason/templates/AGENTS.md.tmpl +23 -0
  28. rulemason/templates/claude.block.md +11 -0
  29. rulemason/templates/code-style.block.md +7 -0
  30. rulemason/templates/copies-README.md +6 -0
  31. rulemason/templates/opencode.jsonc.tmpl +9 -0
  32. rulemason/templates/ruff.toml.tmpl +5 -0
  33. rulemason/templates/workflow-gates-spec.md.tmpl +19 -0
  34. rulemason/templates/workflow-gates.block.md +1 -0
  35. rulemason/update.py +127 -0
  36. rulemason-0.8.0.dist-info/METADATA +203 -0
  37. rulemason-0.8.0.dist-info/RECORD +41 -0
  38. rulemason-0.8.0.dist-info/WHEEL +4 -0
  39. rulemason-0.8.0.dist-info/entry_points.txt +2 -0
  40. rulemason-0.8.0.dist-info/licenses/LICENSE +202 -0
  41. rulemason-0.8.0.dist-info/licenses/NOTICE +4 -0
rulemason/__init__.py ADDED
@@ -0,0 +1,3 @@
1
+ """rulemason 命令行:在项目里接入(init)、升级(update)、只读检查(check)共享规则,并用 judge 判断方案与文案。"""
2
+
3
+ __version__ = "0.8.0"
@@ -0,0 +1,10 @@
1
+ // ESLint 基础规则:代码写法的机械部分(rules/code-style.md)。只导出纯规则,不引插件:插件会从本仓库目录解析,而这里没有 node_modules
2
+ export default {
3
+ "no-console": "error",
4
+ "no-nested-ternary": "error",
5
+ // 空块(含空 catch,九条第 8 条)。确需空 catch 的项目在自己的配置里放宽并写明原因
6
+ "no-empty": "error",
7
+ // 取 15 而非 Python 的 10:JSX 里每个 && 和 ?: 都算一个分支,同样的上限放到组件上会严得多。
8
+ // 项目当前代码超出时,在自己的配置里用当前最复杂函数的值覆盖,写明递减计划,只降不升
9
+ complexity: ["error", 15],
10
+ };
@@ -0,0 +1,23 @@
1
+ # ruff 基础规则:代码写法的机械部分(rules/code-style.md)。项目用 extend 继承,只写自己的豁免并注明原因,不写 select。
2
+
3
+ [lint]
4
+ # 只启用 select 里点名的预览规则(PLW1514);预览模式也会改变部分稳定规则的行为,升级 ruff 后留意新增报错
5
+ preview = true
6
+ explicit-preview-rules = true
7
+ select = [
8
+ "ERA001", # 注释掉的代码(九条第 6 条)
9
+ "T201", # print(第 6 条)
10
+ "BLE001", # 过宽的 except Exception(第 8 条)
11
+ "E722", # 裸 except:(第 8 条)
12
+ "C901", # 圈复杂度
13
+ "F", # pyflakes:未用导入与变量、未定义名(第 6、9 条)
14
+ "E701", # 冒号后接语句写在同一行(附加条款「一行只做一件事」)
15
+ "E702", # 一行用分号写多条语句(同上)
16
+ "RUF021", # a and b or c 不加括号(附加条款「要停下来想优先级的表达式拆开写」)
17
+ "PLW1514", # 读写文本不写 encoding(python.md;Windows 中文版默认按 GBK 读写)
18
+ "B", # bugbear:zip 不写 strict、可变默认值等常见坑;框架约定在参数默认值里调函数的,由项目用 extend-immutable-calls 声明
19
+ "I", # import 排序
20
+ ]
21
+
22
+ [lint.mccabe]
23
+ max-complexity = 10
@@ -0,0 +1,16 @@
1
+ # 产物写法基础版:各项目照写进 openspec/config.yaml 的 rules 段,只追加不改写(OpenSpec 配置不能引用外部文件)
2
+ rules:
3
+ proposal:
4
+ - 必须说明本次明确不做的事:Non-goals 小节,或 What Changes 中以「不做」开头的条目,二者取一即可
5
+ - 控制在 60 行以内,只写为什么和改什么,不写怎么实现
6
+ design:
7
+ - Decisions 每条决策写明至少一个被否决的备选方案及否决理由
8
+ - Risks 每条用「[风险] → 对策」格式,一行一条
9
+ - Migration Plan 写明上线顺序与回滚方式
10
+ - 列出新增与修改的文件及每个文件的估计行数,apply 后对照;超出一倍要说明原因
11
+ specs:
12
+ - 每个 Requirement 至少一个 Scenario,用 WHEN / THEN
13
+ - 用 SHALL / MUST 表达约束,不用 should / may
14
+ - 每条 Requirement 正文不超过 500 个字符(openspec validate --strict 的上限)
15
+ tasks:
16
+ - 每条任务两小时内可完成,并写明怎么验证它完成了
@@ -0,0 +1,8 @@
1
+ # OpenSpec apply / archive 时交给 AI 的提示基础版:只指向检查点文件,不复制正文。$gates 由 rulemason 代入(接入项目为 .rulemason/rules/workflow-gates.md)
2
+ operations:
3
+ apply:
4
+ guidance:
5
+ - 写代码前与写完代码后,按 $gates 的检查点执行;本项目命令表与红线在 AGENTS.md;本提示只是指路,与 AGENTS.md 引入的规则不一致时以规则文件为准
6
+ archive:
7
+ guidance:
8
+ - 归档前确认已按 $gates「apply 最后一个任务做完」各步执行,并把结果写进完成汇报
@@ -0,0 +1,18 @@
1
+ <!-- 代码写法九条:不分语言的原则。项目经 rulemason 生成的 .rulemason/ 副本引用:不把正文抄进项目指令,不手改副本 -->
2
+
3
+ 注释与 docstring 用中文;标识符用英文,界面文字与数据值可以是中文。九条不分语言,某门语言的具体落法写在对应的落法文件(如 python.md、react.md)或项目语言目录的项目指令里;样板文件由项目在引入处写明。
4
+
5
+ 1. 每个文件第一行用一句话写它是干什么的。
6
+ 2. 主流程(分几个阶段完成一件事的函数)按编号步骤写注释(`1.`、`2.`、`2.1`),一个步骤可管几行,不逐行注释。
7
+ 3. 原因不能从条件本身看出的分支或提前返回,写一句「走到这里说明什么」(如 `走到这里说明上游已校验过参数`)。
8
+ 4. 返回元组或特殊值时,docstring 写清每种情况的含义。
9
+ 5. 不复述代码。一看就懂的行不写注释;行尾注释只写「为什么」,不写「做什么」。
10
+ 6. 不留注释掉的代码和调试输出(`print`、`console.log`);命令行与脚本的正常输出除外,见各语言落法。
11
+ 7. 同一段逻辑(两行以上,或含判断的表达式)出现第二次就抽成函数。
12
+ 8. 兜底式错误处理并给降级结果(`except Exception`、`.catch(() => …)`、`catch(e){}`)只放在面向用户或命令行的最外层;内部函数让错误往上抛。把底层异常转换成带上下文的异常再往上抛是允许的,吞掉才不行。
13
+ 9. 不加当前没有调用方的参数、配置项、扩展点。
14
+
15
+ ## 九条之外
16
+
17
+ - 一行只做一件事;`a and b or c` 这类要停下来想优先级的表达式拆开写。
18
+ - 不提交密钥、凭证与本机绝对路径(如 `/Users/<某人>/...`);用环境变量、配置文件或相对路径。
@@ -0,0 +1,43 @@
1
+ <!-- 界面文案标准:三档定义、通用禁用、写法与校准方法;项目只补例句、固定用语、词表与禁词。项目经 rulemason 生成的 .rulemason/ 副本引用:不把正文抄进项目指令,不手改副本 -->
2
+
3
+ 只管界面上给用户看的文字。方案文档怎么写由产物写法(`openspec/config.yaml` 的 rules)管。
4
+
5
+ ## 三档定义
6
+
7
+ 判断尺度是「像不像人平时写给人看的话」。项目判断规则的档位描述照抄下面三行,一字不改;本项目的例句写在档位描述之后,见下一节。
8
+
9
+ - 0 档:模板:英文报错、占位符、翻译腔、框架自带没改过的文案,读者需要猜它在说什么
10
+ - 1 档:生硬或做作:意思对,但有开发术语、公文腔、省略成分、标签式缩略;或者反过来文气、做作、堆修辞,不像平时说话
11
+ - 2 档:自然:用词日常,直接说清楚发生了什么或要用户做什么,一遍读懂;正常的中文状态短句(加载中、提交失败请稍后再试)也算这一档;可以有本站固定用语,但不为了文气加字
12
+
13
+ ## 例句由项目提供
14
+
15
+ 每档至少两个例句,从项目自己的界面文案里取,其中至少一个来自项目领域。Jev 打分准不准主要看例句:给某一档加一个贴切的例句,置信度会明显上升(一次实测从 0.35 升到 0.96);只用通用例句会变不准。项目还没有可用作例句的文案时,不启用文案打分规则,只跑通用禁用与短标签词表。
16
+
17
+ ## 通用禁用
18
+
19
+ 面向用户的文案不用感叹号、颜文字(如「:(」)、英文感叹词(如「Oops」)。命中即在 lint 阶段 block,不送 Jev。项目自己的禁词写在项目里,同样在 lint 阶段拦截。
20
+
21
+ ## 报错与空状态
22
+
23
+ 说明发生了什么,以及用户接下来可以做什么;不归咎用户。例:「无法连接服务器,请稍后重试。」
24
+
25
+ ## 新增文案须达 2 档
26
+
27
+ 相对主分支新增或修改的界面文案:2 档 pass;1 档 review,交人判定;0 档 block,改后才能合并;置信度不足一律 review。阈值由项目按校准结果设定;校准未完成时可把 block 阈值设为 0(0 档也只 review),并在配置里写明原因。
28
+
29
+ ## 1 档改 2 档只润色不重写
30
+
31
+ 只替换术语、补全省略、去掉公文腔或多余修辞,保留原句结构与大致长度,不改成口语或文言。例:「后端暂时没有响应,可以稍后重试。」→「服务暂时没有响应,可以稍后重试。」
32
+
33
+ ## 短标签查词表
34
+
35
+ 不超过四个字(忽略 `{占位符}` 与空格)的控件与状态标签不送 Jev,查词表:rulemason 自带的通用词表与项目的 `openspec/copy-lexicon.yaml` 任一包含即 pass;都不包含则 review,人工通过后加进项目词表,同一标签只复核一次。通用词表只收不属于任何领域的标签;项目词表只收本项目界面上出现、且不在通用词表里的标签,不收其他项目的领域词。
36
+
37
+ ## 标注校准
38
+
39
+ 启用文案打分前做一次:从现网文案取 20 条,两人各自独立标注,或同一负责人间隔标注两次,算一致率。
40
+
41
+ - 一致率低于 70%:先改例句或补说明,再标一次。
42
+ - 模型与人的一致率目标:不低于人与人一致率减 5 个百分点。
43
+ - 校准数字记入该项目对应 change 的 `verification.md`。
@@ -0,0 +1,11 @@
1
+ <!-- 九条在 Python 里的落法,以及 Python 特有的几条。项目经 rulemason 生成的 .rulemason/ 副本引用:不把正文抄进项目指令,不手改副本 -->
2
+
3
+ **Python:**
4
+
5
+ - 章节分隔线写 `# ---------- 1. 名称 ----------`。
6
+ - `if __name__ == "__main__"` 只出现在脚本与命令行入口里(具体是哪些目录、哪些文件由项目写明);被导入的模块不带自测块。
7
+ - 测试放项目的测试目录,不放在被测模块里。
8
+ - 读写文本文件写明 `encoding="utf-8"`:Windows 中文版默认按 GBK 读写,不写会乱码。
9
+ - 服务端与长期运行的程序用 `logging` 输出;`print` 只用于命令行与脚本的正常输出,命令行入口与脚本所在的目录在 ruff 配置里豁免 T201,并注明原因。
10
+ - 机械部分由 ruff 检查,规则继承共享的基础配置。
11
+ - 豁免写在 ruff 配置里并注明原因,不在代码里逐行加 `noqa`。
@@ -0,0 +1,27 @@
1
+ <!-- 九条在 React / TSX 里的落法与 React 额外的几条。项目经 rulemason 生成的 .rulemason/ 副本引用:不把正文抄进项目指令,不手改副本 -->
2
+
3
+ **React / TSX:**
4
+
5
+ 样板文件、界面文字放在哪、目录约定由项目在引入处写明。
6
+
7
+ 九条的落法:
8
+
9
+ 1. 第一行一句话写用途,放在 `"use client"` 之上;不写文件路径,交互细节写在实现它的代码旁。
10
+ 2. 数据加载、路由处理和超过三条语句的事件处理按编号写步骤。JSX 是布局不是流程:只在组件名或标题都没说明时,才用 `{/* 区块名 */}` 标出区块。
11
+ 3. 提前返回(如 `if (!item) return null`)和条件渲染写一句「走到这里说明…」。
12
+ 4. 用类型代替 docstring;只有 `null` 或其他特殊值有含义时才写 JSDoc。
13
+ 5. 行尾注释写为什么(如 `pb-4 抵消缩略图容器的 py-4`),不写做什么(如 `展开调用方传入的配置`)。
14
+ 6. 不留 `console.*`,不留注释掉的 JSX 或代码。
15
+ 7. 同一段 JSX 出现第二次就抽成组件;同一份数据被两处读取(如页面与它的元数据)就抽成一个共用的加载函数。
16
+ 8. `.catch(() => 兜底)` 只放在页面与路由处理里;工具函数和组件让错误往上抛。有路由的项目,详情页把「找不到」转成 404,其他错误交给错误页。
17
+ 9. 不加当前没有调用方的 props、变体或配置。
18
+
19
+ React 额外的几条:
20
+
21
+ - 一行一条语句,事件处理也一样:不写 `() => { a(); b(); c(); }`。
22
+ - JSX 里不嵌套三元;两种以上的渲染状态抽成带提前返回的小组件。
23
+ - 读者要停下来想的 JSX 表达式(如 `[a, b].filter(Boolean).join(" · ")`)移到 `return` 之上,写成具名常量。
24
+ - 用 React Server Components 的项目默认写服务端组件,`"use client"` 只加在持有状态或副作用的叶子组件上。
25
+ - 同一串 class 用了两次就抽成常量或组件 prop;样式类名不加注释,除非是在说明不明显的对齐。
26
+
27
+ 机械部分由 ESLint 以 error 级别检查:项目配置引入共享基础规则(`no-console`、`no-nested-ternary`、`no-empty`、`complexity` 上限 15;项目代码超出时按 eslint-base 的说明写递减计划),并开启 `react-hooks/exhaustive-deps`。豁免写在 ESLint 配置里并注明原因,不在代码里逐行写 `eslint-disable`。
@@ -0,0 +1,75 @@
1
+ <!-- 三个自动检查点:包住每个 OpenSpec change。项目经 rulemason 生成的 .rulemason/ 副本引用:不把正文抄进项目指令,不手改副本 -->
2
+
3
+ 这些检查点包住每一个 OpenSpec change,自动执行,无需提醒。它们写在项目指令(AGENTS.md)引用的这份文件里,而不是工具生成的命令文件里,因为 `openspec update` 会覆盖那些文件。
4
+
5
+ 下文的「命令表的〈名称〉」指项目指令里「本项目命令表」中名称相同的那一行;「红线规则」指项目在同处列出的实质类规则;「额外步骤」同理。项目没有判断层时,见下文「没有判断层的项目」。
6
+
7
+ ## 方案刚写完时
8
+
9
+ 经 `/opsx:continue`、`/opsx:ff`、`/opsx:propose`,或在 explore 模式手写出 proposal / design 之后:
10
+
11
+ 1. 运行命令表的「判断」。
12
+ 2. 格式类 block(`lint/design`,含决策缺否决理由、Risks 格式、缺回滚说明;以及 `proposal-states-non-goals`)是起草失误:自己改好产物再跑一次。
13
+ 3. 实质类 block 或 review(项目列出的红线规则,以及 `design-no-speculative-extension`)等于改方案:报告给用户决定,不自行修改。
14
+ 4. 把最终结果(和改了什么)附在给用户的方案汇报里。
15
+
16
+ ## apply 写任何代码之前
17
+
18
+ 产物可能已被用户改过;用户已经看过,所以这里不静默修改。
19
+
20
+ 1. 运行命令表的「判断」。
21
+ 2. 任一 block:停下不碰代码,逐条报告规则与原文,等用户决定。
22
+ 3. 只有 review:用一小段列出来,然后继续。
23
+
24
+ ## apply 最后一个任务做完、汇报完成之前
25
+
26
+ 按顺序:
27
+
28
+ 1. 按下一节「自动精简与代码审查」,先精简本次改动,再独立审查;自动修复 block 与范围内可修的 review,并重新审查。
29
+ 2. 运行命令表的「ruff」,修掉报出的问题后重跑,直到通过。
30
+ 3. 运行命令表的「测试」(不联网部分);改了判断规则文件或 `JEV_MODEL` 时,加跑命令表的「判断回归」;命令表没有这一行时跳过,并在汇报中写明。
31
+ 4. 改了前端时,运行命令表的「前端 lint」并修掉问题。
32
+ 5. 改了界面文案时,运行命令表的「文案检查」:block 必须修,review 列出。
33
+ 6. 运行项目列出的额外步骤。
34
+ 7. 写完成汇报:逐项写每一步的结果,含跳过了什么、为什么;对照 design 里各文件的估计行数,超出一倍的写明原因。汇报写进 change 的 `verification.md`,再在对话里给出摘要。
35
+
36
+ ## 自动精简与代码审查(apply 后)
37
+
38
+ 上一节第 1 步的细则:先对本次 diff 执行行为保持的自动精简,再执行独立代码审查;整个过程不等待人工逐条审核。
39
+
40
+ **自动精简**只处理本次变更中的重复、死代码、冗余抽象或过复杂逻辑,必须保持需求、外部可观察行为与 API 不变,不得为减少行数重写无关代码;无法安全精简时保持原样并说明。
41
+
42
+ **独立审查**的审查者必须独立于实现者,只分析实现者提供的材料:工具能提供所有工具权限均拒绝的审查子代理时,材料放在交给它的消息里;否则用新会话、单独复审轮次,或只能读一份文件的子代理,由实现者把材料写成一份文件,审查者只读这份文件。审查者不读仓库其他文件、不运行命令、不修改文件。实现者必须提供本次 diff、未跟踪文件清单、change 产物及受影响测试 / 配置的相关内容,不得提供密钥、凭证或用户未提交的无关内容。材料不完整时,审查者报告缺少什么,由实现者筛选后补充。
43
+
44
+ 审查范围:本次 diff 与未跟踪的新文件、change 的 proposal / design / tasks / specs、受影响的测试与配置的相关内容。输入中若意外包含敏感内容,只报告文件与位置,不引用或复制值。
45
+
46
+ 检查项:
47
+
48
+ - **需求符合**:diff 覆盖 tasks / specs,没有范围外改动。
49
+ - **正确性**:边界、错误处理、返回值、回归风险。
50
+ - **安全**:密钥、注入、路径穿越、危险 shell、外部副作用。
51
+ - **代码质量**:九条、重复、复杂度、死代码、注释与命名。
52
+ - **测试**:新增行为有覆盖;现有测试在审查后的验证步骤运行,审查者检查覆盖是否充分。
53
+ - **变更卫生**:无无关文件、无调试输出、无注释掉的代码。
54
+
55
+ 每项检查写 `pass`(通过且无发现),或列出 `block` / `review` 发现,附文件与行、原因和建议。`block` 和范围内可修的 `review` 由实现者自动修复,然后重新运行审查,最多两轮:
56
+
57
+ - 第二轮后仍有 block:停止实现,报告未消除项。
58
+ - 第二轮只剩 review:修复后跑完 ruff 与测试即可,不再审第三轮,修了什么写进汇报。
59
+ - 修复需要改变方案、规格或扩大范围时,才停下报告原因;不把逐条代码审核转交用户。
60
+
61
+ 审查结果与修复摘要写进最终汇报。
62
+
63
+ ## 提交信息
64
+
65
+ 提交信息写成「类型(范围): 中文说明(change 名)」,类型取 `feat`、`fix`、`refactor`、`docs`、`chore` 之一,例如「feat(cli): 新增卸载命令(remove)」。归档提交写「chore: 归档 <change>,规格并入主 specs(…)」。与 change 无关的提交可以省略括号里的 change 名。
66
+
67
+ ## 没有判断层的项目
68
+
69
+ 只执行「apply 最后一个任务做完」中不依赖 Jev 的步骤(自动精简、自动代码审查、ruff、测试、前端 lint、额外步骤)。文案检查依赖 Jev,汇报中写明「判断层未装」并跳过;方案刚写完与写代码前两个检查点同样不执行判断。
70
+
71
+ ## 可选:阶段学习文档同步 Notion
72
+
73
+ 项目指令写明 Notion 落点页面才启用;没写就不启用,汇报中也不提。
74
+
75
+ 启用后,每完成一个阶段(一个产物写完,或一段 apply 完成),主动在落点下新建下一篇编号子页,无需用户提醒。写给零基础读者:做了什么、为什么这样做、要敲的命令、应该看到什么;注明仓库是权威记录。
@@ -0,0 +1,88 @@
1
+ ---
2
+ name: rulemason
3
+ description: 在已运行 rulemason init 的项目里补齐需要理解项目的部分(项目简介、红线、命令表确认、旧名残留),解读 rulemason check 的结果,或在用户要求时启用判断。用户要接入、补全、巡检 rulemason 或启用判断时使用。
4
+ ---
5
+
6
+ # /rulemason:补项目信息、巡检与启用判断
7
+
8
+ rulemason 管三件事:**规则**(写法规矩)、**检查点**(方案与代码前后的自动检查)、**判断**(用 Jev 判断方案是否违反规矩,可选)。共享规则只在 rulemason 仓库编辑;接入(生成规则副本、工具入口、基础配置)以及从 rulekit 0.5 迁移都由 `rulemason init` 完成,升级与补齐由 `rulemason update` 完成。本 skill 只做需要理解项目的事。
9
+
10
+ ## 不可违反
11
+
12
+ - **先识别后动手**:第 1 步 `rulemason check --json` 的 `next.code` 决定做什么,不凭印象判断阶段,也不按文字措辞匹配。
13
+ - **一次只做一件事**:做之前列出要新增、修改的文件,用户同意后才写。
14
+ - **红线与判断规则由用户确认**:候选要注明出处(README、`openspec/config.yaml` 的 context、某个已归档方案);没确认的不写。
15
+ - **不改 rulemason 标记块**:`<!-- rulemason:… -->` 与 `<!-- /rulemason:… -->` 之间的内容、`.rulemason/` 与各工具的 skill 副本都是生成的,由 `rulemason update` 刷新,本 skill 不改。
16
+ - **不提交、不推送**:工作区里用户已有的改动一律不碰;汇报末尾列出本次改了哪些文件。
17
+ - **巡检只报告**:日常巡检不改任何文件。
18
+
19
+ ## 1. 识别
20
+
21
+ 1. 先确认本机有 `rulemason` 命令,且 `rulemason --version` 不低于项目 `.rulemason.yaml` 里 `rulemason:` 记录的版本;没有命令或版本更低时,给出安装命令 `uv tool install rulemason`(已装旧版本时用 `uv tool upgrade rulemason`;需要与清单记录的版本一致时用 `uv tool install --force rulemason==<版本>`;本机还装着旧的 `rulekit` 时先 `uv tool uninstall rulekit`),然后停下。
22
+ 2. 在项目根目录(或任意子目录)运行 `rulemason check --json`,把 `text`(报告全文)原样放进汇报;`next.action` 是给人看的下一步,`next.code` 决定这次做什么。命令以非 0 退出且输出不是 JSON 时(例如「rulemason check 失败:openspec/config.yaml 不是合法的 YAML…」),原样转述这条提示,请用户修正对应文件,然后停下:
23
+
24
+ | `next.code` | 这次做什么 |
25
+ |---|---|
26
+ | `init`、`migrate` | 提示用户在项目根运行 `rulemason init`(没有 `openspec/` 时它会先询问;rulekit 0.5 项目会列出迁移计划并确认一次)。停下,项目已归档再多 change 也一样。用户要你代为接入时,先运行 `rulemason init --plan` 把计划给用户看(不写任何文件),用户同意并选定工具后再运行 `rulemason init --tools <所选> --yes` |
27
+ | `fix_manifest` | 说明清单读不出来的原因(见 `layers` 的「接入 工具入口」),请用户修正,或删掉后重新 `rulemason init`。停下 |
28
+ | `legacy` | 说明项目仍引用同级目录的 rulekit 仓库,需要先迁移。停下 |
29
+ | `fix_gitignore` | 列出要改的 `.gitignore` 规则(只放行 `skills/rulemason/`,说明单独写一行),用户确认后修改,再运行 `rulemason check --json` |
30
+ | `update` | 提示用户运行 `rulemason update`;可先用 `rulemason update --dry-run` 给用户看计划。它列出「补齐计划」时,由用户决定是否 `rulemason update --yes`。停下 |
31
+ | `fix_residue` | 第 2 步的第 0 项(旧名残留) |
32
+ | `fill_project_info` | 第 2 步 |
33
+ | `upgrade_machine` | 本机 rulemason 比项目记录的旧:按上面第 1 条给出安装命令。停下 |
34
+ | `upgrade_copies`、`none` | 第 3 步;用户说要启用判断时做第 4 步 |
35
+
36
+ ## 2. 补项目信息
37
+
38
+ 先列文件计划(要新增、修改的文件,修改写明改哪一段;「其余 N 个未提交改动不变」),用户同意后再写。
39
+
40
+ 0. **旧名残留**(check 的「旧名残留」有内容时先做):逐处列出文件、原文与替换后的内容,用户确认后只改这些地方:
41
+ - `jev-judge --change <name>` → `rulemason judge --change <name>`,`jev-judge --copy` → `rulemason judge --copy`
42
+ - `.rulekit/` → `.rulemason/`
43
+ - `skills/rulekit` → `skills/rulemason`(如 `.gitignore` 的 `!.claude/skills/rulekit/`)
44
+ - 项目测试里的 `from jev_judge import …` 不在 check 的检查范围,但同样改为 `from rulemason.judge import …`,一并列进计划。
45
+
46
+ 改完运行 `rulemason check`,确认「旧名残留」为「一致」。项目简介与红线已补过的,做完这一项就结束本次。
47
+ 1. **项目简介**:按 README 与代码写一两句话,替换 `AGENTS.md` 中「待 /rulemason 补充」的简介。
48
+ 2. **红线候选**:从 README、`openspec/config.yaml` 的 `context` 里找出「绝对不能做的事」,每条注明出处,逐条请用户确认、改写或删除;没有找到也要问用户一句。确认的写进「## 红线(不可违反)」;`context` 本身不改。
49
+ 3. **样板与豁免**:在 Code style 标记块之后写本项目样板文件路径、语言落法位置与 ruff 豁免文件。
50
+ 4. **命令表**:向用户展示「本项目命令表(自动识别,请确认)」中的命令,按用户确认或修改后的内容写入,并去掉「自动识别,请确认」。判断与文案检查保持「判断层未装」。
51
+ 5. **项目工作流规格**:init 或 update 汇报 `openspec/specs/workflow-gates/spec.md` 需要合并时,检查是否已有「Apply 后自动精简并审查」或语义等价的 requirement;缺失则按 rulemason 的 workflow-gates 规格模板合并该 requirement,已有但漏了关键行为只补缺失内容,完整时不重复添加。保留项目的 Purpose、其他 requirements 与 scenarios,规格只引用 `.rulemason/rules/workflow-gates.md`,MUST NOT 复制共享检查点正文。结构异常无法安全合并时停下并报告。
52
+ 6. **验证**(结果写进汇报):运行命令表里的 ruff 与测试,报出的问题列进汇报,**不自动修代码**;再运行一次 `rulemason check --json`,确认 `next.code` 不再是 `fill_project_info`。
53
+ 7. 提醒用户:在本项目开一次交互会话,问「九条第 9 条是什么」,确认所选工具已加载共享规则。
54
+
55
+ ## 3. 日常巡检
56
+
57
+ 只读,不改文件。按 `rulemason check` 的结果逐项解读:一致的写「一致」;有偏离的写出文件、内容和建议怎么改(由用户决定改不改)。
58
+
59
+ - 生成副本或标记块被手改:建议把改动搬到 rulemason 仓库,再运行 `rulemason update` 恢复。
60
+ - 版本落后:建议运行 `rulemason update`,再在项目里跑一遍 lint 与测试。
61
+ - 要增减工具:建议运行 `rulemason init --tools <id,...>`,它会先列出受影响文件并等确认;取消的工具会连同入口一起删除。
62
+ - 识别表「判断」一行写「可选,未启用」时,说明启用条件;用户没有提出要启用就不做第 4 步。
63
+
64
+ ## 4. 启用判断(可选,用户提出时才做)
65
+
66
+ 判断用 Jev 检查方案是否违反已写下的规矩。前提:项目在 git 仓库里,已归档至少 3 个 change。项目没有 git 时,说明判断层需要 git(文案检查要与主分支比较),停下,不生成判断层文件。
67
+
68
+ **4.1 整理判断规则**
69
+
70
+ 1. 读 `openspec/changes/archive/` 下各 change 的 `proposal.md` 与 `design.md`,找出反复出现、且能指回已写下规矩(`AGENTS.md` 某个二级标题、`config.yaml` 的 rules 或 context、某份规格)的问题。
71
+ 2. 每条候选写:规则 id(kebab-case)、要问 Jev 的一句正向陈述、`expect`、`enforces`、出现在哪几个 change。两条通用规则(写明不做什么、不做过度设计)引擎已自带,「决策附备选」由 `lint/design` 检查,都不列。
72
+ 3. 已归档不足 3 个时,写明还差几个,停下。
73
+ 4. 候选只写在汇报里,不建文件;等用户逐条确认。
74
+
75
+ **4.2 启用判断**(用户确认了至少一条候选规则后)
76
+
77
+ 模板随安装包提供,用 `rulemason judge --template <名称>` 取得,项目里没有模板副本。
78
+
79
+ 1. `openspec/judgments.yaml`:以 `rulemason judge --template judgments` 的输出为起点,只写用户确认的规则;红线规则 `thresholds` 的 block 起始为 0(只 review 不拦),文件里注明。
80
+ 2. `openspec/judge.yaml`:以 `rulemason judge --template judge` 为起点;项目有界面文案才写 `copy` 段(嵌套 JSON 用 `json`,HTML 内 `const I18N` 用 `js-i18n` 加 `lang`),有项目禁词才写 `banned`。
81
+ 3. 回归用例骨架:以 `rulemason judge --template judge_cases` 为起点,放在项目测试目录;每条确认的规则至少列出一个待人工标注的用例位置。
82
+ 4. `AGENTS.md` 命令表中的判断与文案检查改为 `rulemason judge --change <name>`、`rulemason judge --copy`,并加一行「判断回归」,填运行回归用例的命令(命令表在标记块之外)。
83
+ 5. key:本机没有 key(`~/.config/rulemason/env` 与旧位置 `~/.config/jev-judge/env` 都没有)时,提示用户在自己的终端运行 `rulemason key`,交互输入;**不代填 key**,也不在会话里处理 key。没有 key 时判断以人工模式运行。
84
+ 6. **验证**:`rulemason judge --change <一个已归档 change> --lint-only` 不报 enforces 指向不存在;有 key 时再跑一次不带 `--lint-only` 的,结果写进汇报。
85
+
86
+ ## 5. 汇报
87
+
88
+ 按顺序:`rulemason check --json` 的 `text` → 本次做了什么 → 每项验证命令与结果 → 本次新增、修改的文件清单 → 没碰的东西(未提交改动数)→ 下一步(以 `next.action` 为准)。
rulemason/banner.py ADDED
@@ -0,0 +1,98 @@
1
+ """启动画面:交互运行 init 或不带参数运行 rulemason 时,显示字标、版本、口号与三件事。"""
2
+ from __future__ import annotations
3
+
4
+ import os
5
+ import shutil
6
+ import sys
7
+
8
+ from rulemason import __version__
9
+
10
+ MIN_WIDTH = 60 # 窄于这个宽度不画字标,避免折行
11
+ # 配色仿 openspec:一个强调色(天空蓝)点字标、标签、命令与最后的提示;正文与命令说明默认白,版本号与 init 说明用暗色
12
+ SKY = "38;5;117" # 256 色的天空蓝(SkyBlue1)
13
+ SKY_BOLD = "1;38;5;117"
14
+ DIM = "2"
15
+ BOLD = "1"
16
+
17
+ # 半格块字形,每个字母 3 行
18
+ GLYPHS = {
19
+ "R": ["█▀▀█", "█▀▀▄", "▀ ▀"],
20
+ "U": ["█ █", "█ █", "▀▀▀▀"],
21
+ "L": ["█ ", "█ ", "▀▀▀▀"],
22
+ "E": ["█▀▀▀", "█▀▀ ", "▀▀▀▀"],
23
+ "M": ["█▀▄▀█", "█ █", "▀ ▀"],
24
+ "A": ["█▀▀█", "█▀▀█", "▀ ▀"],
25
+ "S": ["█▀▀▀", "▀▀▀█", "▀▀▀▀"],
26
+ "O": ["█▀▀█", "█ █", "▀▀▀▀"],
27
+ "N": ["█▀▀▄", "█ █", "▀ ▀"],
28
+ }
29
+ SLOGAN = "AI 写代码,也讲章法"
30
+ THREE_THINGS = (
31
+ ("读", "写之前,先读项目的规则"),
32
+ ("守", "写的时候,方案和代码按规则来"),
33
+ ("查", "写完以后,lint、测试、审查自动跑"),
34
+ )
35
+ # 结尾两种:每行是若干 (文字, 颜色) 段,颜色为 None 表示默认色
36
+ INIT_FOOTER = (
37
+ (("这次会:选你用的 AI 工具,在项目里放好规则和入口,补齐 lint 与方案写法。", DIM),),
38
+ (("先列计划,你确认后才写入。", SKY),),
39
+ )
40
+ COMMANDS = (
41
+ ("rulemason init", "初始化"),
42
+ ("rulemason check", "检查初始化状态,不改文件"),
43
+ ("rulemason update", "同步新版规则和配置"),
44
+ ("rulemason --help", "查看全部命令"),
45
+ )
46
+ _CMD_WIDTH = max(len(cmd) for cmd, _ in COMMANDS) + 2 # 最长命令后空两格对齐说明
47
+ COMMANDS_FOOTER = (
48
+ (("常用命令:", None),),
49
+ *(((" ", None), (cmd.ljust(_CMD_WIDTH), SKY), (desc, None)) for cmd, desc in COMMANDS),
50
+ )
51
+
52
+
53
+ def _paint(text: str, code: str | None, color: bool) -> str:
54
+ if code is None or not color:
55
+ return text
56
+ return f"\033[{code}m{text}\033[0m"
57
+
58
+
59
+ def _word(word: str, row: int) -> str:
60
+ return " ".join(GLYPHS[c][row] for c in word)
61
+
62
+
63
+ def render(version: str, footer: tuple, width: int, color: bool, unicode_ok: bool) -> str:
64
+ """拼出整屏文字;width 不够或写不出块字符时,字标换成一行「rulemason 版本」。"""
65
+ # 1. 字标与版本号,口号紧贴其下
66
+ if width >= MIN_WIDTH and unicode_ok:
67
+ lines = [_paint(_word("RULEMASON", i), SKY, color) for i in range(3)]
68
+ lines[-1] += " " + _paint(version, DIM, color)
69
+ else:
70
+ lines = [_paint("rulemason", SKY_BOLD, color) + " " + _paint(version, DIM, color)]
71
+ lines.append(_paint(SLOGAN, BOLD, color))
72
+ # 2. 三件事
73
+ lines += ["", "只管三件事:"]
74
+ lines += [f" {_paint(label, SKY_BOLD, color)} {text}" for label, text in THREE_THINGS]
75
+ # 3. 本次说明或常用命令
76
+ lines.append("")
77
+ lines += ["".join(_paint(text, code, color) for text, code in segments) for segments in footer]
78
+ return "\n".join(lines)
79
+
80
+
81
+ def interactive() -> bool:
82
+ """标准输入与输出都是终端;输出被管道接走时不显示,免得颜色转义进日志。"""
83
+ return sys.stdin.isatty() and sys.stdout.isatty()
84
+
85
+
86
+ def _unicode_ok() -> bool:
87
+ try:
88
+ "█▀▄".encode(sys.stdout.encoding or "ascii")
89
+ except UnicodeEncodeError:
90
+ return False
91
+ return True
92
+
93
+
94
+ def show(footer: tuple) -> None:
95
+ """按当前终端的颜色、宽度与编码打印启动画面。"""
96
+ color = not os.environ.get("NO_COLOR")
97
+ width = shutil.get_terminal_size().columns
98
+ print("\n" + render(__version__, footer, width, color, _unicode_ok()) + "\n") # 上下各留一行,和提示符隔开