Move Nexa contract checker to standalone skill
This commit is contained in:
@@ -10,8 +10,6 @@ The skill is simplified-Chinese-first:
|
|||||||
|
|
||||||
The source PDF is not included in this repository.
|
The source PDF is not included in this repository.
|
||||||
|
|
||||||
This repository also includes a Nexa BaZi contract checker for programmers. It catches mixed API fields, JSON schema names, prompt variables, and module keys such as `Day_Master`, `DayMaster_Stem`, `Dominant_Deity`, and `Primary_Favorite_Element`.
|
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
Clone the repository and copy it into Codex skills:
|
Clone the repository and copy it into Codex skills:
|
||||||
@@ -46,13 +44,7 @@ Use $bazi-translation to translate these terms into approved English and code na
|
|||||||
Use $bazi-translation to review API field names in packages/bazi.
|
Use $bazi-translation to review API field names in packages/bazi.
|
||||||
```
|
```
|
||||||
|
|
||||||
For Nexa:
|
## Use The Checker
|
||||||
|
|
||||||
```text
|
|
||||||
Use $bazi-translation to review Nexa BaZi API fields, prompt variables, and module keys in nexa_bazi-main.
|
|
||||||
```
|
|
||||||
|
|
||||||
## Use The Generic Checker
|
|
||||||
|
|
||||||
Run the local checker against files or directories:
|
Run the local checker against files or directories:
|
||||||
|
|
||||||
@@ -80,47 +72,6 @@ src/chart.ts:42: dayOwner -> dayMaster
|
|||||||
line: const dayOwner = chart.dayOwner;
|
line: const dayOwner = chart.dayOwner;
|
||||||
```
|
```
|
||||||
|
|
||||||
## Use The Nexa Contract Checker
|
|
||||||
|
|
||||||
Run the Nexa-specific checker against code, JSON, prompt files, or docs:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python ~/.codex/skills/bazi-translation/scripts/check_nexa_bazi_contract.py <path>
|
|
||||||
```
|
|
||||||
|
|
||||||
Migration planning mode:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python ~/.codex/skills/bazi-translation/scripts/check_nexa_bazi_contract.py <path> --no-fail
|
|
||||||
```
|
|
||||||
|
|
||||||
CI-style blocking mode, reporting only hard errors:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python ~/.codex/skills/bazi-translation/scripts/check_nexa_bazi_contract.py <path> --min-severity error
|
|
||||||
```
|
|
||||||
|
|
||||||
List loaded Nexa rules:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
python ~/.codex/skills/bazi-translation/scripts/check_nexa_bazi_contract.py --list-rules
|
|
||||||
```
|
|
||||||
|
|
||||||
Example output:
|
|
||||||
|
|
||||||
```text
|
|
||||||
5 八字详解/5.7 命理符号/2.命理符号-算法与判定规则.md:14: DayMaster_Stem -> day_master_stem [error]
|
|
||||||
concept: 日主天干
|
|
||||||
reason: mixed case 不适合接口契约
|
|
||||||
line: DayMaster_Stem: "辛"
|
|
||||||
```
|
|
||||||
|
|
||||||
Severity handling:
|
|
||||||
|
|
||||||
- `error`: do not add in new code; rename or add a compatibility migration.
|
|
||||||
- `warn`: likely wrong for new BaZi fields; check whether it is already published.
|
|
||||||
- `review`: not automatically unsafe, but it may show cross-system leakage or a published key that needs migration planning.
|
|
||||||
|
|
||||||
## Core Naming Rules
|
## Core Naming Rules
|
||||||
|
|
||||||
- Use `dayMaster`, not `dayOwner` or `dayLord`.
|
- Use `dayMaster`, not `dayOwner` or `dayLord`.
|
||||||
@@ -130,24 +81,10 @@ Severity handling:
|
|||||||
- Use `usefulGod`, not `useGod`.
|
- Use `usefulGod`, not `useGod`.
|
||||||
- Use `favorableElement` in code by default; keep `Favourable Element` for book-style display text.
|
- Use `favorableElement` in code by default; keep `Favourable Element` for book-style display text.
|
||||||
- Keep `Useful God` separate from `Favourable Element`.
|
- Keep `Useful God` separate from `Favourable Element`.
|
||||||
- For Nexa JSON/API/prompt fields, use `snake_case`: `day_master`, `day_master_stem`, `dominant_ten_god`, `primary_favorite_element`.
|
|
||||||
- Do not use `deity` for Ten Gods in interfaces. Use `ten_god`.
|
|
||||||
- Do not use astrology terms such as `astral_event` for BaZi branch relationships or daily BaZi events.
|
|
||||||
|
|
||||||
## Nexa Contract Files
|
|
||||||
|
|
||||||
- `references/nexa-bazi-contract.md`: canonical Nexa BaZi API/JSON/prompt naming contract.
|
|
||||||
- `references/nexa-naming-rules.md`: practical naming rules and migration guidance.
|
|
||||||
- `references/nexa-forbidden-names.md`: deterministic bad-name table used by the checker.
|
|
||||||
|
|
||||||
## Files
|
## Files
|
||||||
|
|
||||||
- `SKILL.md`: Codex skill trigger and workflow.
|
- `SKILL.md`: Codex skill trigger and workflow.
|
||||||
- `references/terms.md`: simplified-Chinese-first dictionary.
|
- `references/terms.md`: simplified-Chinese-first dictionary.
|
||||||
- `references/naming-rules.md`: review rules and anti-patterns.
|
- `references/naming-rules.md`: review rules and anti-patterns.
|
||||||
- `references/nexa-bazi-contract.md`: Nexa BaZi interface and prompt-variable naming contract.
|
|
||||||
- `references/nexa-naming-rules.md`: Nexa-specific naming guidance.
|
|
||||||
- `references/nexa-forbidden-names.md`: Nexa forbidden/legacy name table.
|
|
||||||
- `references/pending-terms.md`: staging area for terms needing domain review.
|
- `references/pending-terms.md`: staging area for terms needing domain review.
|
||||||
- `scripts/check_bazi_names.py`: source-code naming checker.
|
- `scripts/check_bazi_names.py`: source-code naming checker.
|
||||||
- `scripts/check_nexa_bazi_contract.py`: Nexa BaZi API/JSON/prompt naming checker.
|
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
---
|
---
|
||||||
name: bazi-translation
|
name: bazi-translation
|
||||||
description: Simplified-Chinese-first BaZi/Four Pillars terminology dictionary and deterministic code/interface naming review workflow, including a Nexa BaZi system contract. Use when translating Chinese BaZi, Four Pillars, Chinese metaphysics, destiny analysis, Heavenly Stem/Earthly Branch, Ten Gods, Five Factors, luck cycle, or favorable-element terms into English; naming code identifiers, classes, files, API fields, database columns, JSON schema fields, prompt variables, UI labels, or documentation; or reviewing code for inconsistent or literal translations such as dayOwner, skyStem, groundBranch, fortuneColumn, useGod, Day_Master, Dominant_Deity, Primary_Favorite_Element, or mixed BaZi vocabulary.
|
description: Simplified-Chinese-first BaZi/Four Pillars terminology dictionary and code naming review workflow. Use when translating Chinese BaZi, Four Pillars, Chinese metaphysics, destiny analysis, Heavenly Stem/Earthly Branch, Ten Gods, Five Factors, luck cycle, or favorable-element terms into English; naming code identifiers, classes, files, API fields, database columns, UI labels, or documentation; or reviewing code for inconsistent or literal translations such as dayOwner, skyStem, groundBranch, fortuneColumn, useGod, or mixed BaZi vocabulary.
|
||||||
---
|
---
|
||||||
|
|
||||||
# BaZi Translation
|
# BaZi Translation
|
||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
Use this skill to keep BaZi terminology readable and consistent across code, UI labels, comments, docs, schemas, prompt variables, and tests.
|
Use this skill to keep BaZi terminology readable and consistent across code, UI labels, comments, docs, schemas, and tests.
|
||||||
|
|
||||||
The source of truth is simplified Chinese first. Use the approved English display term for user-facing text, and use the approved ASCII code name for identifiers unless an existing project convention is stronger.
|
The source of truth is simplified Chinese first. Use the approved English display term for user-facing text, and use the approved ASCII code name for identifiers unless an existing project convention is stronger.
|
||||||
|
|
||||||
@@ -23,16 +23,8 @@ python ~/.codex/skills/bazi-translation/scripts/check_bazi_names.py <path>
|
|||||||
|
|
||||||
Use `--no-fail` for exploratory review and `--list-approved` to print approved code names.
|
Use `--no-fail` for exploratory review and `--list-approved` to print approved code names.
|
||||||
|
|
||||||
4. For Nexa BaZi code, APIs, JSON schemas, prompt variables, and module keys, read `references/nexa-bazi-contract.md`, `references/nexa-naming-rules.md`, and `references/nexa-forbidden-names.md`, then run:
|
4. Report naming findings as concrete replacements: current name, approved name, reason, and file/line when available.
|
||||||
|
5. When a concept is not in `terms.md`, do not invent a final translation. Add or propose a row in `references/pending-terms.md` with context and a provisional English/code name.
|
||||||
```bash
|
|
||||||
python ~/.codex/skills/bazi-translation/scripts/check_nexa_bazi_contract.py <path>
|
|
||||||
```
|
|
||||||
|
|
||||||
Use `--no-fail` during migration planning and `--min-severity error` for CI-style blocking checks.
|
|
||||||
|
|
||||||
5. Report naming findings as concrete replacements: current name, approved name, reason, severity, and file/line when available.
|
|
||||||
6. When a concept is not in `terms.md` or the Nexa contract, do not invent a final translation. Add or propose a row in `references/pending-terms.md` with context and a provisional English/code name.
|
|
||||||
|
|
||||||
## Standards
|
## Standards
|
||||||
|
|
||||||
@@ -43,16 +35,10 @@ Use `--no-fail` during migration planning and `--min-severity error` for CI-styl
|
|||||||
- Keep Chinese, English display text, and code names separate. Do not force one field to serve all audiences.
|
- Keep Chinese, English display text, and code names separate. Do not force one field to serve all audiences.
|
||||||
- Do not conflate `用神` (`Useful God`, advanced concept) with `喜用元素` or `有利元素` (`Favourable Element`, beginner balancing element).
|
- Do not conflate `用神` (`Useful God`, advanced concept) with `喜用元素` or `有利元素` (`Favourable Element`, beginner balancing element).
|
||||||
- Do not translate classical terms word by word when the approved table has a domain term.
|
- Do not translate classical terms word by word when the approved table has a domain term.
|
||||||
- For programmer-facing Nexa contracts, prefer deterministic rules over vector/semantic matching. Vector search may help discover candidates, but it must not decide final names.
|
|
||||||
- In Nexa JSON/API fields, prefer `snake_case`; reserve PascalCase for type names and camelCase for language-local code when the host language expects it.
|
|
||||||
|
|
||||||
## Resources
|
## Resources
|
||||||
|
|
||||||
- `references/terms.md`: approved simplified-Chinese-first dictionary.
|
- `references/terms.md`: approved simplified-Chinese-first dictionary.
|
||||||
- `references/naming-rules.md`: code naming rules, anti-patterns, and review checklist.
|
- `references/naming-rules.md`: code naming rules, anti-patterns, and review checklist.
|
||||||
- `references/nexa-bazi-contract.md`: Nexa BaZi interface and prompt-variable naming contract.
|
|
||||||
- `references/nexa-naming-rules.md`: Nexa-specific naming rules for APIs, JSON, module keys, and prompt files.
|
|
||||||
- `references/nexa-forbidden-names.md`: deterministic legacy/bad-name table used by the Nexa checker.
|
|
||||||
- `references/pending-terms.md`: staging area for terms needing domain review.
|
- `references/pending-terms.md`: staging area for terms needing domain review.
|
||||||
- `scripts/check_bazi_names.py`: scans source files for forbidden or suspicious BaZi translations and suggests approved names.
|
- `scripts/check_bazi_names.py`: scans source files for forbidden or suspicious BaZi translations and suggests approved names.
|
||||||
- `scripts/check_nexa_bazi_contract.py`: scans Nexa code/docs for mixed interface names and suggests contract names.
|
|
||||||
|
|||||||
@@ -1,70 +0,0 @@
|
|||||||
# Nexa 八字命名契约
|
|
||||||
|
|
||||||
本文件是 Nexa 八字系统的程序员命名契约。目标不是翻译给用户看,而是让 API、JSON、prompt 变量、模块 key、测试数据和文档中的八字命理词汇保持一致。
|
|
||||||
|
|
||||||
## 适用范围
|
|
||||||
|
|
||||||
- Provider / BFF / frontend API 字段。
|
|
||||||
- JSON schema、测试 fixture、调试数据。
|
|
||||||
- Prompt 变量、Context Vault、fallback schema。
|
|
||||||
- 模块 key、SSE path、registry key。
|
|
||||||
- 代码中的函数、变量、类名和枚举名。
|
|
||||||
|
|
||||||
## 分层原则
|
|
||||||
|
|
||||||
| 层级 | 命名风格 | 示例 | 说明 |
|
|
||||||
|---|---|---|---|
|
|
||||||
| API / JSON 字段 | `snake_case` | `day_master`, `ten_god_label` | 对外和跨语言最稳定 |
|
|
||||||
| 模块 key / registry key | `snake_case` | `daily_overview`, `monthly_luck_tag` | 已发布 key 需要迁移策略 |
|
|
||||||
| Python 字段/变量 | `snake_case` | `day_master_stem` | 跟 Python 社区一致 |
|
|
||||||
| JS/TS 局部变量 | `camelCase` | `dayMasterStem` | 仅限局部代码,不用于 JSON contract |
|
|
||||||
| Type / Class / Enum | `PascalCase` | `HeavenlyStem`, `EarthlyBranch`, `TenGod` | 类型名 |
|
|
||||||
| UI 中文 | 简体中文 | `日主`, `十神`, `流日` | 面向用户 |
|
|
||||||
| 英文显示名 | 标准术语 | `Day Master`, `Ten Gods`, `Annual Luck` | 面向英文 UI/文档 |
|
|
||||||
|
|
||||||
## Nexa 推荐字段
|
|
||||||
|
|
||||||
| 中文概念 | 推荐 API/JSON 字段 | Type/Enum | 说明 |
|
|
||||||
|---|---|---|---|
|
|
||||||
| 八字命盘 | `bazi_chart` | `BaziChart` | 完整排盘结构 |
|
|
||||||
| 四柱 | `pillars` | `Pillars` | 包含 year/month/day/hour |
|
|
||||||
| 年柱 | `year_pillar` | `Pillar` | 不要写 `year_column` |
|
|
||||||
| 月柱 | `month_pillar` | `Pillar` | 不要写 `month_column` |
|
|
||||||
| 日柱 | `day_pillar` | `Pillar` | 不要写 `day_column` |
|
|
||||||
| 时柱 | `hour_pillar` | `Pillar` | 不要写 `time_column` |
|
|
||||||
| 日主 | `day_master` | `DayMaster` | 可表示完整日主,如 `辛金` |
|
|
||||||
| 日主天干 | `day_master_stem` | `HeavenlyStem` | 只表示天干,如 `辛` |
|
|
||||||
| 天干 | `heavenly_stem` | `HeavenlyStem` | JSON 字段用 snake_case |
|
|
||||||
| 地支 | `earthly_branch` | `EarthlyBranch` | JSON 字段用 snake_case |
|
|
||||||
| 藏干 | `hidden_stems` | `HiddenStem` | list 字段用复数 |
|
|
||||||
| 十神 | `ten_god` | `TenGod` | 单个十神 |
|
|
||||||
| 十神 ID | `ten_god_label` | `TenGodLabel` | 若已上线为数字 ID,可保留但要写清映射 |
|
|
||||||
| 主导十神 | `dominant_ten_god` | `TenGod` | 不要写 `dominant_deity` |
|
|
||||||
| 五行 | `element` / `five_elements` | `FiveElement` | 单个用 `element`,集合用 `five_elements` |
|
|
||||||
| 五行强度 | `element_strengths` | `ElementStrengths` | map: wood/fire/earth/metal/water |
|
|
||||||
| 喜用元素 | `favorite_element` | `FiveElement` | Nexa 内部可用 favorite;若对齐通用词表可映射为 favorable |
|
|
||||||
| 第一喜用元素 | `primary_favorite_element` | `FiveElement` | 不要写 `Primary_Favorite_Element` |
|
|
||||||
| 第二喜用元素 | `secondary_favorite_element` | `FiveElement` | 不要写 `Secondary_Favorite_Element` |
|
|
||||||
| 用神 | `useful_god` | `UsefulGod` | 高阶概念,不等同喜用元素 |
|
|
||||||
| 大运 | `luck_pillar` | `LuckPillar` | 十年运柱 |
|
|
||||||
| 流年 | `annual_luck` | `AnnualLuck` | 避免和本命年柱混淆 |
|
|
||||||
| 流月 | `monthly_luck` | `MonthlyLuck` | 月运 |
|
|
||||||
| 流日 | `daily_luck` | `DailyLuck` | 日运 |
|
|
||||||
| 流日干支 | `flow_day_ganzhi` | `StemBranch` | 若已上线可保留;新字段建议 `daily_stem_branch` |
|
|
||||||
| 合冲刑害破 | `branch_relationship` | `BranchRelationship` | 不要混用 astrology/astral 命名 |
|
|
||||||
| 合盘 | `bazi_synastry` | `BaziSynastry` | 与占星 synastry 区分时加 `bazi_` 前缀 |
|
|
||||||
|
|
||||||
## 已发布字段处理
|
|
||||||
|
|
||||||
已发布 API 或前端依赖字段不能无迁移直接改名。处理顺序:
|
|
||||||
|
|
||||||
1. 标记为 `compat`:旧字段继续读取。
|
|
||||||
2. 新增推荐字段:例如同时返回 `day_master_stem`。
|
|
||||||
3. 在文档中声明旧字段 deprecated。
|
|
||||||
4. 前端/BFF 迁移完成后,再删除旧字段。
|
|
||||||
|
|
||||||
审查时不要只说“改名”。要说明影响面:API、fixture、prompt、前端 path、SSE key、数据库列、测试断言。
|
|
||||||
|
|
||||||
## 不做向量判断
|
|
||||||
|
|
||||||
本契约用于程序员纠错,必须可重复、可解释、可进入 CI。不要用向量相似度决定字段是否违规。向量搜索最多用于发现候选混名,最终仍必须落回本契约或 `nexa-forbidden-names.md`。
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
# Nexa 禁用/兼容命名表
|
|
||||||
|
|
||||||
本表供 `scripts/check_nexa_bazi_contract.py` 读取。`error` 表示新代码不应出现;`warn` 表示需要迁移或人工确认;`review` 表示可能是已发布兼容字段,改动前必须查影响面。
|
|
||||||
|
|
||||||
| severity | current | preferred | concept | reason |
|
|
||||||
|---|---|---|---|---|
|
|
||||||
| error | Day_Master | day_master | 日主 | Nexa JSON/API/prompt 字段使用 snake_case |
|
|
||||||
| error | DayMaster | day_master | 日主 | JSON/API 字段不要用 PascalCase 或拼接式 camel |
|
|
||||||
| error | DayMaster_Stem | day_master_stem | 日主天干 | mixed case 不适合接口契约 |
|
|
||||||
| error | dayMasterStem | day_master_stem | 日主天干 | JSON/API 字段使用 snake_case;JS 局部变量除外 |
|
|
||||||
| error | Primary_Favorite_Element | primary_favorite_element | 第一喜用元素 | mixed case 不适合接口契约 |
|
|
||||||
| error | Secondary_Favorite_Element | secondary_favorite_element | 第二喜用元素 | mixed case 不适合接口契约 |
|
|
||||||
| error | Favorite_Element | favorite_element | 喜用元素 | mixed case 不适合接口契约 |
|
|
||||||
| error | Element_Strengths | element_strengths | 五行强度 | mixed case 不适合接口契约 |
|
|
||||||
| error | Dominant_Deity | dominant_ten_god | 主导十神 | 十神不是 deity |
|
|
||||||
| error | dominant_deity | dominant_ten_god | 主导十神 | 十神不是 deity |
|
|
||||||
| error | deity | ten_god | 十神 | 接口字段不要用 deity 表示十神 |
|
|
||||||
| error | useGod | useful_god | 用神 | 不要把 Useful God 写成动词短语 |
|
|
||||||
| error | usefulGod | useful_god | 用神 | Nexa JSON/API 字段使用 snake_case |
|
|
||||||
| error | primary_useful_god | primary_favorite_element | 喜用元素 | 喜用元素不等同用神 |
|
|
||||||
| error | dayOwner | day_master | 日主 | 日主标准术语是 Day Master |
|
|
||||||
| error | dayLord | day_master | 日主 | 日主标准术语是 Day Master |
|
|
||||||
| error | skyStem | heavenly_stem | 天干 | 标准术语是 Heavenly Stem |
|
|
||||||
| error | groundBranch | earthly_branch | 地支 | 标准术语是 Earthly Branch |
|
|
||||||
| error | fortuneColumn | luck_pillar | 大运 | 大运是 Luck Pillar,不是 column |
|
|
||||||
| error | fortune_column | luck_pillar | 大运 | 大运是 Luck Pillar,不是 column |
|
|
||||||
| error | big_luck | luck_pillar | 大运 | 新接口使用 luck_pillar |
|
|
||||||
| error | annual_pillar | annual_luck | 流年 | 避免和本命 year_pillar 混淆 |
|
|
||||||
| error | monthly_pillar | monthly_luck | 流月 | 避免和本命 month_pillar 混淆 |
|
|
||||||
| error | daily_pillar | daily_luck | 流日 | 避免和本命 day_pillar 混淆 |
|
|
||||||
| warn | flowing_year | annual_luck | 流年 | 直译味过重,优先 annual_luck |
|
|
||||||
| warn | flowing_month | monthly_luck | 流月 | 直译味过重,优先 monthly_luck |
|
|
||||||
| warn | flowing_day | daily_luck | 流日 | 若表示干支请用 daily_stem_branch |
|
|
||||||
| warn | daily_fortune_insights | daily_luck_insights | 日运洞察模块 | 新八字模块 key 优先用 luck;已发布则兼容迁移 |
|
|
||||||
| review | key_astral_event | key_bazi_event | 今日关键八字事件 | 八字模块避免 astral;若已发布需迁移 |
|
|
||||||
| review | astral_event | bazi_event | 八字事件 | astral 属占星语境,八字事实链不要混用 |
|
|
||||||
| review | provider_visual_tags | provider_bazi_tags | Provider 标签 | 若标签全是八字来源,名称应体现 bazi |
|
|
||||||
| review | provider_visual_tag_detail | provider_bazi_tag_detail | Provider 标签详情 | 若标签全是八字来源,名称应体现 bazi |
|
|
||||||
@@ -1,78 +0,0 @@
|
|||||||
# Nexa 八字程序员命名规则
|
|
||||||
|
|
||||||
## 最高优先级
|
|
||||||
|
|
||||||
1. JSON/API 字段使用 `snake_case`。
|
|
||||||
2. 八字体系字段不得混入占星术语,除非该模块确实属于占星产品线。
|
|
||||||
3. 十神不要翻译成 `deity`、`god`、`star` 作为接口字段。对外字段用 `ten_god`,用户文案可写中文十神名。
|
|
||||||
4. `favorite_element` 是 Nexa 喜用元素字段;`useful_god` 是用神字段。两者不要互换。
|
|
||||||
5. 已上线字段必须走兼容迁移,不做无提示破坏性重命名。
|
|
||||||
|
|
||||||
## 推荐命名
|
|
||||||
|
|
||||||
| 类别 | 推荐 | 避免 | 说明 |
|
|
||||||
|---|---|---|---|
|
|
||||||
| 日主 | `day_master` | `Day_Master`, `dayOwner`, `self_lord` | API/JSON 用 snake_case |
|
|
||||||
| 日主天干 | `day_master_stem` | `DayMaster_Stem`, `dayMasterStem` in JSON | JSON 禁止 mixed case |
|
|
||||||
| 主导十神 | `dominant_ten_god` | `Dominant_Deity`, `dominant_star` | 十神不是 deity |
|
|
||||||
| 喜用元素 | `primary_favorite_element` | `Primary_Favorite_Element`, `primary_useful_god` | 喜用元素不等同用神 |
|
|
||||||
| 五行强度 | `element_strengths` | `Element_Strengths`, `energy_strengths` | 明确是五行强度 |
|
|
||||||
| 大运 | `luck_pillar` | `fortune_column`, `big_luck` | 八字术语是 Luck Pillar |
|
|
||||||
| 流年 | `annual_luck` | `flowing_year`, `annual_pillar` | 避免和本命年柱混淆 |
|
|
||||||
| 流日 | `daily_luck` | `flow_day` when used as event concept | 若表示干支可写 `daily_stem_branch` |
|
|
||||||
| 刑冲合害 | `branch_relationship` | `astral_event` | 八字地支关系不是占星事件 |
|
|
||||||
|
|
||||||
## 模块 key
|
|
||||||
|
|
||||||
模块 key 是前后端契约,不是标题文案。命名要表达数据职责:
|
|
||||||
|
|
||||||
| 当前/候选 | 建议 | 备注 |
|
|
||||||
|---|---|---|
|
|
||||||
| `daily_overview` | 保留 | 已清晰,非术语问题 |
|
|
||||||
| `monthly_luck_tag` | 保留 | 已上线可保留 |
|
|
||||||
| `daily_luck_mantra` | 保留 | 产品文案 key,可保留 |
|
|
||||||
| `daily_fortune_insights` | `daily_luck_insights` | 新增模块建议用 luck;已上线则兼容迁移 |
|
|
||||||
| `key_astral_event` | `key_bazi_event` 或 `key_energy_event` | 八字首页避免 astral |
|
|
||||||
| `home_image_module` | 保留或改为业务中性名 | 不属于术语违规 |
|
|
||||||
|
|
||||||
## Prompt 变量
|
|
||||||
|
|
||||||
Prompt 变量也属于接口契约,不应出现临时写法:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
destiny_symbols_context:
|
|
||||||
birth_date: "1999-09-09"
|
|
||||||
day_master_stem: "辛"
|
|
||||||
primary_favorite_element: "金"
|
|
||||||
secondary_favorite_element: "水"
|
|
||||||
element_strengths:
|
|
||||||
wood: 10
|
|
||||||
fire: 35
|
|
||||||
earth: 12
|
|
||||||
metal: 22
|
|
||||||
water: 8
|
|
||||||
```
|
|
||||||
|
|
||||||
不要写:
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
DayMaster_Stem: "辛"
|
|
||||||
Primary_Favorite_Element: "金"
|
|
||||||
Dominant_Deity: "七杀"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 审查输出
|
|
||||||
|
|
||||||
输出必须具体到替换建议:
|
|
||||||
|
|
||||||
```text
|
|
||||||
path/file.md:14 DayMaster_Stem -> day_master_stem [error]
|
|
||||||
原因:Nexa JSON/prompt contract 使用 snake_case;日主天干字段为 day_master_stem。
|
|
||||||
```
|
|
||||||
|
|
||||||
如果是已发布字段,输出迁移提醒:
|
|
||||||
|
|
||||||
```text
|
|
||||||
key_astral_event -> key_bazi_event [review]
|
|
||||||
原因:八字模块不应使用 astral;若前端已依赖,先新增新 key 并保留旧 key 兼容。
|
|
||||||
```
|
|
||||||
@@ -1,247 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""Scan Nexa BaZi files for mixed interface and prompt-variable names."""
|
|
||||||
|
|
||||||
from __future__ import annotations
|
|
||||||
|
|
||||||
import argparse
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
from dataclasses import dataclass
|
|
||||||
from pathlib import Path
|
|
||||||
|
|
||||||
|
|
||||||
SKILL_DIR = Path(__file__).resolve().parents[1]
|
|
||||||
DEFAULT_RULES = SKILL_DIR / "references" / "nexa-forbidden-names.md"
|
|
||||||
|
|
||||||
SEVERITY_ORDER = {"review": 0, "warn": 1, "error": 2}
|
|
||||||
|
|
||||||
EXCLUDE_DIRS = {
|
|
||||||
".git",
|
|
||||||
".hg",
|
|
||||||
".svn",
|
|
||||||
".idea",
|
|
||||||
".vscode",
|
|
||||||
"__pycache__",
|
|
||||||
"node_modules",
|
|
||||||
"vendor",
|
|
||||||
"dist",
|
|
||||||
"build",
|
|
||||||
"coverage",
|
|
||||||
".next",
|
|
||||||
".nuxt",
|
|
||||||
".venv",
|
|
||||||
"venv",
|
|
||||||
}
|
|
||||||
|
|
||||||
TEXT_EXTENSIONS = {
|
|
||||||
".c",
|
|
||||||
".cc",
|
|
||||||
".cpp",
|
|
||||||
".cs",
|
|
||||||
".css",
|
|
||||||
".csv",
|
|
||||||
".go",
|
|
||||||
".h",
|
|
||||||
".hpp",
|
|
||||||
".html",
|
|
||||||
".java",
|
|
||||||
".js",
|
|
||||||
".json",
|
|
||||||
".jsx",
|
|
||||||
".kt",
|
|
||||||
".lua",
|
|
||||||
".md",
|
|
||||||
".mjs",
|
|
||||||
".php",
|
|
||||||
".py",
|
|
||||||
".rb",
|
|
||||||
".rs",
|
|
||||||
".scss",
|
|
||||||
".sh",
|
|
||||||
".sql",
|
|
||||||
".svelte",
|
|
||||||
".swift",
|
|
||||||
".toml",
|
|
||||||
".ts",
|
|
||||||
".tsx",
|
|
||||||
".txt",
|
|
||||||
".vue",
|
|
||||||
".xml",
|
|
||||||
".yaml",
|
|
||||||
".yml",
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class Rule:
|
|
||||||
severity: str
|
|
||||||
current: str
|
|
||||||
preferred: str
|
|
||||||
concept: str
|
|
||||||
reason: str
|
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
|
||||||
class Issue:
|
|
||||||
path: Path
|
|
||||||
line_no: int
|
|
||||||
line: str
|
|
||||||
rule: Rule
|
|
||||||
|
|
||||||
|
|
||||||
def split_md_row(line: str) -> list[str]:
|
|
||||||
stripped = line.strip()
|
|
||||||
if not stripped.startswith("|") or not stripped.endswith("|"):
|
|
||||||
return []
|
|
||||||
return [cell.strip().strip("`").strip() for cell in stripped.strip("|").split("|")]
|
|
||||||
|
|
||||||
|
|
||||||
def parse_rules(path: Path) -> list[Rule]:
|
|
||||||
header: list[str] | None = None
|
|
||||||
rules: list[Rule] = []
|
|
||||||
|
|
||||||
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
|
||||||
cells = split_md_row(raw_line)
|
|
||||||
if not cells:
|
|
||||||
continue
|
|
||||||
if {"severity", "current", "preferred"}.issubset(set(cells)):
|
|
||||||
header = cells
|
|
||||||
continue
|
|
||||||
if cells and all(set(cell) <= {"-"} for cell in cells):
|
|
||||||
continue
|
|
||||||
if not header or len(cells) < len(header):
|
|
||||||
continue
|
|
||||||
|
|
||||||
row = dict(zip(header, cells))
|
|
||||||
severity = row.get("severity", "").lower()
|
|
||||||
if severity not in SEVERITY_ORDER:
|
|
||||||
continue
|
|
||||||
current = row.get("current", "").strip()
|
|
||||||
preferred = row.get("preferred", "").strip()
|
|
||||||
if not current or not preferred:
|
|
||||||
continue
|
|
||||||
rules.append(
|
|
||||||
Rule(
|
|
||||||
severity=severity,
|
|
||||||
current=current,
|
|
||||||
preferred=preferred,
|
|
||||||
concept=row.get("concept", ""),
|
|
||||||
reason=row.get("reason", ""),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return rules
|
|
||||||
|
|
||||||
|
|
||||||
def iter_files(paths: list[Path]) -> list[Path]:
|
|
||||||
found: list[Path] = []
|
|
||||||
for path in paths:
|
|
||||||
if not path.exists():
|
|
||||||
print(f"warning: path does not exist: {path}", file=sys.stderr)
|
|
||||||
continue
|
|
||||||
if path.is_file():
|
|
||||||
if is_text_candidate(path):
|
|
||||||
found.append(path)
|
|
||||||
continue
|
|
||||||
for root, dirs, files in os.walk(path):
|
|
||||||
dirs[:] = [d for d in dirs if d not in EXCLUDE_DIRS]
|
|
||||||
for name in files:
|
|
||||||
candidate = Path(root) / name
|
|
||||||
if is_text_candidate(candidate):
|
|
||||||
found.append(candidate)
|
|
||||||
return found
|
|
||||||
|
|
||||||
|
|
||||||
def is_text_candidate(path: Path) -> bool:
|
|
||||||
return path.suffix.lower() in TEXT_EXTENSIONS
|
|
||||||
|
|
||||||
|
|
||||||
def identifier_pattern(name: str) -> re.Pattern[str] | None:
|
|
||||||
if not re.match(r"^[A-Za-z_][A-Za-z0-9_]*$", name):
|
|
||||||
return None
|
|
||||||
return re.compile(r"(?<![A-Za-z0-9_])" + re.escape(name) + r"(?![A-Za-z0-9_])")
|
|
||||||
|
|
||||||
|
|
||||||
def line_has_rule(line: str, rule: Rule) -> bool:
|
|
||||||
pattern = identifier_pattern(rule.current)
|
|
||||||
if pattern:
|
|
||||||
return bool(pattern.search(line))
|
|
||||||
return rule.current in line
|
|
||||||
|
|
||||||
|
|
||||||
def scan(paths: list[Path], rules: list[Rule], min_severity: str) -> list[Issue]:
|
|
||||||
min_rank = SEVERITY_ORDER[min_severity]
|
|
||||||
active_rules = [rule for rule in rules if SEVERITY_ORDER[rule.severity] >= min_rank]
|
|
||||||
issues: list[Issue] = []
|
|
||||||
|
|
||||||
for path in iter_files(paths):
|
|
||||||
try:
|
|
||||||
text = path.read_text(encoding="utf-8")
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
try:
|
|
||||||
text = path.read_text(encoding="utf-8-sig")
|
|
||||||
except UnicodeDecodeError:
|
|
||||||
continue
|
|
||||||
|
|
||||||
for line_no, line in enumerate(text.splitlines(), start=1):
|
|
||||||
for rule in active_rules:
|
|
||||||
if line_has_rule(line, rule):
|
|
||||||
issues.append(Issue(path=path, line_no=line_no, line=line.strip(), rule=rule))
|
|
||||||
return issues
|
|
||||||
|
|
||||||
|
|
||||||
def print_rules(rules: list[Rule]) -> None:
|
|
||||||
for rule in rules:
|
|
||||||
print(f"{rule.severity}\t{rule.current}\t{rule.preferred}\t{rule.concept}")
|
|
||||||
|
|
||||||
|
|
||||||
def main() -> int:
|
|
||||||
parser = argparse.ArgumentParser(description=__doc__)
|
|
||||||
parser.add_argument("paths", nargs="*", type=Path, help="files or directories to scan")
|
|
||||||
parser.add_argument("--rules", type=Path, default=DEFAULT_RULES, help="path to nexa-forbidden-names.md")
|
|
||||||
parser.add_argument(
|
|
||||||
"--min-severity",
|
|
||||||
choices=sorted(SEVERITY_ORDER, key=SEVERITY_ORDER.get),
|
|
||||||
default="review",
|
|
||||||
help="minimum severity to report",
|
|
||||||
)
|
|
||||||
parser.add_argument("--list-rules", action="store_true", help="print loaded rules and exit")
|
|
||||||
parser.add_argument("--no-fail", action="store_true", help="always exit 0")
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
if not args.rules.exists():
|
|
||||||
print(f"rules file not found: {args.rules}", file=sys.stderr)
|
|
||||||
return 2
|
|
||||||
|
|
||||||
rules = parse_rules(args.rules)
|
|
||||||
if args.list_rules:
|
|
||||||
print_rules(rules)
|
|
||||||
return 0
|
|
||||||
|
|
||||||
if not args.paths:
|
|
||||||
parser.error("provide at least one file or directory")
|
|
||||||
|
|
||||||
issues = scan(args.paths, rules, args.min_severity)
|
|
||||||
if not issues:
|
|
||||||
print("No Nexa BaZi contract naming issues found.")
|
|
||||||
return 0
|
|
||||||
|
|
||||||
for issue in issues:
|
|
||||||
rule = issue.rule
|
|
||||||
print(f"{issue.path}:{issue.line_no}: {rule.current} -> {rule.preferred} [{rule.severity}]")
|
|
||||||
print(f" concept: {rule.concept}")
|
|
||||||
print(f" reason: {rule.reason}")
|
|
||||||
print(f" line: {issue.line}")
|
|
||||||
|
|
||||||
counts: dict[str, int] = {"error": 0, "warn": 0, "review": 0}
|
|
||||||
for issue in issues:
|
|
||||||
counts[issue.rule.severity] += 1
|
|
||||||
print(f"\n{len(issues)} issue(s) found: {counts['error']} error, {counts['warn']} warn, {counts['review']} review.")
|
|
||||||
|
|
||||||
blocking = any(issue.rule.severity in {"error", "warn"} for issue in issues)
|
|
||||||
return 0 if args.no_fail or not blocking else 1
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
|
||||||
raise SystemExit(main())
|
|
||||||
Reference in New Issue
Block a user