ai-engineering-init 1.15.0 → 1.16.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.
@@ -25,81 +25,114 @@ description: |
25
25
 
26
26
  ## 多环境配置
27
27
 
28
- 配置文件:`.claude/skills/loki-log-query/environments.json`
28
+ ### 配置文件查找顺序
29
29
 
30
- ### 环境列表
30
+ ```
31
+ 1. 本地项目:.claude/loki-config.json(新格式)
32
+ 2. 本地项目:.claude/skills/loki-log-query/environments.json(旧格式,兼容)
33
+ 3. 全局配置:~/.claude/loki-config.json
34
+ ```
35
+
36
+ 本地配置优先。全局配置推荐用 `npx ai-engineering-init config --type loki --scope global` 创建。
37
+
38
+ ### 配置结构(支持 range 范围匹配)
39
+
40
+ ```json
41
+ {
42
+ "active": "monitor-dev",
43
+ "environments": {
44
+ "monitor-dev": {
45
+ "name": "Monitor 开发环境",
46
+ "url": "https://monitor-dev.xnzn.net/grafana",
47
+ "token": "glsa_xxx",
48
+ "aliases": ["mdev", "dev"],
49
+ "range": "dev1~15",
50
+ "projects": ["dev01","dev02","dev03","dev04","dev05","dev06","dev07","dev08","dev09","dev10","dev11","dev12","dev13","dev14","dev15"]
51
+ }
52
+ }
53
+ }
54
+ ```
55
+
56
+ ### 环境匹配规则(含 range 范围匹配)
31
57
 
32
- | 环境别名 | 名称 | URL | 快捷词 |
33
- |----------|------|-----|--------|
34
- | `test13` | 测试13(主测试环境) | `https://test13.xnzn.net/grafana` | test13, 13 |
35
- | `monitor-test` | Monitor 测试环境 | `https://monitor-test.xnzn.net/grafana` | mtest |
36
- | `monitor-dev` | Monitor 开发环境 | `https://monitor-dev.xnzn.net/grafana` | mdev, dev |
37
- | `monitor02-dev` | Monitor02 开发环境 | `https://monitor02-dev.xnzn.net/grafana` | m02, monitor02 |
38
- | `monitor-tyy-dev` | Monitor 体验园开发环境 | `https://monitor-tyy-dev.xnzn.net/grafana` | tyy, 体验园 |
58
+ 用户说的话 匹配逻辑:
39
59
 
40
- ### 环境匹配规则
60
+ | 用户说法 | 匹配方式 | 结果 |
61
+ |---------|---------|------|
62
+ | "查 test13 的日志" | 精确匹配 key 或 aliases | `test13` 环境 |
63
+ | "去 dev10 查" | **range 匹配**:dev10 在 monitor-dev 的 projects 中 | `monitor-dev` 环境,`project="dev10"` |
64
+ | "去 monitor-dev 查" | 精确匹配 key | `monitor-dev` 环境 |
65
+ | "切到体验园" | aliases 匹配 | `monitor-tyy-dev` 环境 |
66
+ | 未指定环境 | 使用 `active` 字段 | 默认活跃环境 |
41
67
 
42
- 用户说的话 → 匹配环境:
43
- - "查 test13 的日志" → `test13`
44
- - "去 monitor-dev 查" → `monitor-dev`
45
- - "切到体验园"`monitor-tyy-dev`
46
- - "去 m02 查一下" → `monitor02-dev`
47
- - 未指定环境使用 `active` 字段指定的默认环境
68
+ **range 匹配算法**:
69
+ ```
70
+ 1. 用户输入 "dev10"
71
+ 2. 先精确匹配 key / aliases 未命中
72
+ 3. 遍历所有环境的 projects 列表,检查是否包含 "dev10"
73
+ 4. 命中使用该环境,并自动添加 project="dev10" 标签过滤
74
+ ```
48
75
 
49
- ### 读取配置
76
+ ### 读取配置(含全局降级 + range 匹配)
50
77
 
51
78
  ```bash
52
- SKILL_DIR="$CLAUDE_PROJECT_DIR/.claude/skills/loki-log-query"
53
- ENV_FILE="${SKILL_DIR}/environments.json"
54
-
55
- # 读取指定环境(参数: 环境别名)
56
- read_env() {
57
- local ENV_KEY="${1:-$(python3 -c "import json; print(json.load(open('${ENV_FILE}'))['active'])")}"
58
- GRAFANA_URL=$(python3 -c "import json; print(json.load(open('${ENV_FILE}'))['environments']['${ENV_KEY}']['url'])")
59
- TOKEN=$(python3 -c "import json; print(json.load(open('${ENV_FILE}'))['environments']['${ENV_KEY}']['token'])")
60
- API="${GRAFANA_URL}/api/datasources/proxy/uid/loki/loki/api/v1"
61
- echo "Environment: ${ENV_KEY} → ${GRAFANA_URL}"
79
+ # 按优先级查找配置文件
80
+ find_config() {
81
+ local PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
82
+ for f in \
83
+ "${PROJECT_DIR}/.claude/loki-config.json" \
84
+ "${PROJECT_DIR}/.claude/skills/loki-log-query/environments.json" \
85
+ "${HOME}/.claude/loki-config.json"; do
86
+ [ -f "$f" ] && echo "$f" && return
87
+ done
88
+ echo ""
62
89
  }
63
90
 
64
- # 通过别名查找环境 key
91
+ ENV_FILE=$(find_config)
92
+
93
+ # 通过别名或 range 查找环境 key + project
65
94
  find_env() {
66
95
  python3 -c "
67
- import json
96
+ import json, re
68
97
  data = json.load(open('${ENV_FILE}'))
69
98
  alias = '${1}'.lower()
99
+ # 1. 精确匹配 key 或 aliases
70
100
  for key, env in data['environments'].items():
71
101
  if alias == key or alias in env.get('aliases', []):
72
- print(key)
73
- break
74
- else:
75
- print(data['active'])
102
+ print(f'{key}|') # key|project(project 为空)
103
+ exit()
104
+ # 2. range 匹配:在 projects 列表中查找
105
+ for key, env in data['environments'].items():
106
+ if alias in env.get('projects', []):
107
+ print(f'{key}|{alias}')
108
+ exit()
109
+ # 3. 未命中,返回默认
110
+ print(f'{data[\"active\"]}|')
76
111
  "
77
112
  }
113
+
114
+ # 使用示例:
115
+ # result=$(find_env "dev10")
116
+ # ENV_KEY=$(echo "$result" | cut -d'|' -f1) # monitor-dev
117
+ # PROJECT=$(echo "$result" | cut -d'|' -f2) # dev10
118
+ # 查询时:{app="yunshitang",project="${PROJECT}"}
78
119
  ```
79
120
 
80
- ### 切换活跃环境
121
+ ### 切换活跃环境 / 更新 Token
81
122
 
82
123
  ```bash
83
- # 切换默认环境为 monitor-dev
84
124
  python3 -c "
85
125
  import json
86
126
  data = json.load(open('${ENV_FILE}'))
87
127
  data['active'] = 'monitor-dev'
88
128
  json.dump(data, open('${ENV_FILE}', 'w'), indent=2, ensure_ascii=False)
89
- print('Switched to:', data['active'])
90
129
  "
91
- ```
92
130
 
93
- ### 更新 Token
94
-
95
- ```bash
96
- # 为某个环境设置 Token
97
131
  python3 -c "
98
132
  import json
99
133
  data = json.load(open('${ENV_FILE}'))
100
- data['environments']['monitor-dev']['token'] = 'glsa_新的token'
134
+ data['environments']['monitor-dev']['token'] = 'glsa_token'
101
135
  json.dump(data, open('${ENV_FILE}', 'w'), indent=2, ensure_ascii=False)
102
- print('Token updated for monitor-dev')
103
136
  "
104
137
  ```
105
138
 
@@ -27,22 +27,27 @@ description: |
27
27
 
28
28
  ---
29
29
 
30
- ## 连接信息获取(三级降级,自动查找)
30
+ ## 连接信息获取(四级降级,自动查找)
31
31
 
32
32
  当本技能被激活时,**按以下优先级获取数据库连接信息**:
33
33
 
34
34
  ### 优先级 1:用户对话中指定(最高优先级)
35
35
 
36
36
  用户直接给出连接信息,或指定环境名:
37
- - "连 dev 环境查一下" → 使用 `.claude/mysql-config.json` 中 dev 环境的配置
37
+ - "连 dev 环境查一下" → 使用配置中 dev 环境
38
+ - "去 dev10 查" → **范围匹配** dev 环境(range: "1~15")
38
39
  - 直接给出 host/port/user/password → 直接使用
39
40
 
40
- ### 优先级 2:`.claude/mysql-config.json`(显式配置,可选)
41
+ ### 优先级 2:本地项目 `.claude/mysql-config.json`
41
42
 
42
- 如果文件存在且当前环境的 password 不是占位符 `YOUR_PASSWORD`,使用该配置。
43
- **此文件为可选**,主要用于连接非本地环境(dev/prod 远程数据库)。
43
+ 项目目录下的配置文件,优先于全局配置。
44
44
 
45
- ### 优先级 3:工程配置文件(零配置,本地开发默认)
45
+ ### 优先级 3:全局 `~/.claude/mysql-config.json`
46
+
47
+ 全局配置文件(通过 `npx ai-engineering-init config --scope global` 创建),所有项目共享。
48
+ **推荐**:公司统一的数据库连接信息放全局,项目特定的覆盖放本地。
49
+
50
+ ### 优先级 4:工程配置文件(零配置,本地开发默认)
46
51
 
47
52
  从项目的 `bootstrap-dev.yml` 中自动提取连接信息:
48
53
 
@@ -107,43 +112,60 @@ brew install mysql-client
107
112
 
108
113
  ## 多环境支持
109
114
 
110
- ### 配置文件结构
115
+ ### 配置文件查找顺序
116
+
117
+ ```
118
+ 1. 本地项目:.claude/mysql-config.json
119
+ 2. 全局配置:~/.claude/mysql-config.json
120
+ ```
121
+
122
+ 本地配置优先。如果本地无配置,使用全局。两者都存在时,本地覆盖全局(同名环境取本地值)。
111
123
 
112
- `.claude/mysql-config.json` 支持多环境配置:
124
+ ### 配置文件结构(支持 range 范围匹配)
113
125
 
114
126
  ```json
115
127
  {
116
128
  "environments": {
117
- "local": { "host": "127.0.0.1", "port": 3306, "user": "root", "password": "xxx" },
118
- "dev": { "host": "dev-db.example.com", "port": 3306, "user": "dev_user", "password": "xxx" },
129
+ "local": { "host": "127.0.0.1", "port": 3306, "user": "root", "password": "xxx", "_desc": "本地环境" },
130
+ "dev": { "host": "dev-db.example.com", "port": 3306, "user": "dev_user", "password": "xxx", "range": "1~15", "_desc": "覆盖 dev1→dev15" },
131
+ "test": { "host": "test-db.example.com", "port": 3306, "user": "test_user", "password": "xxx", "range": "1~30", "_desc": "覆盖 test1→test30" },
119
132
  "prod": { "host": "prod-db.example.com", "port": 3306, "user": "readonly", "password": "xxx" }
120
133
  },
121
134
  "default": "local"
122
135
  }
123
136
  ```
124
137
 
138
+ ### 环境范围匹配(range)
139
+
140
+ `range` 字段支持 `起始~结束` 格式,用于一个配置覆盖多个编号环境:
141
+
142
+ | 用户说法 | 匹配逻辑 | 结果 |
143
+ |---------|---------|------|
144
+ | "去 dev10 查" | 提取前缀 `dev`、编号 `10`,检查 dev 环境 range "1~15",10 在范围内 | 使用 dev 配置 |
145
+ | "连 test25" | 提取前缀 `test`、编号 `25`,检查 test 环境 range "1~30",25 在范围内 | 使用 test 配置 |
146
+ | "连 prod" | 精确匹配 prod 环境 | 使用 prod 配置 |
147
+
148
+ **匹配算法**:
149
+ ```
150
+ 1. 用户输入的环境名(如 "dev10")
151
+ 2. 先精确匹配环境 key(environments["dev10"])
152
+ 3. 未命中 → 拆分为前缀+编号("dev" + 10)
153
+ 4. 查找有 range 字段的环境,前缀匹配 + 编号在范围内
154
+ 5. 命中 → 使用该环境的 host/port/user/password
155
+ ```
156
+
125
157
  ### 环境选择规则
126
158
 
127
159
  | 优先级 | 来源 | 示例 |
128
160
  |--------|------|------|
129
- | 1(最高) | 用户对话中指定 | "连 dev 环境查一下"、"用生产库看看" |
161
+ | 1(最高) | 用户对话中指定 | "连 dev10 环境查一下" |
130
162
  | 2 | 配置文件 `default` 字段 | `"default": "local"` |
131
163
 
132
- ### 环境关键词映射
133
-
134
- 用户说的话 → 对应环境名:
135
-
136
- | 用户说法 | 环境 |
137
- |---------|------|
138
- | "本地"、"local"、"本地环境" | `local` |
139
- | "开发"、"dev"、"测试环境"、"开发环境" | `dev` |
140
- | "生产"、"prod"、"线上"、"正式环境" | `prod` |
141
-
142
164
  ### 连接示例
143
165
 
144
166
  ```bash
145
- # 用户说"连 dev 环境查一下 order_info"
146
- # → 读取 environments.dev 的连接信息 + 日志提取的数据库名
167
+ # 用户说"连 dev10 环境查一下 order_info"
168
+ # → range 匹配到 dev 环境的连接信息 + 日志提取的数据库名
147
169
  mysql -h dev-db.example.com -P 3306 -u dev_user -p'xxx' 546198574447230976 -e "SELECT ..."
148
170
  ```
149
171
 
package/bin/index.js CHANGED
@@ -52,6 +52,9 @@ let targetDir = process.cwd();
52
52
  let force = false;
53
53
  let skillFilter = ''; // sync-back --skill <名称>
54
54
  let submitIssue = false; // sync-back --submit
55
+ let configType = ''; // config --type <mysql|loki|all>
56
+ let configScope = ''; // config --scope <local|global>
57
+ let configAdd = false; // config --add
55
58
 
56
59
  for (let i = 0; i < args.length; i++) {
57
60
  const arg = args[i];
@@ -101,6 +104,23 @@ for (let i = 0; i < args.length; i++) {
101
104
  case '--submit':
102
105
  submitIssue = true;
103
106
  break;
107
+ case '--type':
108
+ if (i + 1 >= args.length || args[i + 1].startsWith('-')) {
109
+ console.error(fmt('red', `错误:${arg} 需要一个值(mysql | loki | all)`));
110
+ process.exit(1);
111
+ }
112
+ configType = args[++i];
113
+ break;
114
+ case '--scope':
115
+ if (i + 1 >= args.length || args[i + 1].startsWith('-')) {
116
+ console.error(fmt('red', `错误:${arg} 需要一个值(local | global)`));
117
+ process.exit(1);
118
+ }
119
+ configScope = args[++i];
120
+ break;
121
+ case '--add':
122
+ configAdd = true;
123
+ break;
104
124
  case '--help': case '-h':
105
125
  printHelp();
106
126
  process.exit(0);
@@ -122,7 +142,7 @@ function printHelp() {
122
142
  console.log(` ${fmt('bold', 'update')} 更新已安装的框架文件(跳过用户自定义文件)`);
123
143
  console.log(` ${fmt('bold', 'global')} 全局安装到 ~/.claude / ~/.cursor 等,对所有项目生效`);
124
144
  console.log(` ${fmt('bold', 'sync-back')} 对比本地技能修改,生成 diff 或提交 GitHub Issue`);
125
- console.log(` ${fmt('bold', 'config')} 初始化数据库配置文件(.claude/mysql-config.json)`);
145
+ console.log(` ${fmt('bold', 'config')} 初始化环境配置(数据库连接 / Loki 日志 / 全部)`);
126
146
  console.log(` ${fmt('bold', 'mcp')} MCP 服务器管理(安装/卸载/状态检查)\n`);
127
147
  console.log(`无命令时显示交互式主菜单。\n`);
128
148
  console.log('选项:');
@@ -131,6 +151,9 @@ function printHelp() {
131
151
  console.log(' --force, -f 强制覆盖(init 时覆盖已有文件;update/global 时同时更新保留文件)');
132
152
  console.log(' --skill, -s <技能> sync-back 时只对比指定技能');
133
153
  console.log(' --submit sync-back 时自动创建 GitHub Issue(需要 gh CLI)');
154
+ console.log(' --type <类型> config 时指定配置类型: mysql | loki | all');
155
+ console.log(' --scope <范围> config 时指定范围: local(当前项目) | global(全局 ~/)');
156
+ console.log(' --add config 时追加环境(不覆盖已有配置)');
134
157
  console.log(' --help, -h 显示此帮助\n');
135
158
  console.log('示例:');
136
159
  console.log(' npx ai-engineering-init --tool claude');
@@ -144,6 +167,12 @@ function printHelp() {
144
167
  console.log(' npx ai-engineering-init sync-back --tool claude # 只扫描 Claude');
145
168
  console.log(' npx ai-engineering-init sync-back --skill bug-detective # 只对比指定技能');
146
169
  console.log(' npx ai-engineering-init sync-back --skill bug-detective --submit # 提交 Issue');
170
+ console.log(' npx ai-engineering-init config # 交互式选择配置类型');
171
+ console.log(' npx ai-engineering-init config --type mysql # 只配置数据库连接');
172
+ console.log(' npx ai-engineering-init config --type loki # 只配置 Loki 日志');
173
+ console.log(' npx ai-engineering-init config --type all # 配置全部');
174
+ console.log(' npx ai-engineering-init config --type mysql --scope global # 全局配置(所有项目共享)');
175
+ console.log(' npx ai-engineering-init config --type mysql --add # 追加环境到已有配置');
147
176
  }
148
177
 
149
178
  // ── 工具定义(init 用)────────────────────────────────────────────────────
@@ -1119,162 +1148,479 @@ function runSyncBack(selectedTool, selectedSkill, doSubmit) {
1119
1148
  console.log('');
1120
1149
  }
1121
1150
 
1122
- // ── 数据库配置初始化 ──────────────────────────────────────────────────────
1151
+ // ── 环境配置初始化(MySQL / Loki)─────────────────────────────────────────
1152
+
1123
1153
  function runConfig() {
1124
1154
  if (!process.stdin.isTTY) {
1125
1155
  console.error(fmt('red', '错误:config 命令需要交互式终端'));
1126
1156
  process.exit(1);
1127
1157
  }
1128
1158
 
1129
- const configPath = path.join(targetDir, '.claude', 'mysql-config.json');
1130
1159
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
1131
-
1132
1160
  const ask = (question) => new Promise((resolve) => {
1133
1161
  rl.question(question, (answer) => resolve(answer.trim()));
1134
1162
  });
1135
1163
 
1136
- const ENV_DEFAULTS = {
1137
- local: { host: '127.0.0.1', user: 'root', desc: '本地开发环境' },
1138
- dev: { host: '', user: '', desc: '开发测试环境' },
1139
- test: { host: '', user: '', desc: '测试环境' },
1140
- prod: { host: '', user: '', desc: '生产环境' },
1141
- };
1142
-
1143
1164
  (async () => {
1144
1165
  try {
1145
- // 1. 检测已有配置
1146
- if (fs.existsSync(configPath)) {
1147
- console.log(fmt('yellow', `⚠ 配置文件已存在:${configPath}`));
1148
- const overwrite = await ask(fmt('bold', '是否重新配置?[y/N]: '));
1149
- if (overwrite.toLowerCase() !== 'y') {
1150
- console.log('已取消。');
1151
- rl.close();
1152
- return;
1153
- }
1154
- console.log('');
1155
- }
1166
+ let type = configType;
1167
+ let scope = configScope;
1156
1168
 
1157
- // 2. 选择环境
1158
- console.log(fmt('cyan', '请选择要配置的数据库环境(多选,用逗号分隔):'));
1159
- console.log('');
1160
- console.log(` ${fmt('bold', '1')}) local — 本地开发环境`);
1161
- console.log(` ${fmt('bold', '2')}) dev 开发测试环境`);
1162
- console.log(` ${fmt('bold', '3')}) test 测试环境`);
1163
- console.log(` ${fmt('bold', '4')}) prod 生产环境`);
1164
- console.log('');
1165
- const envAnswer = await ask(fmt('bold', '请输入选项(如 1,2 或 1-3): '));
1166
-
1167
- // 解析选择
1168
- const envNames = ['local', 'dev', 'test', 'prod'];
1169
- const selected = new Set();
1170
- for (const part of envAnswer.split(',')) {
1171
- const trimmed = part.trim();
1172
- const rangeMatch = trimmed.match(/^(\d)-(\d)$/);
1173
- if (rangeMatch) {
1174
- const start = parseInt(rangeMatch[1], 10);
1175
- const end = parseInt(rangeMatch[2], 10);
1176
- for (let n = start; n <= end; n++) {
1177
- if (n >= 1 && n <= 4) selected.add(envNames[n - 1]);
1178
- }
1179
- } else {
1180
- const n = parseInt(trimmed, 10);
1181
- if (n >= 1 && n <= 4) selected.add(envNames[n - 1]);
1169
+ // 未指定 --type 时显示交互式菜单
1170
+ if (!type) {
1171
+ console.log(fmt('cyan', '请选择要初始化的配置类型:'));
1172
+ console.log('');
1173
+ console.log(` ${fmt('bold', '1')}) ${fmt('green', 'MySQL 数据库连接')} 配置 mysql-config.json`);
1174
+ console.log(` ${fmt('bold', '2')}) ${fmt('blue', 'Loki 日志查询')} 配置 Grafana Loki Token`);
1175
+ console.log(` ${fmt('bold', '3')}) ${fmt('yellow','全部配置')} 依次配置 MySQL + Loki`);
1176
+ console.log('');
1177
+ const answer = await ask(fmt('bold', '请输入选项 [1-3]: '));
1178
+ switch (answer) {
1179
+ case '1': type = 'mysql'; break;
1180
+ case '2': type = 'loki'; break;
1181
+ case '3': type = 'all'; break;
1182
+ default:
1183
+ console.error(fmt('red', '无效选项,退出。'));
1184
+ rl.close();
1185
+ process.exit(1);
1182
1186
  }
1187
+ console.log('');
1183
1188
  }
1184
1189
 
1185
- const selectedEnvs = [...selected];
1186
- if (selectedEnvs.length === 0) {
1187
- console.error(fmt('red', '未选择任何环境,退出。'));
1190
+ if (!['mysql', 'loki', 'all'].includes(type)) {
1191
+ console.error(fmt('red', `错误:不支持的配置类型 "${type}",可选:mysql | loki | all`));
1188
1192
  rl.close();
1189
1193
  process.exit(1);
1190
1194
  }
1191
1195
 
1192
- console.log('');
1193
- console.log(fmt('green', `已选择环境:${selectedEnvs.join(', ')}`));
1194
- console.log('');
1195
-
1196
- // 3. 收集每个环境的配置
1197
- const environments = {};
1198
- for (const env of selectedEnvs) {
1199
- const defaults = ENV_DEFAULTS[env];
1200
- console.log(fmt('cyan', `── ${env} 环境配置 ──`));
1201
-
1202
- const host = await ask(` host [${defaults.host || '无默认'}]: `) || defaults.host;
1203
- const port = await ask(' port [3306]: ') || '3306';
1204
- const user = await ask(` user [${defaults.user || '无默认'}]: `) || defaults.user;
1205
- const password = await ask(' password: ');
1206
- const desc = await ask(` 描述 [${defaults.desc}]: `) || defaults.desc;
1196
+ // 未指定 --scope 时询问
1197
+ if (!scope) {
1198
+ console.log(fmt('cyan', '请选择配置范围:'));
1199
+ console.log('');
1200
+ console.log(` ${fmt('bold', '1')}) ${fmt('green', 'global(全局)')} — 写入 ~/.claude/,所有项目共享`);
1201
+ console.log(` ${fmt('bold', '2')}) ${fmt('blue', 'local(本地)')} — 写入当前项目目录`);
1202
+ console.log('');
1203
+ const scopeAnswer = await ask(fmt('bold', '请输入选项 [1-2,默认 1]: ')) || '1';
1204
+ scope = scopeAnswer === '2' ? 'local' : 'global';
1207
1205
  console.log('');
1208
-
1209
- if (!host) {
1210
- console.error(fmt('red', `错误:${env} 环境的 host 不能为空`));
1211
- rl.close();
1212
- process.exit(1);
1213
- }
1214
- if (!user) {
1215
- console.error(fmt('red', `错误:${env} 环境的 user 不能为空`));
1216
- rl.close();
1217
- process.exit(1);
1218
- }
1219
-
1220
- environments[env] = {
1221
- host,
1222
- port: parseInt(port, 10),
1223
- user,
1224
- password,
1225
- description: desc,
1226
- };
1227
1206
  }
1228
1207
 
1229
- // 4. 选择默认环境
1230
- let defaultEnv = selectedEnvs[0];
1231
- if (selectedEnvs.length > 1) {
1232
- console.log(fmt('cyan', '请选择默认环境:'));
1233
- selectedEnvs.forEach((env, i) => {
1234
- console.log(` ${fmt('bold', String(i + 1))}) ${env}`);
1235
- });
1236
- const defaultAnswer = await ask(fmt('bold', `请输入选项 [1-${selectedEnvs.length}]: `));
1237
- const idx = parseInt(defaultAnswer, 10) - 1;
1238
- if (idx >= 0 && idx < selectedEnvs.length) {
1239
- defaultEnv = selectedEnvs[idx];
1240
- }
1241
- console.log('');
1208
+ if (!['local', 'global'].includes(scope)) {
1209
+ console.error(fmt('red', `错误:不支持的范围 "${scope}",可选:local | global`));
1210
+ rl.close();
1211
+ process.exit(1);
1242
1212
  }
1243
1213
 
1244
- // 5. 写入配置文件
1245
- const config = { defaultEnv, environments };
1246
- const claudeDir = path.join(targetDir, '.claude');
1247
- if (!fs.existsSync(claudeDir)) {
1248
- fs.mkdirSync(claudeDir, { recursive: true });
1214
+ const isGlobal = scope === 'global';
1215
+ if (isGlobal) {
1216
+ console.log(fmt('magenta', `配置范围:全局(~/.claude/),所有项目共享`));
1217
+ } else {
1218
+ console.log(fmt('magenta', `配置范围:本地(${targetDir})`));
1249
1219
  }
1250
- fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
1251
- console.log(fmt('green', `✔ 配置已写入:${configPath}`));
1252
-
1253
- // 6. 确保 .gitignore 包含该文件
1254
- const gitignorePath = path.join(targetDir, '.gitignore');
1255
- const ignoreEntry = '.claude/mysql-config.json';
1256
- let needAppend = true;
1257
- if (fs.existsSync(gitignorePath)) {
1258
- const content = fs.readFileSync(gitignorePath, 'utf-8');
1259
- if (content.split('\n').some(line => line.trim() === ignoreEntry)) {
1260
- needAppend = false;
1261
- }
1220
+ console.log('');
1221
+
1222
+ if (type === 'mysql' || type === 'all') {
1223
+ await runMysqlConfig(ask, isGlobal);
1262
1224
  }
1263
- if (needAppend) {
1264
- const separator = fs.existsSync(gitignorePath) ? '\n' : '';
1265
- fs.appendFileSync(gitignorePath, `${separator}${ignoreEntry}\n`, 'utf-8');
1266
- console.log(fmt('green', `✔ 已添加 ${ignoreEntry} 到 .gitignore`));
1225
+ if (type === 'loki' || type === 'all') {
1226
+ if (type === 'all') console.log('');
1227
+ await runLokiConfig(ask, isGlobal);
1267
1228
  }
1268
1229
 
1269
1230
  console.log('');
1270
- console.log(fmt('green', '数据库配置初始化完成!'));
1271
- console.log(`使用 ${fmt('bold', 'mysql-debug')} 技能时将自动读取此配置。`);
1231
+ console.log(fmt('green', fmt('bold', '配置初始化完成!')));
1232
+ if (isGlobal) {
1233
+ console.log(fmt('cyan', '技能会按 全局(~/.claude/) → 本地(.claude/) 顺序查找配置,本地优先。'));
1234
+ }
1272
1235
  } finally {
1273
1236
  rl.close();
1274
1237
  }
1275
1238
  })();
1276
1239
  }
1277
1240
 
1241
+ // ── MySQL 数据库配置 ────────────────────────────────────────────────────────
1242
+
1243
+ async function runMysqlConfig(ask, isGlobal) {
1244
+ console.log(fmt('blue', fmt('bold', '┌─ MySQL 数据库连接配置 ─┐')));
1245
+ console.log('');
1246
+
1247
+ const configPath = isGlobal
1248
+ ? path.join(HOME_DIR, '.claude', 'mysql-config.json')
1249
+ : path.join(targetDir, '.claude', 'mysql-config.json');
1250
+
1251
+ // 读取已有配置
1252
+ let existingConfig = null;
1253
+ if (fs.existsSync(configPath)) {
1254
+ try { existingConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8')); } catch { /* ignore */ }
1255
+ }
1256
+
1257
+ // --add 模式
1258
+ if (configAdd && existingConfig && existingConfig.environments) {
1259
+ console.log(fmt('cyan', '当前已配置的环境:'));
1260
+ for (const [key, env] of Object.entries(existingConfig.environments)) {
1261
+ const rangeStr = env.range ? fmt('magenta', ` (range: ${env.range})`) : '';
1262
+ console.log(` ${fmt('bold', key)} — ${env._desc || key}${rangeStr} host=${env.host}`);
1263
+ }
1264
+ console.log('');
1265
+ console.log(fmt('green', '将追加新环境到已有配置。'));
1266
+ console.log('');
1267
+ } else if (existingConfig && !configAdd) {
1268
+ console.log(fmt('yellow', `⚠ 配置文件已存在:${configPath}`));
1269
+ const overwrite = await ask(fmt('bold', '输入 add 追加环境,y 重建,N 跳过 [add/y/N]: '));
1270
+ if (overwrite.toLowerCase() === 'add') {
1271
+ // 进入追加模式
1272
+ } else if (overwrite.toLowerCase() !== 'y') {
1273
+ console.log('已跳过 MySQL 配置。');
1274
+ return;
1275
+ } else {
1276
+ existingConfig = null;
1277
+ }
1278
+ console.log('');
1279
+ }
1280
+
1281
+ // 自定义环境名输入
1282
+ console.log(fmt('cyan', '请输入要配置的环境(自定义名称,多个用逗号分隔):'));
1283
+ console.log(fmt('yellow', ' 示例:local, dev, test, prod 或自定义名称'));
1284
+ console.log('');
1285
+ const envAnswer = await ask(fmt('bold', '环境名称: '));
1286
+ const envNames = envAnswer.split(',').map(s => s.trim()).filter(Boolean);
1287
+
1288
+ if (envNames.length === 0) {
1289
+ console.error(fmt('red', '未输入任何环境名,跳过 MySQL 配置。'));
1290
+ return;
1291
+ }
1292
+ console.log('');
1293
+
1294
+ // 收集每个环境的配置
1295
+ const newEnvironments = {};
1296
+ for (const env of envNames) {
1297
+ if (existingConfig && existingConfig.environments && existingConfig.environments[env]) {
1298
+ console.log(fmt('yellow', ` ${env} 已存在,跳过。使用 y 模式可重建。`));
1299
+ continue;
1300
+ }
1301
+
1302
+ const isLocal = env === 'local';
1303
+ console.log(fmt('cyan', `── ${env} 环境配置 ──`));
1304
+
1305
+ const host = await ask(` host [${isLocal ? '127.0.0.1' : '无默认'}]: `) || (isLocal ? '127.0.0.1' : '');
1306
+ if (!host) { console.error(fmt('red', ` host 不能为空,跳过。`)); continue; }
1307
+ const port = await ask(' port [3306]: ') || '3306';
1308
+ const user = await ask(` user [${isLocal ? 'root' : '无默认'}]: `) || (isLocal ? 'root' : '');
1309
+ if (!user) { console.error(fmt('red', ` user 不能为空,跳过。`)); continue; }
1310
+ const password = await ask(' password: ');
1311
+ const desc = await ask(` 描述 [${env}环境]: `) || `${env}环境`;
1312
+ const rangeInput = await ask(` 覆盖范围(如 ${fmt('bold', '1~15')} 表示 ${env}1→${env}15,留空=无范围): `);
1313
+ console.log('');
1314
+
1315
+ newEnvironments[env] = { host, port: parseInt(port, 10), user, password, _desc: desc };
1316
+ if (rangeInput) newEnvironments[env].range = rangeInput;
1317
+ }
1318
+
1319
+ if (Object.keys(newEnvironments).length === 0 && !existingConfig) {
1320
+ console.error(fmt('red', '未成功配置任何环境。'));
1321
+ return;
1322
+ }
1323
+
1324
+ // 合并配置
1325
+ const allEnvironments = {
1326
+ ...(existingConfig && existingConfig.environments ? existingConfig.environments : {}),
1327
+ ...newEnvironments,
1328
+ };
1329
+
1330
+ // 选择默认环境
1331
+ const allEnvKeys = Object.keys(allEnvironments);
1332
+ let defaultEnv = (existingConfig && existingConfig.default) || allEnvKeys[0];
1333
+ if (Object.keys(newEnvironments).length > 0 && allEnvKeys.length > 1) {
1334
+ console.log(fmt('cyan', '请选择默认环境:'));
1335
+ allEnvKeys.forEach((env, i) => {
1336
+ const marker = env === defaultEnv ? fmt('green', ' (当前)') : '';
1337
+ console.log(` ${fmt('bold', String(i + 1))}) ${env}${marker}`);
1338
+ });
1339
+ const defaultAnswer = await ask(fmt('bold', `请输入选项 [1-${allEnvKeys.length},回车保持当前]: `));
1340
+ if (defaultAnswer) {
1341
+ const idx = parseInt(defaultAnswer, 10) - 1;
1342
+ if (idx >= 0 && idx < allEnvKeys.length) defaultEnv = allEnvKeys[idx];
1343
+ }
1344
+ console.log('');
1345
+ }
1346
+
1347
+ const config = {
1348
+ environments: allEnvironments,
1349
+ default: defaultEnv,
1350
+ _comment: '环境支持 range 字段(如 "1~15"),用户说"dev10"时自动匹配。查找顺序:本地 .claude/ > 全局 ~/.claude/',
1351
+ };
1352
+
1353
+ const dir = path.dirname(configPath);
1354
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
1355
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
1356
+ console.log(` ${fmt('green', '✔')} 已写入:${configPath}`);
1357
+
1358
+ if (!isGlobal) ensureGitignore(['mysql-config.json']);
1359
+
1360
+ console.log('');
1361
+ console.log(fmt('green', 'MySQL 数据库配置完成!'));
1362
+ for (const [key, env] of Object.entries(newEnvironments)) {
1363
+ if (env.range) {
1364
+ console.log(fmt('cyan', ` ${key} 覆盖 ${key}${env.range.replace('~', '→')},说"${key}10"将自动匹配`));
1365
+ }
1366
+ }
1367
+ }
1368
+
1369
+ // ── Loki 日志查询配置 ──────────────────────────────────────────────────────
1370
+
1371
+ async function runLokiConfig(ask, isGlobal) {
1372
+ console.log(fmt('blue', fmt('bold', '┌─ Loki 日志查询配置 ─┐')));
1373
+ console.log('');
1374
+
1375
+ const configPath = isGlobal
1376
+ ? path.join(HOME_DIR, '.claude', 'loki-config.json')
1377
+ : getLokiConfigPath();
1378
+
1379
+ if (!configPath) {
1380
+ console.log(fmt('yellow', '⚠ 未检测到配置目录。请先运行 init 安装框架。'));
1381
+ return;
1382
+ }
1383
+
1384
+ let existingConfig = null;
1385
+ if (fs.existsSync(configPath)) {
1386
+ try { existingConfig = JSON.parse(fs.readFileSync(configPath, 'utf-8')); } catch { /* ignore */ }
1387
+ }
1388
+
1389
+ if (existingConfig && existingConfig.environments) {
1390
+ const envs = existingConfig.environments;
1391
+ const envList = Object.keys(envs);
1392
+ console.log(fmt('cyan', '当前已配置的 Loki 环境:'));
1393
+ console.log('');
1394
+ for (const key of envList) {
1395
+ const env = envs[key];
1396
+ const hasToken = env.token && env.token.length > 0;
1397
+ const status = hasToken ? fmt('green', '✔ Token') : fmt('red', '✗ 缺Token');
1398
+ const rangeStr = env.range ? fmt('magenta', ` (range: ${env.range})`) : '';
1399
+ console.log(` ${fmt('bold', key)} — ${env.name || key} ${status}${rangeStr}`);
1400
+ }
1401
+ console.log('');
1402
+
1403
+ if (configAdd) {
1404
+ console.log(fmt('green', '将追加新环境到已有配置。'));
1405
+ } else {
1406
+ const missingTokenEnvs = envList.filter(k => !envs[k].token);
1407
+ const action = await ask(fmt('bold', '输入 token 补充Token,add 追加环境,N 跳过 [token/add/N]: ')) || 'token';
1408
+ if (action.toLowerCase() === 'token') {
1409
+ await updateLokiTokens(ask, existingConfig, configPath, isGlobal);
1410
+ return;
1411
+ } else if (action.toLowerCase() !== 'add') {
1412
+ console.log('已跳过 Loki 配置。');
1413
+ return;
1414
+ }
1415
+ }
1416
+ console.log('');
1417
+
1418
+ // 追加环境
1419
+ await addLokiEnvironments(ask, existingConfig, configPath, isGlobal);
1420
+ } else {
1421
+ await createLokiConfig(ask, configPath, isGlobal);
1422
+ }
1423
+ }
1424
+
1425
+ async function updateLokiTokens(ask, config, configPath, isGlobal) {
1426
+ console.log('');
1427
+ console.log(fmt('cyan', fmt('bold', 'Grafana Token 获取:')));
1428
+ console.log(` Grafana → Administration → Service accounts → Add(Viewer)→ Add token`);
1429
+ console.log('');
1430
+
1431
+ const envs = config.environments;
1432
+ for (const key of Object.keys(envs)) {
1433
+ const env = envs[key];
1434
+ const hasToken = env.token && env.token.length > 0;
1435
+ if (hasToken) {
1436
+ const update = await ask(` ${fmt('bold', key)} 已有 Token,更新?[y/N]: `);
1437
+ if (update.toLowerCase() !== 'y') continue;
1438
+ }
1439
+ if (env.url) console.log(` URL: ${fmt('bold', env.url)}`);
1440
+ const token = await ask(` 输入 ${key} 的 Token: `);
1441
+ if (token) {
1442
+ envs[key].token = token;
1443
+ console.log(` ${fmt('green', '✔')} 已设置`);
1444
+ }
1445
+ console.log('');
1446
+ }
1447
+
1448
+ writeLokiConfig(config, configPath, isGlobal);
1449
+ }
1450
+
1451
+ async function addLokiEnvironments(ask, config, configPath, isGlobal) {
1452
+ printLokiTokenGuide();
1453
+ const countAnswer = await ask(fmt('bold', '要追加几个环境?[1]: ')) || '1';
1454
+ const count = Math.max(1, Math.min(10, parseInt(countAnswer, 10) || 1));
1455
+ console.log('');
1456
+
1457
+ for (let i = 0; i < count; i++) {
1458
+ const envData = await collectLokiEnvInput(ask, i + 1, count);
1459
+ if (!envData) continue;
1460
+ config.environments[envData.key] = envData.value;
1461
+ }
1462
+
1463
+ writeLokiConfig(config, configPath, isGlobal);
1464
+ }
1465
+
1466
+ async function createLokiConfig(ask, configPath, isGlobal) {
1467
+ console.log(fmt('cyan', '将创建新的 Loki 日志查询配置。'));
1468
+ console.log('');
1469
+ printLokiTokenGuide();
1470
+ const countAnswer = await ask(fmt('bold', '要配置几个 Grafana 环境?[1]: ')) || '1';
1471
+ const count = Math.max(1, Math.min(10, parseInt(countAnswer, 10) || 1));
1472
+ console.log('');
1473
+
1474
+ const environments = {};
1475
+ let activeEnv = '';
1476
+
1477
+ for (let i = 0; i < count; i++) {
1478
+ const envData = await collectLokiEnvInput(ask, i + 1, count);
1479
+ if (!envData) continue;
1480
+ environments[envData.key] = envData.value;
1481
+ if (!activeEnv) activeEnv = envData.key;
1482
+ }
1483
+
1484
+ if (Object.keys(environments).length === 0) {
1485
+ console.error(fmt('red', '未配置任何环境。'));
1486
+ return;
1487
+ }
1488
+
1489
+ const envKeys = Object.keys(environments);
1490
+ if (envKeys.length > 1) {
1491
+ console.log(fmt('cyan', '请选择默认活跃环境:'));
1492
+ envKeys.forEach((env, i) => console.log(` ${fmt('bold', String(i + 1))}) ${env}`));
1493
+ const activeAnswer = await ask(fmt('bold', `请输入选项 [1-${envKeys.length}]: `));
1494
+ const idx = parseInt(activeAnswer, 10) - 1;
1495
+ if (idx >= 0 && idx < envKeys.length) activeEnv = envKeys[idx];
1496
+ console.log('');
1497
+ }
1498
+
1499
+ const config = {
1500
+ _comment: 'Loki 多环境配置。环境支持 range 字段。查找顺序:本地 > 全局 ~/.claude/',
1501
+ _setup: 'Token:Grafana → Administration → Service accounts → Add(Viewer)→ Add token',
1502
+ active: activeEnv,
1503
+ environments,
1504
+ };
1505
+
1506
+ writeLokiConfig(config, configPath, isGlobal);
1507
+ }
1508
+
1509
+ function printLokiTokenGuide() {
1510
+ console.log(fmt('cyan', fmt('bold', 'Grafana Token 获取:')));
1511
+ console.log(` Grafana → Administration → Service accounts → Add(Viewer)→ Add token`);
1512
+ console.log('');
1513
+ }
1514
+
1515
+ async function collectLokiEnvInput(ask, index, total) {
1516
+ console.log(fmt('cyan', `── 环境 ${index}/${total} ──`));
1517
+ const envKey = await ask(` 环境标识(如 monitor-dev、test13): `);
1518
+ if (!envKey) { console.log(fmt('yellow', ' 跳过')); return null; }
1519
+ const name = await ask(` 环境名称(如 "开发环境"): `) || envKey;
1520
+ const url = await ask(` Grafana URL: `);
1521
+ const token = await ask(` Token(可留空稍后配): `);
1522
+ const aliasStr = await ask(` 别名(逗号分隔)[${envKey}]: `) || envKey;
1523
+ const aliases = aliasStr.split(',').map(s => s.trim()).filter(Boolean);
1524
+ const rangeInput = await ask(` 项目覆盖范围(如 ${fmt('bold', 'dev1~15')} 表示 dev01→dev15,留空=无范围): `);
1525
+ console.log('');
1526
+
1527
+ const value = { name, url, token: token || '', aliases };
1528
+ if (rangeInput) {
1529
+ value.range = rangeInput;
1530
+ value.projects = expandRange(rangeInput);
1531
+ if (value.projects.length > 0) {
1532
+ console.log(fmt('cyan', ` 已展开 ${value.projects.length} 个项目:${value.projects.slice(0, 5).join(', ')}${value.projects.length > 5 ? '...' : ''}`));
1533
+ console.log('');
1534
+ }
1535
+ } else {
1536
+ value.projects = [];
1537
+ }
1538
+
1539
+ return { key: envKey, value };
1540
+ }
1541
+
1542
+ function writeLokiConfig(config, configPath, isGlobal) {
1543
+ const dir = path.dirname(configPath);
1544
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
1545
+ fs.writeFileSync(configPath, JSON.stringify(config, null, 2) + '\n', 'utf-8');
1546
+ console.log(` ${fmt('green', '✔')} 已写入:${configPath}`);
1547
+ if (!isGlobal) ensureGitignore(['loki-config.json', 'environments.json']);
1548
+ console.log('');
1549
+ console.log(fmt('green', 'Loki 日志查询配置完成!'));
1550
+ }
1551
+
1552
+ // ── Config 工具函数 ─────────────────────────────────────────────────────────
1553
+
1554
+ function getLokiConfigPath() {
1555
+ const lokiJsonClaude = path.join(targetDir, '.claude', 'loki-config.json');
1556
+ if (fs.existsSync(lokiJsonClaude)) return lokiJsonClaude;
1557
+ const envJsonClaude = path.join(targetDir, '.claude', 'skills', 'loki-log-query', 'environments.json');
1558
+ if (fs.existsSync(envJsonClaude)) return envJsonClaude;
1559
+ const envJsonCursor = path.join(targetDir, '.cursor', 'skills', 'loki-log-query', 'environments.json');
1560
+ if (fs.existsSync(envJsonCursor)) return envJsonCursor;
1561
+ if (fs.existsSync(path.join(targetDir, '.claude'))) return lokiJsonClaude;
1562
+ if (fs.existsSync(path.join(targetDir, '.cursor'))) return path.join(targetDir, '.cursor', 'loki-config.json');
1563
+ return null;
1564
+ }
1565
+
1566
+ /** 展开范围字符串为项目名列表,如 "dev1~15" → ["dev01","dev02",...,"dev15"] */
1567
+ function expandRange(rangeStr) {
1568
+ const match = rangeStr.match(/^([a-zA-Z-]*)(\d+)\s*[~~]\s*(\d+)$/);
1569
+ if (!match) return [];
1570
+ const prefix = match[1];
1571
+ const start = parseInt(match[2], 10);
1572
+ const end = parseInt(match[3], 10);
1573
+ const maxDigits = Math.max(String(start).length, String(end).length, 2);
1574
+ const result = [];
1575
+ for (let i = start; i <= end; i++) {
1576
+ result.push(prefix + String(i).padStart(maxDigits, '0'));
1577
+ }
1578
+ return result;
1579
+ }
1580
+
1581
+ function parseSelection(answer, names) {
1582
+ const selected = new Set();
1583
+ for (const part of answer.split(',')) {
1584
+ const trimmed = part.trim();
1585
+ const rangeMatch = trimmed.match(/^(\d)-(\d)$/);
1586
+ if (rangeMatch) {
1587
+ const start = parseInt(rangeMatch[1], 10);
1588
+ const end = parseInt(rangeMatch[2], 10);
1589
+ for (let n = start; n <= end; n++) {
1590
+ if (n >= 1 && n <= names.length) selected.add(names[n - 1]);
1591
+ }
1592
+ } else {
1593
+ const n = parseInt(trimmed, 10);
1594
+ if (n >= 1 && n <= names.length) selected.add(names[n - 1]);
1595
+ }
1596
+ }
1597
+ return [...selected];
1598
+ }
1599
+
1600
+ function ensureGitignore(patterns) {
1601
+ const gitignorePath = path.join(targetDir, '.gitignore');
1602
+ let content = '';
1603
+ if (fs.existsSync(gitignorePath)) {
1604
+ content = fs.readFileSync(gitignorePath, 'utf-8');
1605
+ }
1606
+ const lines = content.split('\n').map(l => l.trim());
1607
+ const toAdd = [];
1608
+ for (const pattern of patterns) {
1609
+ const alreadyIgnored = lines.some(line =>
1610
+ line.endsWith(pattern) || line.endsWith(`/${pattern}`) || line === `**/${pattern}`
1611
+ );
1612
+ if (!alreadyIgnored) {
1613
+ toAdd.push(`# 敏感配置 - ${pattern}`);
1614
+ toAdd.push(`**/${pattern}`);
1615
+ }
1616
+ }
1617
+ if (toAdd.length > 0) {
1618
+ const separator = content.endsWith('\n') || content === '' ? '' : '\n';
1619
+ fs.appendFileSync(gitignorePath, `${separator}${toAdd.join('\n')}\n`, 'utf-8');
1620
+ console.log(` ${fmt('green', '✔')} 已更新 .gitignore`);
1621
+ }
1622
+ }
1623
+
1278
1624
  // ── MCP 服务器管理 ──────────────────────────────────────────────────────────
1279
1625
 
1280
1626
  const MCP_REGISTRY = [
@@ -1765,7 +2111,7 @@ function showMainMenu() {
1765
2111
  console.log(` ${fmt('bold', '2')}) ${fmt('cyan', '更新')} — 更新已安装的框架文件`);
1766
2112
  console.log(` ${fmt('bold', '3')}) ${fmt('yellow', '全局安装')} — 安装到 ~/.claude 等,对所有项目生效`);
1767
2113
  console.log(` ${fmt('bold', '4')}) ${fmt('magenta', '技能同步反馈')} — 对比本地技能修改,生成 diff`);
1768
- console.log(` ${fmt('bold', '5')}) ${fmt('blue', '数据库配置')} 初始化 mysql-config.json(数据库连接信息)`);
2114
+ console.log(` ${fmt('bold', '5')}) ${fmt('blue', '环境配置')} 初始化数据库连接 / Loki 日志查询配置`);
1769
2115
  console.log(` ${fmt('bold', '6')}) ${fmt('green', 'MCP 管理')} — MCP 服务器安装/卸载/状态检查`);
1770
2116
  console.log('');
1771
2117
  rl.question(fmt('bold', '请输入选项 [1-6]: '), (answer) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-engineering-init",
3
- "version": "1.15.0",
3
+ "version": "1.16.1",
4
4
  "description": "AI 工程化配置初始化工具 — 一键为 Claude Code、OpenAI Codex 等 AI 工具初始化 Skills 和项目规范",
5
5
  "keywords": [
6
6
  "claude-code",