@deepstorm/cli 0.3.2 → 0.4.0

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.
package/dist/cli.js CHANGED
@@ -8307,6 +8307,19 @@ function skillIdsToTools(skillIds, registry2) {
8307
8307
  }
8308
8308
  return [...toolSet];
8309
8309
  }
8310
+ function detectToolsFromConfig(config, registry2) {
8311
+ const knownTools = new Set(Object.keys(registry2.tools ?? {}));
8312
+ const found = /* @__PURE__ */ new Set();
8313
+ for (const key of Object.keys(config)) {
8314
+ const dotIdx = key.indexOf(".");
8315
+ if (dotIdx < 0) continue;
8316
+ const prefix = key.slice(0, dotIdx);
8317
+ if (knownTools.has(prefix)) {
8318
+ found.add(prefix);
8319
+ }
8320
+ }
8321
+ return [...found];
8322
+ }
8310
8323
  function readSettingsConfig(targetDir) {
8311
8324
  const settingsPath = path18.join(targetDir, ".claude", "settings.json");
8312
8325
  try {
@@ -8367,16 +8380,18 @@ function syncToolAssets(cliDir2, targetDir, installedSkillIds) {
8367
8380
  }
8368
8381
  const reader = new RegistryReader(registry2);
8369
8382
  const toolNames = skillIdsToTools(installedSkillIds, registry2);
8370
- if (toolNames.length === 0) {
8371
- console.log("\u26A0 \u65E0\u6CD5\u4ECE\u5DF2\u5B89\u88C5 skill \u89E3\u6790\u5DE5\u5177\u540D\u79F0\uFF0C\u8DF3\u8FC7\u8D44\u4EA7\u540C\u6B65");
8383
+ const { config, installedMcpTools } = readSettingsConfig(targetDir);
8384
+ const configTools = detectToolsFromConfig(config, registry2);
8385
+ const allTools = [.../* @__PURE__ */ new Set([...toolNames, ...configTools])];
8386
+ if (allTools.length === 0) {
8387
+ console.log("\u26A0 \u65E0\u6CD5\u4ECE\u5DF2\u5B89\u88C5 skill \u6216\u914D\u7F6E\u89E3\u6790\u5DE5\u5177\u540D\u79F0\uFF0C\u8DF3\u8FC7\u8D44\u4EA7\u540C\u6B65");
8372
8388
  return { syncedSkills: [], syncedAgents: [], syncedHooks: [], backedUpFiles: [] };
8373
8389
  }
8374
- const { config, installedMcpTools } = readSettingsConfig(targetDir);
8375
8390
  const templateVariables = buildTemplateVariables(registry2, config, installedMcpTools);
8376
8391
  const mcpSkillSet = /* @__PURE__ */ new Set();
8377
8392
  if (installedMcpTools.length > 0) {
8378
8393
  const selectedMcpSet = new Set(installedMcpTools);
8379
- for (const tool of toolNames) {
8394
+ for (const tool of allTools) {
8380
8395
  for (const skillId of reader.getToolMcpSkills(tool)) {
8381
8396
  const service = skillId.replace(/^deepstorm-mcp-/, "").replace(/-(read|write)$/, "");
8382
8397
  if (selectedMcpSet.has(service)) mcpSkillSet.add(skillId);
@@ -8385,7 +8400,7 @@ function syncToolAssets(cliDir2, targetDir, installedSkillIds) {
8385
8400
  }
8386
8401
  const mcpSkillIds = [...mcpSkillSet];
8387
8402
  const allSkillIdsForBackup = [.../* @__PURE__ */ new Set([...installedSkillIds, ...mcpSkillIds])];
8388
- const backedUpFiles = backupModifiedAssets(targetDir, allSkillIdsForBackup, toolNames, registry2);
8403
+ const backedUpFiles = backupModifiedAssets(targetDir, allSkillIdsForBackup, allTools, registry2);
8389
8404
  if (backedUpFiles.length > 0) {
8390
8405
  console.log("\n\u26A0 \u68C0\u6D4B\u5230\u7528\u6237\u4FEE\u6539\u7684\u6587\u4EF6\uFF1A");
8391
8406
  for (const info of backedUpFiles) {
@@ -8397,7 +8412,7 @@ function syncToolAssets(cliDir2, targetDir, installedSkillIds) {
8397
8412
  const syncedAgents = [];
8398
8413
  const syncedHooks = [];
8399
8414
  let hasWarning = false;
8400
- for (const tool of toolNames) {
8415
+ for (const tool of allTools) {
8401
8416
  try {
8402
8417
  reader.getToolEntry(tool);
8403
8418
  } catch {
@@ -8428,11 +8443,11 @@ function syncToolAssets(cliDir2, targetDir, installedSkillIds) {
8428
8443
  const syncedSkills = actualSkillIds.length > 0 ? actualSkillIds : [...installedSkillIds];
8429
8444
  const syncedMcpSkillIds = [];
8430
8445
  if (installedMcpTools.length > 0) {
8431
- installMcpSkills(toolNames, reader, cliDir2, targetDir, syncedMcpSkillIds, installedMcpTools);
8446
+ installMcpSkills(allTools, reader, cliDir2, targetDir, syncedMcpSkillIds, installedMcpTools);
8432
8447
  }
8433
- mergeToolHooksJson(toolNames, cliDir2, targetDir);
8448
+ mergeToolHooksJson(allTools, cliDir2, targetDir);
8434
8449
  const allSkillIds = [...installedSkillIds, ...syncedMcpSkillIds];
8435
- storeNewChecksums(targetDir, allSkillIds, toolNames, registry2);
8450
+ storeNewChecksums(targetDir, allSkillIds, allTools, registry2);
8436
8451
  if (hasWarning) {
8437
8452
  console.log("\n\u26A0 \u90E8\u5206\u5DE5\u5177\u540C\u6B65\u51FA\u9519\uFF0C\u8BF7\u68C0\u67E5\u540E\u91CD\u8BD5");
8438
8453
  }
@@ -0,0 +1,97 @@
1
+ #!/bin/bash
2
+ # reef-code-style-verify.sh.tmpl
3
+ # PostToolUse: 在 Edit/Write 后自动验证代码风格
4
+ #
5
+ # 模板变量(安装时由 wizard 注入):
6
+ # {{reef.backend.java.lintCmd}} — Java lint 命令(默认 checkstyle)
7
+ # {{reef.backend.python.lintCmd}} — Python lint 命令(默认 ruff check)
8
+ # {{reef.frontend.lintCmd}} — 前端 lint 命令(默认 eslint)
9
+
10
+ set -euo pipefail
11
+
12
+ # ── 1. 获取文件路径 ──────────────────────────────────────────────────
13
+ filepath=""
14
+
15
+ if [ -p /dev/stdin ] && command -v jq &>/dev/null; then
16
+ filepath=$(jq -r '.tool_response.filePath // .tool_input.file_path // ""' 2>/dev/null || true)
17
+ fi
18
+
19
+ if [ -z "$filepath" ] || [ "$filepath" = "null" ]; then
20
+ filepath="${CLAUDE_CODE_TOOL_RESULT_FILEPATH:-}"
21
+ fi
22
+
23
+ [ -z "$filepath" ] && exit 0
24
+ [ ! -f "$filepath" ] && exit 0
25
+
26
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-$(pwd)}"
27
+
28
+ JAVA_LINT="{{reef.backend.java.lintCmd}}"
29
+ PYTHON_LINT="{{reef.backend.python.lintCmd}}"
30
+ TS_LINT="{{reef.frontend.lintCmd}}"
31
+
32
+ # ── ESLint 配置检测 ───────────────────────────────────────────────────
33
+ has_eslint_config() {
34
+ [ -f "$PROJECT_DIR/eslint.config.js" ] || \
35
+ [ -f "$PROJECT_DIR/eslint.config.mjs" ] || \
36
+ [ -f "$PROJECT_DIR/.eslintrc.js" ] || \
37
+ [ -f "$PROJECT_DIR/.eslintrc.json" ] || \
38
+ [ -f "$PROJECT_DIR/.eslintrc" ]
39
+ }
40
+
41
+ # ── 2. 按文件类型执行验证 ────────────────────────────────────────────
42
+
43
+ case "$filepath" in
44
+ *.java)
45
+ # Java: checkstyle(如果项目配置了 checkstyle.xml)
46
+ if [ "$JAVA_LINT" != "none" ] && command -v "${JAVA_LINT%% *}" &>/dev/null; then
47
+ for conf in "$PROJECT_DIR/checkstyle.xml" "$PROJECT_DIR/config/checkstyle/checkstyle.xml"; do
48
+ if [ -f "$conf" ]; then
49
+ output=$($JAVA_LINT -c "$conf" "$filepath" 2>&1 | grep -iE "error|warning" | head -20 || true)
50
+ if [ -n "$output" ]; then
51
+ echo "[style-verify] ⚠ Java 代码风格问题:"
52
+ echo "$output"
53
+ echo "[style-verify] 运行 $JAVA_LINT -c $conf $filepath 查看完整报告。"
54
+ fi
55
+ fi
56
+ done
57
+ fi
58
+ ;;
59
+
60
+ *.py)
61
+ # Python: ruff check
62
+ if [ "$PYTHON_LINT" != "none" ] && command -v "${PYTHON_LINT%% *}" &>/dev/null; then
63
+ output=$($PYTHON_LINT "$filepath" 2>/dev/null || true)
64
+ if [ -n "$output" ]; then
65
+ echo "[style-verify] ⚠ Python 代码风格问题:"
66
+ echo "$output" | head -20
67
+ echo "[style-verify] 请使用 $PYTHON_LINT $filepath 查看完整报告。"
68
+ fi
69
+ fi
70
+ ;;
71
+
72
+ *.ts|*.tsx|*.js|*.jsx)
73
+ # TypeScript/JavaScript: eslint
74
+ if [ "$TS_LINT" != "none" ] && command -v npx &>/dev/null && has_eslint_config; then
75
+ output=$(cd "$PROJECT_DIR" && npx $TS_LINT "$filepath" 2>/dev/null || true)
76
+ if [ -n "$output" ]; then
77
+ echo "[style-verify] ⚠ TypeScript/JavaScript 代码风格问题:"
78
+ echo "$output" | head -20
79
+ echo "[style-verify] 请运行 npx $TS_LINT $filepath 查看完整报告。"
80
+ fi
81
+ fi
82
+ ;;
83
+
84
+ *.html|*.css|*.scss|*.less)
85
+ # 前端模板/样式文件:eslint(如果项目已配置)
86
+ if [ "$TS_LINT" != "none" ] && command -v npx &>/dev/null && has_eslint_config; then
87
+ output=$(cd "$PROJECT_DIR" && npx $TS_LINT "$filepath" 2>/dev/null || true)
88
+ if [ -n "$output" ]; then
89
+ echo "[style-verify] ⚠ 前端文件代码风格问题:"
90
+ echo "$output" | head -10
91
+ echo "[style-verify] 请运行 npx $TS_LINT $filepath 查看完整报告。"
92
+ fi
93
+ fi
94
+ ;;
95
+ esac
96
+
97
+ exit 0
@@ -30,7 +30,7 @@
30
30
  {
31
31
  "type": "prompt",
32
32
  "timeout": 5,
33
- "prompt": "You are about to write or edit a file. If the target file extension is .java, load reef-style-backend skill for coding standards. If the target is .ts, .html, .css, .scss, or .less, load reef-style-frontend skill for coding standards. Follow the loaded skill's rules in your output."
33
+ "prompt": "You are about to write or edit a source file. The following core rules apply based on the file extension. You MUST follow the relevant rules for your file type.\n\n## 🔴 Core Coding Rules (must follow)\n\n### Java\n1. Use @Getter @AllArgsConstructor for event/POJO classes; NEVER use @Data\n2. NEVER use @Autowired field injection; always use constructor injection (@RequiredArgsConstructor)\n3. Use abstract method instead of instanceof chain for type dispatch\n4. Use formatted() / Text Block for string formatting; never use + concatenation\n5. Use @RequiredArgsConstructor for Service/Controller; @Getter @NoArgsConstructor(access = PROTECTED) for Entity\n\n### Python\n1. Use f-strings; never use % or .format()\n2. snake_case for functions/variables; PascalCase for classes\n3. Always annotate return types and parameter types (mypy must pass)\n4. Use is / is not for None comparison; never use == None\n5. Use Enum / Protocol / abstract method instead of isinstance() chain\n\n### TypeScript\n1. Standalone + OnPush + inject() (Angular) / Function components + hooks (React) / <script setup lang=\"ts\"> (Vue)\n2. Use output() instead of @Output() (Angular) / custom hooks over lifecycle methods (React) / composables (Vue)\n3. Use signal() / computed() for reactive state management\n4. Use loadComponent for lazy-loaded routes\n5. Use interface for Props / Entity types\n\n## 📖 Full Reference\nLoad the reef-style-backend or reef-style-frontend skill for the complete coding standard.\n\n## Self-Verification\nBefore writing code, first output `✅ [STYLE] confirmed {language} rules` to acknowledge you have read and will follow the rules above."
34
34
  }
35
35
  ]
36
36
  }
@@ -45,6 +45,17 @@
45
45
  }
46
46
  ]
47
47
  },
48
+ {
49
+ "matcher": "Edit|Write",
50
+ "hooks": [
51
+ {
52
+ "type": "command",
53
+ "command": "bash .claude/hooks/reef-code-style-verify.sh",
54
+ "async": true,
55
+ "statusMessage": "Verifying code style..."
56
+ }
57
+ ]
58
+ },
48
59
  {
49
60
  "matcher": "Edit|Write",
50
61
  "hooks": [
@@ -562,6 +562,20 @@
562
562
  ]
563
563
  }
564
564
  ]
565
+ },
566
+ {
567
+ "key": "reef.backend.java.formatTool",
568
+ "label": "Java 格式化工具",
569
+ "type": "static",
570
+ "value": "google-java-format",
571
+ "template": {
572
+ "formatCmd": "google-java-format -i",
573
+ "lintCmd": "checkstyle"
574
+ },
575
+ "affectedTemplates": [
576
+ "hooks/reef-auto-format.sh.tmpl",
577
+ "hooks/reef-code-style-verify.sh.tmpl"
578
+ ]
565
579
  }
566
580
  ]
567
581
  },
@@ -951,13 +965,13 @@
951
965
  "hooks": [
952
966
  "reef-auto-format.sh",
953
967
  "reef-block-dangerous.sh",
968
+ "reef-code-style-verify.sh",
954
969
  "reef-intent-detect.sh",
955
970
  "reef-protect-files.sh",
956
971
  "reef-run-tests.sh",
957
972
  "reef-scope-check.sh",
958
973
  "reef-scope-ci.sh",
959
974
  "reef-scope-gate.sh",
960
- "reef-scope-pre-commit.sh",
961
975
  "reef-scope-setup.sh",
962
976
  "reef-scope-split.sh"
963
977
  ]
@@ -159,6 +159,38 @@ git status --short
159
159
  - 测试全部通过 → 继续
160
160
  - 任一测试失败 → 提示用户修复后再提交
161
161
 
162
+ ### 6.5 OpenSpec 验证与归档检查
163
+
164
+ > 提交前检查关联的 OpenSpec change 是否已完成验证和归档。如果 `openspec/` 目录不存在或无活跃 change 则跳过本步骤。
165
+
166
+ **判断流程(LLM 自行推理执行):**
167
+
168
+ 1. **查找关联的 OpenSpec change:**
169
+ ```bash
170
+ BRANCH=$(git branch --show-current)
171
+ echo "当前分支: $BRANCH"
172
+
173
+ for dir in openspec/changes/*/; do
174
+ CHANGE_NAME=$(basename "$dir")
175
+ if [ -f "$dir/.openspec.yaml" ] && [ "$CHANGE_NAME" != "archive" ]; then
176
+ echo "发现活跃 OpenSpec change: $CHANGE_NAME"
177
+ cat "$dir/.openspec.yaml"
178
+ fi
179
+ done
180
+ ```
181
+
182
+ 2. **匹配规则:** 扫描 `openspec/changes/*/` 下活跃 change(不包含 `archive/`),与当前分支名比对;无匹配则跳过;多匹配则让用户选择。
183
+
184
+ 3. **检查归档状态:** 读取 `.openspec.yaml` 中 `status` 字段。`archived` → 跳过后续检查;否则继续。
185
+
186
+ 4. **运行验证:** 确认 `tasks.md` 全部 checkbox 已完成 → 通过 Skill 工具自动调用 `/opsx:verify`。有 CRITICAL 问题则中止;仅 WARNING/SUGGESTION 则通过。
187
+
188
+ 5. **运行归档:** 验证通过后 → 通过 Skill 工具自动调用 `/opsx:archive`。执行失败则提示用户手动处理。
189
+
190
+ 6. **确认已就绪:** 校验状态并向用户报告。
191
+
192
+ > **提示:** verify/archive 执行后产生额外文件变更的,后续步骤会重新检测并纳入提交。
193
+
162
194
  ### 7. 收集上下文
163
195
 
164
196
  ```bash
@@ -200,6 +200,6 @@ TypeScript union type + `type` literal 可直接利用 `@switch` 的类型窄化
200
200
  | `@JsonProperty("xAxisName") private String categoryAxisLabel` | 字段名合规,getter 合规,JSON 输出保留 `xAxisName` | ✅ |
201
201
  | 禁用 Checkstyle `ParameterNameCheck` | 全局禁用降低代码规范性 | ❌ |
202
202
 
203
- ### 5.6 Decoder 对象保持 record
203
+ ### 5.6 DTO 对象保持 record
204
204
 
205
205
  如 `MetricAnswer` 是 Java record,不能继承。改造方案为直接替换字段类型(`ChartType chartType` → `ChartView chartView`),record 紧凑语法不受影响。构建逻辑从 `determineChartType()` 改为 `buildChartView()` 并返回具体子类。
@@ -53,31 +53,8 @@ DTO 继承基类,写请求用独立 CreateRequest/UpdateRequest record。MapSt
53
53
 
54
54
  ### 异常处理
55
55
 
56
- | 异常 | HTTP | 场景 |
57
- | --- | --- | --- |
58
- | `NotFoundException` | 404 | 资源不存在 |
59
- | `AlreadyExistsException` | 409 | 重复创建 |
60
- | `InvalidArgumentException` | 400 | 参数校验失败 |
61
- | `PermissionDeniedException` | 403 | 无权限 |
62
- | `FailedPreconditionException` | 400 | 前置条件不满足 |
63
-
64
- 所有自定义异常通过 `@RestControllerAdvice` `GlobalExceptionHandler` 统一处理,返回 AIP-193 兼容的 `CustomError` 结构:
65
-
66
- ```java
67
- @RestControllerAdvice
68
- public class GlobalExceptionHandler {
69
- @ExceptionHandler(ResourceNotFoundException.class)
70
- public ResponseEntity<ErrorResponse> handleNotFound(ResourceNotFoundException ex) {
71
- return ResponseEntity.status(NOT_FOUND).body(new ErrorResponse(ex.getMessage()));
72
- }
73
- }
74
- ```
56
+ > 异常处理规范(异常继承层次、ErrorCode 枚举、GlobalExceptionHandler)参见 [异常处理规范](../exception-handling/quick-reference.md)。
75
57
 
76
58
  ### 安全编码规范
77
59
 
78
- - Controller 写操作接口必须加 `@PreAuthorize("hasAuthority('PERMISSION_NAME')")`,禁止在方法内硬编码权限字符串
79
- - 请求体参数加 `@Valid`,嵌套对象加 `@Valid`
80
- - DTO 中密码/token 用 `@JsonIgnore` 或从响应 DTO 中排除
81
- - 密码使用 `BCryptPasswordEncoder`
82
- - 禁止 `createNativeQuery`(绕过多租户 + SQL 注入风险)
83
- - `@Query` 始终使用命名参数(`:paramName`),不用 `?1` 位置参数
60
+ > 安全红线(密码存储、SQL 注入、敏感信息脱敏、文件上传等)参见 [安全红线](../security-redlines/quick-reference.md)
@@ -185,7 +185,8 @@ Java 15+ 的 Text Block 用于嵌入 JSON / SQL / XML / 模板等 DSL。缩进
185
185
  ```java
186
186
  // ✅ opening """ 后直接换行,内容相对 opening 行缩进 4 格
187
187
  // ✅ closing """ 决定 stripIndent() 的公共缩进基线
188
- var json = """
188
+ var json =
189
+ """
189
190
  {
190
191
  "name": "example",
191
192
  "value": 42,
@@ -194,7 +195,8 @@ var json = """
194
195
  """;
195
196
 
196
197
  // ✅ 嵌入 SQL:closing """ 与 SQL 内容的公共缩进最左列对齐
197
- var sql = """
198
+ var sql =
199
+ """
198
200
  SELECT u.id, u.name, r.role_name
199
201
  FROM users u
200
202
  JOIN user_roles r ON r.user_id = u.id
@@ -209,7 +211,8 @@ var message = """
209
211
  """.formatted(userName, orderId);
210
212
 
211
213
  // ✅ 空行可用 \s 占位避免 stripIndent 清空:
212
- var json = """
214
+ var json =
215
+ """
213
216
  {
214
217
  "title": "test",
215
218
  \s
@@ -219,9 +222,10 @@ var json = """
219
222
  ```
220
223
 
221
224
  **规范:**
222
- - `"""` 必须后跟换行,**禁止在同行放内容**
223
- - `closing """` 的缩进决定公共缩进基线,放在最后一行内容之下,与之缩进对齐
225
+
226
+ - `"""` **独立占一行**,后跟换行,禁止在同行放内容
227
+ - `closing """` **独立占一行**,其缩进决定公共缩进基线,放在最后一行内容之下与之对齐
224
228
  - 禁止 text block 与 `+` 拼接混用;需要变量替换统一用 `formatted()`
225
229
  - 短字符串(≤ 100 列单行)不使用 text block,直接使用普通字符串
226
230
  - `\s` 用于显式保留 text block 中的空行(避免 `stripIndent()` 把空行清空)
227
- ```
231
+
@@ -22,45 +22,17 @@
22
22
  | 字段注释 | Model/Entity/Event 加 `/** */`,DTO/Record 不加 |
23
23
  | 日志实体 | 继承 `LogEntry`(SINGLE_TABLE),不直接继承基类 |
24
24
  | 弃用 API | 编译警告中的 `@Deprecated` API 在同一次 PR 中替换为新 API |
25
- | 多行字符串 | 用 Text Block `"""..."""` + `formatted()` 嵌入 JSON/SQL/XML,禁止 `+` 拼接;详见 [code-wrapping.md](examples/code-wrapping.md) |
25
+ | 多行字符串 | 用 Text Block `"""..."""` + `formatted()` 嵌入 JSON/SQL/XML,禁止 `+` 拼接 |
26
26
 
27
27
  ## 代码风格
28
28
 
29
- ### 通用约定
30
-
31
- - 变量命名有意义,禁止单字母(循环计数器 `i`/`j`/`k` 除外)
32
- - 局部变量用 `var`
33
- - 字符串格式化用 `formatted()`,不用 `+` 拼接
34
- - 多态替代 `instanceof` 链做类型分发
35
- - 100 列折行,运算符/`.`/`::` 放行首。适用范围如下:
36
- - **代码结构**(方法调用、签名、表达式)严格执行 100 列
37
- - **Javadoc / 注释文本**以可读性优先,不硬断中文句子;仅单行 `/** ... */` 标记超长时拆成多行
38
- - **方法调用参数换行**:所有参数能在列宽内一行放完则一行,放不完则每个参数独立一行(不混合分组);详见 [code-wrapping.md](examples/code-wrapping.md)「方法调用参数换行」
39
- - 类内字段按用途逻辑分组,组间空行分隔,**禁止使用 `// ======`(或其他重复符号装饰)做视觉分隔线注释**。例如应避免 `// ========== 维度信息 ==========` 这种写法
40
- - 注释与代码逻辑块之间:相邻的两个 `// 注释 + 代码块` 之间用空行分隔,使每个逻辑块保持独立。
41
- ```java
42
- // ✅ 正确:逻辑块之间有空行
43
- // PROPORTION → 饼图
44
- if (queryIntent == QueryIntent.PROPORTION) {
45
- return ChartType.PIE_CHART;
46
- }
47
-
48
- // GROUP_BY → 柱状图
49
- if (queryIntent == QueryIntent.GROUP_BY) {
50
- return ChartType.BAR_CHART;
51
- }
52
- ```
53
- ```java
54
- // ❌ 错误:缺少分隔空行,两个逻辑块粘连
55
- // PROPORTION → 饼图
56
- if (queryIntent == QueryIntent.PROPORTION) {
57
- return ChartType.PIE_CHART;
58
- }
59
- // GROUP_BY → 柱状图
60
- if (queryIntent == QueryIntent.GROUP_BY) {
61
- return ChartType.BAR_CHART;
62
- }
63
- ```
29
+ ### LLM 常犯错误
30
+
31
+ > 📌 以下规则仅约束 LLM 容易跑偏的地方。LLM 天然写对的内容(命名、代码空行分隔等)不再列出。
32
+
33
+ - 局部变量优先用 `var`,避免冗余的类型声明
34
+ - 字符串格式化用 `formatted()` / Text Block,不用 `+` 拼接
35
+ - 用多态 / Abstract Method 替代 `instanceof` 链做类型分发
64
36
 
65
37
  ### Lombok 使用规范
66
38
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deepstorm/cli",
3
- "version": "0.3.2",
3
+ "version": "0.4.0",
4
4
  "description": "DeepStorm CLI — 一键配置项目开发环境",
5
5
  "license": "MIT",
6
6
  "author": "billkang",