@eevee-ta/cli 0.1.0-beta.1

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 (70) hide show
  1. package/README.md +492 -0
  2. package/assets/skills/eevee-mcp/SKILL.md +127 -0
  3. package/assets/skills/eevee-mcp/reference.md +105 -0
  4. package/assets/skills/eevee-migration/SKILL.md +181 -0
  5. package/assets/skills/eevee-project-setup/SKILL.md +266 -0
  6. package/dist/src/bin/cli.js +237 -0
  7. package/dist/src/bin/cli.js.map +1 -0
  8. package/dist/src/commands/business-command.js +340 -0
  9. package/dist/src/commands/business-command.js.map +1 -0
  10. package/dist/src/commands/config-command.js +71 -0
  11. package/dist/src/commands/config-command.js.map +1 -0
  12. package/dist/src/commands/intl-command.js +131 -0
  13. package/dist/src/commands/intl-command.js.map +1 -0
  14. package/dist/src/commands/lint-command.js +105 -0
  15. package/dist/src/commands/lint-command.js.map +1 -0
  16. package/dist/src/commands/mcp-command.js +167 -0
  17. package/dist/src/commands/mcp-command.js.map +1 -0
  18. package/dist/src/commands/skill-command.js +116 -0
  19. package/dist/src/commands/skill-command.js.map +1 -0
  20. package/dist/src/lib/args.js +77 -0
  21. package/dist/src/lib/args.js.map +1 -0
  22. package/dist/src/lib/business-catalog.js +445 -0
  23. package/dist/src/lib/business-catalog.js.map +1 -0
  24. package/dist/src/lib/config.js +69 -0
  25. package/dist/src/lib/config.js.map +1 -0
  26. package/dist/src/lib/intl-catalog.js +58 -0
  27. package/dist/src/lib/intl-catalog.js.map +1 -0
  28. package/dist/src/lib/intl-config.js +125 -0
  29. package/dist/src/lib/intl-config.js.map +1 -0
  30. package/dist/src/lib/intl-downloader.js +217 -0
  31. package/dist/src/lib/intl-downloader.js.map +1 -0
  32. package/dist/src/lib/intl-serializers/csv.js +25 -0
  33. package/dist/src/lib/intl-serializers/csv.js.map +1 -0
  34. package/dist/src/lib/intl-serializers/custom.js +23 -0
  35. package/dist/src/lib/intl-serializers/custom.js.map +1 -0
  36. package/dist/src/lib/intl-serializers/index.js +22 -0
  37. package/dist/src/lib/intl-serializers/index.js.map +1 -0
  38. package/dist/src/lib/intl-serializers/js.js +10 -0
  39. package/dist/src/lib/intl-serializers/js.js.map +1 -0
  40. package/dist/src/lib/intl-serializers/json.js +7 -0
  41. package/dist/src/lib/intl-serializers/json.js.map +1 -0
  42. package/dist/src/lib/intl-serializers/properties.js +43 -0
  43. package/dist/src/lib/intl-serializers/properties.js.map +1 -0
  44. package/dist/src/lib/intl-serializers/ts.js +16 -0
  45. package/dist/src/lib/intl-serializers/ts.js.map +1 -0
  46. package/dist/src/lib/intl-serializers/xml.js +21 -0
  47. package/dist/src/lib/intl-serializers/xml.js.map +1 -0
  48. package/dist/src/lib/json.js +40 -0
  49. package/dist/src/lib/json.js.map +1 -0
  50. package/dist/src/lib/lint-config.js +180 -0
  51. package/dist/src/lib/lint-config.js.map +1 -0
  52. package/dist/src/lib/lint-java.js +226 -0
  53. package/dist/src/lib/lint-java.js.map +1 -0
  54. package/dist/src/lib/lint-types.js +2 -0
  55. package/dist/src/lib/lint-types.js.map +1 -0
  56. package/dist/src/lib/lint-web.js +183 -0
  57. package/dist/src/lib/lint-web.js.map +1 -0
  58. package/dist/src/lib/lint.js +192 -0
  59. package/dist/src/lib/lint.js.map +1 -0
  60. package/dist/src/lib/mcp-client.js +185 -0
  61. package/dist/src/lib/mcp-client.js.map +1 -0
  62. package/dist/src/lib/output.js +34 -0
  63. package/dist/src/lib/output.js.map +1 -0
  64. package/dist/src/lib/skill-installer.js +123 -0
  65. package/dist/src/lib/skill-installer.js.map +1 -0
  66. package/dist/src/lib/tool-docs.js +66 -0
  67. package/dist/src/lib/tool-docs.js.map +1 -0
  68. package/dist/src/lib/types.js +2 -0
  69. package/dist/src/lib/types.js.map +1 -0
  70. package/package.json +33 -0
@@ -0,0 +1,105 @@
1
+ # @eevee/cli reference for Agents
2
+
3
+ ## Command model
4
+
5
+ 1. **Business layer (preferred):** stable English `<group> <action>` commands with typed options, Bearer-token auth and response unwrapping handled by the CLI.
6
+ 2. **Raw MCP layer (fallback/debugging):** tools/list, resources/*, prompts/*, arbitrary requests and notifications.
7
+
8
+ Never call HTTP REST endpoints directly unless the user explicitly asks; route through MCP so session negotiation and auditing stay consistent.
9
+
10
+ ## Configuration
11
+
12
+ | Source | Example |
13
+ |---|---|
14
+ | Persistent file | `eevee config set --url http://127.0.0.1:10001/mcp --token <token>` |
15
+ | Per command | `eevee project list --url <mcp-url> --token <token>` |
16
+ | Environment | `EEVEE_MCP_URL=<url> EEVEE_TOKEN=<token> eevee doctor` |
17
+
18
+ Precedence is flags → environment → `~/.eevee-cli/config.json` → built-in local URL. The config file is created with mode `0600`.
19
+
20
+ ## Business discovery
21
+
22
+ ```bash
23
+ eevee business # all packaged operations grouped by domain
24
+ eevee <group> <action> --help
25
+ ```
26
+
27
+ Domains included: project, version, term, translation, tag-group, tag, permission, log, profile, config, and system user. Every delete/clear command is guarded by `--yes`.
28
+
29
+ ### Representative lifecycle
30
+
31
+ ```bash
32
+ # Projects
33
+ eevee project list --json
34
+ eevee project create --name Demo --locales zh-CN,en-US --json
35
+ eevee project update --id 86 --description 'Updated by CLI'
36
+ eevee project delete 86 --yes --json
37
+
38
+ # Versions and terms
39
+ eevee version list 86
40
+ eevee version create --id 86 --name v1
41
+ eevee term list 86
42
+ eevee term set-tags --id 86 --version-id 9 --term-id 22 --tag-ids 3,4
43
+
44
+ # Translations
45
+ eevee translation set-content --id 86 --term-id 22 --locale en-US --content Hello
46
+ eevee translation set-status --id 86 --term-id 22 --locale en-US --status 3
47
+ ```
48
+
49
+ Use the returned IDs from live responses rather than examples above.
50
+
51
+ ## Built-in MCP tool fallbacks
52
+
53
+ | Server tool | Purpose | Invocation |
54
+ |---|---|---|
55
+ | `list_api_operations` | Search Swagger-derived REST operations by method/tag/text; returns pagination and `operation_id`. | `eevee tools call list_api_operations '{"method":"GET","search":"terms","limit":50}'` |
56
+ | `get_api_operation` | Return one operation's contract: path/query/header parameters and JSON body schema. | `eevee tools call get_api_operation '{"operation_id":"<id>"}'` |
57
+ | `call_api_operation` | Execute an operation as the authenticated token owner using `parameters.path/query` plus `body`. | `eevee tools call call_api_operation '{"operation_id":"<id>","parameters":{"id":1},"body":{}}'` |
58
+
59
+ Check `eevee tools list` first; it is authoritative if the server adds tools later. Read detailed schemas and offline workflow notes with `eevee tools docs [tool-name]`.
60
+
61
+ ## Full MCP surface
62
+
63
+ ```bash
64
+ eevee ping
65
+ eevee tools list [--raw|--json]
66
+ eevee tools docs [tool]
67
+ eevee tools call <tool> '<arguments-json>' # also supports @payload.json / --args @file.json
68
+ eevee resources list | templates | read <uri>
69
+ eevee resources subscribe <uri>
70
+ eevee prompts list
71
+ eevee prompts get <name> --args '{"key":"value"}'
72
+ eevee request completion/complete '{"ref":{"type":"ref/prompt","name":"review"},"argument":{"name":"code","value":"..."}}'
73
+ eevee notify notifications/cancelled '{"requestId":"<id>","reason":"cancelled"}'
74
+ eevee request logging/setLevel '{"level":"info"}'
75
+ ```
76
+
77
+ Every network command completes MCP initialize + initialized handshake over Streamable HTTP, sends `Authorization: Bearer <user-token>`, and preserves `Mcp-Session-Id` automatically.
78
+
79
+ ## Output conventions
80
+
81
+ - Human output unwraps REST `{status,data,msg}` envelopes and renders arrays as aligned tables.
82
+ - `--json` prints `{ok:true,data}` or `{ok:false,data}`.
83
+ - `--raw` prints the full MCP CallToolResult including content blocks and structured content.
84
+ - Empty REST data prints `(空)` instead of implying failure.
85
+
86
+ ## Skill installation
87
+
88
+ Default Codex + Claude global copy:
89
+
90
+ ```bash
91
+ eevee skills install
92
+ ```
93
+
94
+ Target examples:
95
+
96
+ ```bash
97
+ eevee skills install codex # $CODEX_HOME/skills/eevee-mcp
98
+ eevee skills install claude # $CLAUDE_CONFIG_DIR/skills/eevee-mcp
99
+ eevee skills install universal # ~/.agents/skills/eevee-mcp
100
+ eevee skills install codex,claude --project .
101
+ eevee skills install universal --dir .agents/skills
102
+ eevee skills update # replace bundled-skill managed copies
103
+ eevee skills uninstall --yes # explicit removal
104
+ eevee skills paths # inspect destination roots
105
+ ```
@@ -0,0 +1,181 @@
1
+ ---
2
+ name: eevee-migration
3
+ description: Guide i18n term migration from code repositories into eevee — incremental versioning, param extraction, Git source_info, deduplication, and API-first operations.
4
+ ---
5
+
6
+ # eevee i18n 迁移技能
7
+
8
+ 当需要将代码仓库中的 i18n 词条迁移到 eevee 平台时使用此技能。
9
+ 核心原则:**所有操作走 API,不直接修改数据库**。
10
+
11
+ ## 操作人规范
12
+
13
+ - 统一使用超管用户(如 id=1)的 token,不要通过 mock 登录创建多余用户
14
+ - Token 通过 `Authorization: Bearer <token>` 传递
15
+ - 不确定 token 时先用 `GET /api/v1/users` 确认用户身份
16
+
17
+ ## 完整迁移流程
18
+
19
+ ### 第一步:切换代码仓库分支
20
+
21
+ ```bash
22
+ cd /path/to/repo && git checkout release/X.Y
23
+ git branch --show-current # 确认分支
24
+ ```
25
+
26
+ ### 第二步:提取代码中使用的 i18n key
27
+
28
+ **Java 项目(properties + MessageFormat)**:
29
+
30
+ 1. 扫描所有 `.java` 文件,匹配以下模式:
31
+ - `getLocaleMessage("key")` / `tryLocaleMessage("key", params)`
32
+ - `new BizException(ReturnCode.XXX, "key", params...)`
33
+ - 字符串字面量 `"gaia.xxx.yyy"`(带包名前缀)
34
+ 2. 扫描枚举文件(`ReturnCode.java` 等),提取 `localeMsgRef` 值
35
+ 3. 读取 `messages_zh_CN.properties` 获取所有已翻译 key
36
+ 4. 计算交集:`matched = codeKeys ∩ propsKeys`
37
+
38
+ **前端项目(JSON/tsx formatMessage)**:
39
+
40
+ 1. 扫描 `intl.tsx`/`intl.ts` 文件,匹配 `formatMessage({ id: 'key' }, { 0: x })`
41
+ 2. 从函数签名提取参数名和类型(`x: number` → 数量,`x: ReactNode` → 名称)
42
+ 3. 通过英文内容反向搜索匹配未直接引用的 key
43
+
44
+ ### 第三步:对比上一版本获取增量
45
+
46
+ ```
47
+ 新增 key = 当前版本 matched - 上一版本 matched
48
+ 删除 key = 上一版本 matched - 当前版本 matched
49
+ ```
50
+
51
+ 只将**新增 key** 迁移到新版本(增量模式),不重复已有词条。
52
+
53
+ ### 第四步:通过 API 创建版本
54
+
55
+ ```bash
56
+ curl -X POST /api/v1/projects/{projectId}/versions \
57
+ -H "Authorization: Bearer $TOKEN" \
58
+ -H "Content-Type: application/json" \
59
+ -d '{"name": "release-X.Y"}'
60
+ ```
61
+
62
+ ⚠️ 如果同名版本软删后唯一键冲突,需要应用层在软删时改名(`name__del__{id}`)或硬删旧记录。
63
+
64
+ ### 第五步:通过 API 创建词条(含翻译、描述、参数)
65
+
66
+ ```bash
67
+ curl -X POST /api/v1/projects/{projectId}/versions/{versionId}/terms \
68
+ -H "Authorization: Bearer $TOKEN" \
69
+ -H "Content-Type: application/json" \
70
+ -d '{
71
+ "name": "gaia.service.error.xxx",
72
+ "description": "模块:错误描述",
73
+ "params": [{"name":"count","value":"1","desc":"数量"}],
74
+ "translations": {"zh-CN":"中文","en-US":"English","ja-JP":"日本語","ko-KR":"한국어"}
75
+ }'
76
+ ```
77
+
78
+ **描述生成规则**:
79
+ 从 key 路径推断模块前缀 + zh-CN 内容:
80
+ `gaia.service.workflow.error.task_check.xxx` → `"任务校验:xxx"`
81
+
82
+ **参数提取规则**:
83
+ 扫描 zh-CN 翻译中的 `{0}` `{1}` 占位符,结合上下文推断:
84
+
85
+ | 上下文 | 推断 | 示例 |
86
+ |--------|------|------|
87
+ | `{0}` 后跟 个/条/项 | count | `{"name":"count","value":"1","desc":"数量(个)"}` |
88
+ | `{0}` 成功 + `{1}` 失败 | successCount/failCount | `{"name":"successCount","value":"1","desc":"成功数"}` |
89
+ | `【{0}】` 括号内 + 表 | tableName | `{"name":"tableName","value":"table_name","desc":"表名"}` |
90
+ | `{0}` + 原因/失败 | errorMsg | `{"name":"errorMsg","value":"错误详情","desc":"错误信息"}` |
91
+ | 通知模板 `{0}`~`{4}` | 依次: 空间/时间/名称/原因/链接 | 见下方模板映射 |
92
+
93
+ 通知模板映射:
94
+ `{0}`→spaceName, `{1}`→timestamp, `{2}`→flowName/taskName, `{3}`→failReason, `{4}`→detailUrl
95
+
96
+ ⚠️ **同一词条内参数名不可重复**。如两个占位符推断出同名(如两个 `tableName`),需根据位置区分(如 `spaceName` + `tableName`)。
97
+
98
+ **source_info(Git 信息)**:
99
+ API 要求 `repo/branch/path/line` 四字段完整或全部留空:
100
+
101
+ ```json
102
+ {
103
+ "repo": "https://gitlab.example.com/team/repo",
104
+ "branch": "release/6.1",
105
+ "path": "gaia-common/src/main/java/.../ReturnCode.java",
106
+ "line": 0
107
+ }
108
+ ```
109
+
110
+ 通过已有词条 PATCH 补充:
111
+ ```bash
112
+ curl -X PATCH /api/v1/projects/{projectId}/terms/{termId} \
113
+ -H "Authorization: Bearer $TOKEN" \
114
+ -H "Content-Type: application/json" \
115
+ -d '{"source_info": {...}, "params": [...]}'
116
+ ```
117
+
118
+ ### 第六步:去重(新版本不含旧版本已有的词条)
119
+
120
+ 对比新旧版本的 term name 集合,删除新版本中与旧版本重复的:
121
+
122
+ ```
123
+ V6.0 增量 = V6.0 全量 keys - V5.0 keys
124
+ ```
125
+
126
+ 逐个通过 `DELETE /api/v1/projects/{projectId}/terms/{termId}` 删除。
127
+
128
+ ### 第七步:清理"待分类"中的重复词条
129
+
130
+ 新版本发布后,"待分类"中与正式版本重复的词条应删除,只保留独有词条。
131
+
132
+ ## 质量检查
133
+
134
+ ### 遗漏 key 报告
135
+
136
+ 分类输出:
137
+
138
+ | 分类 | 说明 |
139
+ |------|------|
140
+ | 代码引用但 properties 缺失 | 枚举或代码中定义了 key 但 properties 里没翻译 |
141
+ | properties 有但代码未引用 | 历史遗留或预留 |
142
+ | 前缀 key(尾带 `.`) | 动态拼接,非缺失 |
143
+ | 误报 | 非 i18n 字符串被正则误匹配 |
144
+
145
+ ### 参数覆盖率
146
+
147
+ 检查翻译中含 `{N}` 但 params 为空的词条,通过 PATCH 补齐。
148
+
149
+ ## 常见坑
150
+
151
+ ### ❌ 不要做的事
152
+
153
+ 1. **不要直接修改数据库**——绕过校验、日志和数据完整性
154
+ 2. **不要用 `DELETE /projects/:id/terms` 清空单版本**——这会清空整个项目所有版本的词条
155
+ 3. **不要 mock 登录创建多余用户**——统一用超管 token
156
+ 4. **不要假设 properties 文件名用横杠**——Java 习惯用下划线(`messages_zh_CN.properties` 不是 `messages_zh-CN.properties`)
157
+ 5. **不要用 SQL LIKE 搜索 `__del__`**——`_` 在 LIKE 中是通配符,会误匹配 `delete_failed` 等正常 key
158
+
159
+ ### ⚠️ 注意事项
160
+
161
+ 1. source_info 四字段要么全填要么全空,不能只填部分
162
+ 2. 同一词条的 params 数组中 name 不可重复
163
+ 3. 版本删除前必须清空其下所有词条
164
+ 4. 软删记录的唯一键冲突需在应用层处理(改名后软删)
165
+ 5. 批量 API 调用时控制并发(建议 3~5),避免打垮服务
166
+
167
+ ## 多版本增量模式图
168
+
169
+ ```
170
+ V4.5(全量 311)
171
+ └── V5.0(增量 184 = 5.0新增key)
172
+ └── V6.0(增量 194 = 6.0新增key)
173
+ └── V6.1(增量 0 = 无新增,复用 V6.0)
174
+
175
+ 待分类 = 全量导入 - 各正式版本已有的 key
176
+ ```
177
+
178
+ 每次切换分支后:
179
+ 1. 提取 key → 对比上一版 → 得到增量
180
+ 2. 创建新版本 → 创建增量词条
181
+ 3. 无增量则不创建新版本,仅更新版本名
@@ -0,0 +1,266 @@
1
+ ---
2
+ name: eevee-project-setup
3
+ description: Guide configuring a frontend or Java backend project to use @eevee/cli for term sync (intl download) and lint (missing-key / hardcoded-text detection). Covers web and Java adapters, file layout, and step-by-step initialization.
4
+ ---
5
+
6
+ # eevee 项目接入技能
7
+
8
+ 当需要让一个代码仓库通过 `@eevee/cli` 实现**词条同步**和**国际化检查**时使用此技能。
9
+ 核心原则:**所有词条数据通过 API 拉取,配置文件提交到仓库,token 不入 Git**。
10
+
11
+ ## 前置条件
12
+
13
+ ```bash
14
+ eevee config set --url http://127.0.0.1:10001/mcp
15
+ eevee config set --token <your-user-token>
16
+ eevee ping
17
+ ```
18
+
19
+ 确认项目 ID 和版本 ID:
20
+
21
+ ```bash
22
+ eevee project list
23
+ eevee intl versions --project <projectId>
24
+ ```
25
+
26
+ ## 通用流程
27
+
28
+ 1. 确认项目类型(前端 web / 后端 java)
29
+ 2. 创建 `.eevee/` 目录存放同步配置
30
+ 3. 创建 `.eevee-lint.json`(Java)或在 `package.json` 配置 `eevee.lint`(web)
31
+ 4. 运行 `eevee intl download --dry-run` 验证
32
+ 5. 运行 `eevee lint` 验证
33
+ 6. 把快照目录加入 `.gitignore`(快照不入 Git)
34
+
35
+ ---
36
+
37
+ ## 前端项目(web adapter)
38
+
39
+ 前端项目有 `package.json`,配置写入 `eevee` 字段或独立 `.eevee-lint.json`。
40
+
41
+ ### package.json
42
+
43
+ ```jsonc
44
+ {
45
+ "eevee": {
46
+ "intl": "intl.download.json",
47
+ "lint": {
48
+ "refresh": {
49
+ "config": "intl.download.json"
50
+ },
51
+ "languages": {
52
+ "web": {
53
+ "enabled": true,
54
+ "root": ".",
55
+ "intlConfig": "intl.json",
56
+ "includes": ["src/**/*.{ts,tsx,js,jsx}"],
57
+ "excludes": ["**/*.test.*", "**/mock/**", "**/node_modules/**"],
58
+ "excludeAscii": true,
59
+ "tagMatchMap": {
60
+ "src/**/*": ["<project-tag>"]
61
+ },
62
+ "excludeProperties": ["title", "description"],
63
+ "excludeTrack": true
64
+ }
65
+ }
66
+ }
67
+ }
68
+ }
69
+ ```
70
+
71
+ ### intl.download.json
72
+
73
+ ```json
74
+ [
75
+ {
76
+ "outPath": ".eevee/locales",
77
+ "format": "json",
78
+ "splitByTag": false,
79
+ "languages": {
80
+ "zh-CN": "zh-CN-<tag-name>"
81
+ },
82
+ "projects": [
83
+ { "projectId": 1, "tags": [123] }
84
+ ]
85
+ }
86
+ ]
87
+ ```
88
+
89
+ ### intl.json(lint 数据源)
90
+
91
+ ```json
92
+ {
93
+ "outPaths": [
94
+ { "path": ".eevee/locales" }
95
+ ]
96
+ }
97
+ ```
98
+
99
+ ### 运行
100
+
101
+ ```bash
102
+ eevee lint --staged
103
+ eevee lint --language web
104
+ ```
105
+
106
+ ---
107
+
108
+ ## 后端项目(Java adapter)
109
+
110
+ Java 项目(如 Spring Boot)通常没有 `package.json`,推荐在仓库根目录放 `.eevee-lint.json`,
111
+ 同步配置放 `.eevee/` 目录。
112
+
113
+ ### 目录结构
114
+
115
+ ```
116
+ project-root/
117
+ ├── .eevee/
118
+ │ ├── sync.json ← 运行时 properties 全量生成
119
+ │ ├── download.json ← lint 用 JSON 快照
120
+ │ ├── intl.json ← lint 数据源声明
121
+ │ └── README.md
122
+ ├── .eevee-lint.json ← lint 规则
123
+ └── .gitignore ← 忽略 .eevee/locales/
124
+ ```
125
+
126
+ ### .eevee-lint.json
127
+
128
+ ```json
129
+ {
130
+ "refresh": {
131
+ "config": ".eevee/download.json"
132
+ },
133
+ "languages": {
134
+ "web": { "enabled": false },
135
+ "java": {
136
+ "enabled": true,
137
+ "root": ".",
138
+ "intlConfig": ".eevee/intl.json",
139
+ "includes": ["*/src/main/java/**/*.java"],
140
+ "excludes": ["**/src/test/**", "**/generated/**", "**/target/**"],
141
+ "tagMatchMap": {
142
+ "*/src/main/java/**/*": ["<tag-name>"]
143
+ },
144
+ "localeMethods": ["getLocaleMessage", "tryLocaleMessage", "getMessage"],
145
+ "checkHardcoded": true,
146
+ "excludeMethods": ["trace", "debug", "info", "warn", "error"]
147
+ }
148
+ }
149
+ }
150
+ ```
151
+
152
+ ### .eevee/download.json(lint 快照)
153
+
154
+ ```json
155
+ [
156
+ {
157
+ "outPath": "locales",
158
+ "format": "json",
159
+ "splitByTag": false,
160
+ "languages": {
161
+ "zh-CN": "zh-CN-<tag-name>"
162
+ },
163
+ "projects": [
164
+ { "projectId": 3, "versions": [2, 29, 30, 31] }
165
+ ]
166
+ }
167
+ ]
168
+ ```
169
+
170
+ ### .eevee/intl.json
171
+
172
+ ```json
173
+ {
174
+ "outPaths": [
175
+ { "path": "locales" }
176
+ ]
177
+ }
178
+ ```
179
+
180
+ > `outPaths` 相对于 `.eevee/intl.json` 自身目录解析,所以 `"locales"` 指向 `.eevee/locales/`。
181
+
182
+ ### .eevee/sync.json(运行时 properties 全量生成)
183
+
184
+ ```json
185
+ [
186
+ {
187
+ "outPath": "<module>/src/main/resources/static/i18n",
188
+ "format": "properties",
189
+ "splitByTag": false,
190
+ "languages": {
191
+ "zh-CN": "messages_zh_CN",
192
+ "en-US": "messages_en_US",
193
+ "ja-JP": "messages_ja_JP",
194
+ "ko-KR": "messages_ko_KR"
195
+ },
196
+ "projects": [
197
+ {
198
+ "projectId": 3,
199
+ "versions": [2, 29, 30, 31],
200
+ "tags": []
201
+ }
202
+ ],
203
+ "options": {
204
+ "properties": { "escape": "none" }
205
+ }
206
+ }
207
+ ]
208
+ ```
209
+
210
+ 关键点:
211
+
212
+ - `tags: []` 表示不做标签过滤,项目级全量拉取(适用于没有平台标签的后端项目)
213
+ - 语言映射的 value 是文件名前缀(不含 `.properties`),直接生成 `messages_zh_CN.properties`
214
+ - `escape: "none"` 保持 UTF-8 原文,与 Spring Boot `MessageSource` 兼容
215
+ - `outPath` 相对于 `.eevee/` 目录解析,`../` 可以跳出回到仓库根
216
+
217
+ ### .gitignore 追加
218
+
219
+ ```gitignore
220
+ # eevee lint 本地快照;运行时 properties 由 sync.json 显式同步
221
+ /.eevee/locales/
222
+ ```
223
+
224
+ ### 运行
225
+
226
+ ```bash
227
+ # 预览运行时同步
228
+ eevee intl download --config .eevee/sync.json --dry-run
229
+
230
+ # 全量生成 properties
231
+ eevee intl download --config .eevee/sync.json
232
+
233
+ # lint(自动刷新快照 + 检查)
234
+ eevee lint --staged --language java
235
+ ```
236
+
237
+ ---
238
+
239
+ ## 无标签项目的特殊处理
240
+
241
+ 如果 EEVEE 平台项目没有创建任何标签组:
242
+
243
+ - `tags: []` → 项目级全量拉取
244
+ - 省略 `tags` 且平台无标签 → 自动降级为项目级全量
245
+ - `versions` 省略 → 拉取项目全部版本
246
+
247
+ 后端项目推荐显式配置 `versions`,避免新版本迁移后把未发布的 key 拉进当前分支。
248
+
249
+ ## 版本升级维护
250
+
251
+ 每次创建新 release 版本后:
252
+
253
+ 1. 在 EEVEE 通过 API 迁移增量词条
254
+ 2. 拿到新 version ID
255
+ 3. 追加到 `.eevee/sync.json` 和 `.eevee/download.json` 的 `versions` 数组
256
+ 4. 执行 `eevee intl download --config .eevee/sync.json` 刷新 properties
257
+
258
+ ## 常见问题
259
+
260
+ | 问题 | 原因 | 解决 |
261
+ |------|------|------|
262
+ | 未读取到任何 zh-CN*.json | `intl.json` 的 `outPaths` 路径不对 | 确认路径相对于 `intl.json` 所在目录 |
263
+ | 平台返回 0 个词条 | 项目没有标签且未配置 `tags: []` | 显式设置 `"tags": []` |
264
+ | token 过期 401 | `~/.eevee-cli/config.json` 里的 token 失效 | `eevee config set --token <new>` |
265
+ | 全量 sync 后 diff 很大 | key 顺序按平台排序 | 一次性重排,后续 diff 只看新增/删除 |
266
+ | properties 某行换行 | 翻译里含物理换行 | 已在 CLI 修复,确保使用最新版本 |