ai-engineering-init 1.16.4 → 1.17.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.
@@ -0,0 +1,154 @@
1
+ # /init-config - 从 Markdown 一键初始化环境配置
2
+
3
+ 从用户提供的 Markdown 文件中解析 MySQL 数据库连接和 Loki 日志查询配置,一键写入配置文件。
4
+
5
+ ## 使用方式
6
+
7
+ ```
8
+ /init-config <md文件路径> [--scope global|local]
9
+ ```
10
+
11
+ - `/init-config env-config.md` — 解析 md 文件,写入全局配置(默认)
12
+ - `/init-config env-config.md --scope local` — 写入当前项目本地配置
13
+ - `/init-config` — 无参数时,使用模板文件 `.claude/templates/env-config.md`
14
+
15
+ ## 参数
16
+
17
+ - `$ARGUMENTS` 的第一个词为文件路径,可以是相对路径或绝对路径
18
+ - `--scope global`(默认)写入 `~/.claude/` + `~/.cursor/`
19
+ - `--scope local` 写入当前项目 `.claude/`
20
+
21
+ ## Markdown 文件格式
22
+
23
+ 文件中必须包含以下两个段落(任一即可):
24
+
25
+ ### MySQL 数据库连接(标题含"MySQL"或"数据库")
26
+
27
+ ```markdown
28
+ ## MySQL 数据库连接
29
+
30
+ | 环境 | host | port | user | password | range | 描述 |
31
+ |------|------|------|------|----------|-------|------|
32
+ | local | 127.0.0.1 | 3306 | root | xxx | | 本地环境 |
33
+ | dev | dev-db.com | 3306 | dev_user | xxx | 1~15 | 开发环境 |
34
+
35
+ 默认环境: local
36
+ ```
37
+
38
+ ### Loki 日志查询(标题含"Loki"或"日志")
39
+
40
+ ```markdown
41
+ ## Loki 日志查询
42
+
43
+ | 环境 | 名称 | URL | Token | 别名 | range |
44
+ |------|------|-----|-------|------|-------|
45
+ | monitor-dev | Monitor开发 | https://grafana.example.com | glsa_xxx | mdev,dev | dev1~15 |
46
+
47
+ 默认环境: monitor-dev
48
+ ```
49
+
50
+ ### 注意事项
51
+
52
+ - host/URL 为 `YOUR_*` 占位符的行会被跳过
53
+ - Token 为 `YOUR_*` 的行会写入空 Token(稍后可用 `config --type loki` 补充)
54
+ - range 字段会自动展开为 projects 列表(如 `dev1~15` → `["dev01","dev02",...,"dev15"]`)
55
+
56
+ ## 执行流程
57
+
58
+ ### 第一步:确定文件路径和范围
59
+
60
+ ```bash
61
+ # 解析参数
62
+ FILE_PATH="${ARGUMENTS%% --*}" # 第一个参数为文件路径
63
+ SCOPE="global" # 默认全局
64
+ [[ "$ARGUMENTS" == *"--scope local"* ]] && SCOPE="local"
65
+
66
+ # 如果没指定文件,检查模板
67
+ if [ -z "$FILE_PATH" ]; then
68
+ FILE_PATH=".claude/templates/env-config.md"
69
+ fi
70
+ ```
71
+
72
+ 确认文件存在,不存在则提示用户创建。
73
+
74
+ ### 第二步:读取并解析 Markdown
75
+
76
+ 读取文件内容,按标题(`## MySQL`/`## Loki`)分割为两个段落。
77
+
78
+ 解析每个段落中的 Markdown 表格:
79
+ 1. 找到表头行(含 `|` 且下一行为 `|---|`)
80
+ 2. 提取表头列名
81
+ 3. 逐行解析数据行
82
+ 4. 跳过 `YOUR_*` 占位符
83
+ 5. 提取"默认环境: xxx"
84
+
85
+ ### 第三步:生成配置 JSON
86
+
87
+ **MySQL 配置**(mysql-config.json):
88
+ ```json
89
+ {
90
+ "environments": {
91
+ "local": { "host": "127.0.0.1", "port": 3306, "user": "root", "password": "xxx", "_desc": "本地环境" },
92
+ "dev": { "host": "dev-db.com", "port": 3306, "user": "dev_user", "password": "xxx", "range": "1~15", "_desc": "开发环境" }
93
+ },
94
+ "default": "local"
95
+ }
96
+ ```
97
+
98
+ **Loki 配置**(loki-config.json):
99
+ ```json
100
+ {
101
+ "active": "monitor-dev",
102
+ "environments": {
103
+ "monitor-dev": {
104
+ "name": "Monitor开发",
105
+ "url": "https://grafana.example.com",
106
+ "token": "glsa_xxx",
107
+ "aliases": ["mdev", "dev"],
108
+ "range": "dev1~15",
109
+ "projects": ["dev01", "dev02", "...", "dev15"]
110
+ }
111
+ }
112
+ }
113
+ ```
114
+
115
+ ### 第四步:写入配置文件
116
+
117
+ 根据 scope 决定写入位置:
118
+
119
+ | scope | MySQL 路径 | Loki 路径 |
120
+ |-------|-----------|----------|
121
+ | global | `~/.claude/mysql-config.json` + `~/.cursor/mysql-config.json` | `~/.claude/loki-config.json` + `~/.cursor/loki-config.json` |
122
+ | local | `.claude/mysql-config.json` | `.claude/loki-config.json` |
123
+
124
+ 写入后输出:
125
+ ```
126
+ ✔ MySQL 配置(N 个环境)→ 路径
127
+ ✔ Loki 配置(N 个环境)→ 路径
128
+ ```
129
+
130
+ ### 第五步:确保 .gitignore(仅 local scope)
131
+
132
+ 如果是本地配置,确保 `.gitignore` 包含:
133
+ ```
134
+ **/mysql-config.json
135
+ **/loki-config.json
136
+ ```
137
+
138
+ ## 模板文件
139
+
140
+ 如果用户没有配置文件,输出模板路径:
141
+
142
+ ```
143
+ 提示:请先创建配置文件,参考模板:
144
+ .claude/templates/env-config.md
145
+
146
+ 填写后运行:
147
+ /init-config .claude/templates/env-config.md
148
+ ```
149
+
150
+ ## 安全规则
151
+
152
+ - 不要在输出中显示完整的 password 和 Token(用 `***` 脱敏)
153
+ - local scope 时自动更新 .gitignore
154
+ - 覆盖已有配置前先展示将要写入的环境列表,让用户确认
@@ -0,0 +1,27 @@
1
+ # 环境配置模板
2
+
3
+ > 填写后运行 `npx ai-engineering-init config --from .claude/templates/env-config.md --scope global`
4
+ > 一键初始化 MySQL 数据库连接和 Loki 日志查询配置。
5
+
6
+ ## MySQL 数据库连接
7
+
8
+ | 环境 | host | port | user | password | range | 描述 |
9
+ |------|------|------|------|----------|-------|------|
10
+ | local | 127.0.0.1 | 3306 | root | do@u.can | | 本地环境 |
11
+ | dev | YOUR_DEV_HOST | 3306 | YOUR_USER | YOUR_PASSWORD | 1~15 | 开发环境(dev1~dev15) |
12
+ | test | YOUR_TEST_HOST | 3306 | YOUR_USER | YOUR_PASSWORD | 1~30 | 测试环境(test1~test30) |
13
+ | prod | YOUR_PROD_HOST | 3306 | readonly | YOUR_PASSWORD | | 生产环境(只读) |
14
+
15
+ 默认环境: local
16
+
17
+ ## Loki 日志查询
18
+
19
+ | 环境 | 名称 | URL | Token | 别名 | range |
20
+ |------|------|-----|-------|------|-------|
21
+ | test13 | 测试13 | https://test13.xnzn.net/grafana | YOUR_TOKEN | test13,13 | |
22
+ | monitor-test | Monitor测试 | https://monitor-test.xnzn.net/grafana | YOUR_TOKEN | mtest | test1~12 |
23
+ | monitor-dev | Monitor开发 | https://monitor-dev.xnzn.net/grafana | YOUR_TOKEN | mdev,dev | dev1~15 |
24
+ | monitor02-dev | Monitor02开发 | https://monitor02-dev.xnzn.net/grafana | YOUR_TOKEN | m02,monitor02 | dev16~42 |
25
+ | monitor-tyy-dev | 体验园开发 | https://monitor-tyy-dev.xnzn.net/grafana | YOUR_TOKEN | tyy,体验园 | dev44~58 |
26
+
27
+ 默认环境: test13
@@ -0,0 +1,27 @@
1
+ # 环境配置模板
2
+
3
+ > 填写后运行 `npx ai-engineering-init config --from .claude/templates/env-config.md --scope global`
4
+ > 一键初始化 MySQL 数据库连接和 Loki 日志查询配置。
5
+
6
+ ## MySQL 数据库连接
7
+
8
+ | 环境 | host | port | user | password | range | 描述 |
9
+ |------|------|------|------|----------|-------|------|
10
+ | local | 127.0.0.1 | 3306 | root | do@u.can | | 本地环境 |
11
+ | dev | YOUR_DEV_HOST | 3306 | YOUR_USER | YOUR_PASSWORD | 1~15 | 开发环境(dev1~dev15) |
12
+ | test | YOUR_TEST_HOST | 3306 | YOUR_USER | YOUR_PASSWORD | 1~30 | 测试环境(test1~test30) |
13
+ | prod | YOUR_PROD_HOST | 3306 | readonly | YOUR_PASSWORD | | 生产环境(只读) |
14
+
15
+ 默认环境: local
16
+
17
+ ## Loki 日志查询
18
+
19
+ | 环境 | 名称 | URL | Token | 别名 | range |
20
+ |------|------|-----|-------|------|-------|
21
+ | test13 | 测试13 | https://test13.xnzn.net/grafana | YOUR_TOKEN | test13,13 | |
22
+ | monitor-test | Monitor测试 | https://monitor-test.xnzn.net/grafana | YOUR_TOKEN | mtest | test1~12 |
23
+ | monitor-dev | Monitor开发 | https://monitor-dev.xnzn.net/grafana | YOUR_TOKEN | mdev,dev | dev1~15 |
24
+ | monitor02-dev | Monitor02开发 | https://monitor02-dev.xnzn.net/grafana | YOUR_TOKEN | m02,monitor02 | dev16~42 |
25
+ | monitor-tyy-dev | 体验园开发 | https://monitor-tyy-dev.xnzn.net/grafana | YOUR_TOKEN | tyy,体验园 | dev44~58 |
26
+
27
+ 默认环境: test13
package/bin/index.js CHANGED
@@ -55,6 +55,7 @@ let submitIssue = false; // sync-back --submit
55
55
  let configType = ''; // config --type <mysql|loki|all>
56
56
  let configScope = ''; // config --scope <local|global>
57
57
  let configAdd = false; // config --add
58
+ let configFrom = ''; // config --from <file.md>
58
59
 
59
60
  for (let i = 0; i < args.length; i++) {
60
61
  const arg = args[i];
@@ -121,6 +122,13 @@ for (let i = 0; i < args.length; i++) {
121
122
  case '--add':
122
123
  configAdd = true;
123
124
  break;
125
+ case '--from':
126
+ if (i + 1 >= args.length || args[i + 1].startsWith('-')) {
127
+ console.error(fmt('red', `错误:${arg} 需要一个文件路径`));
128
+ process.exit(1);
129
+ }
130
+ configFrom = path.resolve(args[++i]);
131
+ break;
124
132
  case '--help': case '-h':
125
133
  printHelp();
126
134
  process.exit(0);
@@ -154,6 +162,7 @@ function printHelp() {
154
162
  console.log(' --type <类型> config 时指定配置类型: mysql | loki | all');
155
163
  console.log(' --scope <范围> config 时指定范围: local(当前项目) | global(全局 ~/)');
156
164
  console.log(' --add config 时追加环境(不覆盖已有配置)');
165
+ console.log(' --from <文件> config 时从 Markdown 文件解析配置(跳过交互)');
157
166
  console.log(' --help, -h 显示此帮助\n');
158
167
  console.log('示例:');
159
168
  console.log(' npx ai-engineering-init --tool claude');
@@ -173,6 +182,7 @@ function printHelp() {
173
182
  console.log(' npx ai-engineering-init config --type all # 配置全部');
174
183
  console.log(' npx ai-engineering-init config --type mysql --scope global # 全局配置(所有项目共享)');
175
184
  console.log(' npx ai-engineering-init config --type mysql --add # 追加环境到已有配置');
185
+ console.log(' npx ai-engineering-init config --from env-config.md --scope global # 从 MD 文件一键初始化');
176
186
  }
177
187
 
178
188
  // ── 工具定义(init 用)────────────────────────────────────────────────────
@@ -1151,8 +1161,14 @@ function runSyncBack(selectedTool, selectedSkill, doSubmit) {
1151
1161
  // ── 环境配置初始化(MySQL / Loki)─────────────────────────────────────────
1152
1162
 
1153
1163
  function runConfig() {
1164
+ // --from 模式:从 MD 文件解析配置(非交互式)
1165
+ if (configFrom) {
1166
+ runConfigFromFile(configFrom, configScope || 'global');
1167
+ return;
1168
+ }
1169
+
1154
1170
  if (!process.stdin.isTTY) {
1155
- console.error(fmt('red', '错误:config 命令需要交互式终端'));
1171
+ console.error(fmt('red', '错误:config 命令需要交互式终端(或使用 --from <file> 跳过交互)'));
1156
1172
  process.exit(1);
1157
1173
  }
1158
1174
 
@@ -1571,6 +1587,224 @@ function writeLokiConfig(config, configPath, isGlobal) {
1571
1587
  console.log(fmt('green', 'Loki 日志查询配置完成!'));
1572
1588
  }
1573
1589
 
1590
+ // ── 从 Markdown 文件解析配置(非交互式)──────────────────────────────────────
1591
+
1592
+ function runConfigFromFile(filePath, scope) {
1593
+ if (!fs.existsSync(filePath)) {
1594
+ console.error(fmt('red', `错误:文件不存在 "${filePath}"`));
1595
+ process.exit(1);
1596
+ }
1597
+
1598
+ const content = fs.readFileSync(filePath, 'utf-8');
1599
+ const isGlobal = scope === 'global';
1600
+
1601
+ console.log(fmt('blue', fmt('bold', `从 Markdown 文件解析配置:${filePath}`)));
1602
+ console.log(fmt('magenta', `配置范围:${isGlobal ? '全局(~/.claude/)' : '本地(当前项目)'}`));
1603
+ console.log('');
1604
+
1605
+ // 解析 MySQL 表格
1606
+ const mysqlConfig = parseMysqlFromMd(content);
1607
+ if (mysqlConfig) {
1608
+ const mysqlPath = isGlobal
1609
+ ? path.join(HOME_DIR, '.claude', 'mysql-config.json')
1610
+ : path.join(targetDir, '.claude', 'mysql-config.json');
1611
+
1612
+ const configJson = JSON.stringify(mysqlConfig, null, 2) + '\n';
1613
+ const dir = path.dirname(mysqlPath);
1614
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
1615
+ fs.writeFileSync(mysqlPath, configJson, 'utf-8');
1616
+ console.log(` ${fmt('green', '✔')} MySQL 配置(${Object.keys(mysqlConfig.environments).length} 个环境)→ ${mysqlPath}`);
1617
+
1618
+ // 全局同步到 cursor
1619
+ if (isGlobal && fs.existsSync(path.join(HOME_DIR, '.cursor'))) {
1620
+ const cursorPath = path.join(HOME_DIR, '.cursor', 'mysql-config.json');
1621
+ fs.writeFileSync(cursorPath, configJson, 'utf-8');
1622
+ console.log(` ${fmt('green', '✔')} 已同步 → ${cursorPath}`);
1623
+ }
1624
+ }
1625
+
1626
+ // 解析 Loki 表格
1627
+ const lokiConfig = parseLokiFromMd(content);
1628
+ if (lokiConfig) {
1629
+ const lokiPath = isGlobal
1630
+ ? path.join(HOME_DIR, '.claude', 'loki-config.json')
1631
+ : path.join(targetDir, '.claude', 'loki-config.json');
1632
+
1633
+ const configJson = JSON.stringify(lokiConfig, null, 2) + '\n';
1634
+ const dir = path.dirname(lokiPath);
1635
+ if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true });
1636
+ fs.writeFileSync(lokiPath, configJson, 'utf-8');
1637
+ console.log(` ${fmt('green', '✔')} Loki 配置(${Object.keys(lokiConfig.environments).length} 个环境)→ ${lokiPath}`);
1638
+
1639
+ if (isGlobal && fs.existsSync(path.join(HOME_DIR, '.cursor'))) {
1640
+ const cursorPath = path.join(HOME_DIR, '.cursor', 'loki-config.json');
1641
+ fs.writeFileSync(cursorPath, configJson, 'utf-8');
1642
+ console.log(` ${fmt('green', '✔')} 已同步 → ${cursorPath}`);
1643
+ }
1644
+ }
1645
+
1646
+ if (!mysqlConfig && !lokiConfig) {
1647
+ console.error(fmt('red', '未在文件中找到 MySQL 或 Loki 配置表格。'));
1648
+ console.log('');
1649
+ console.log('期望格式(MySQL):');
1650
+ console.log(' | 环境 | host | port | user | password | range | 描述 |');
1651
+ console.log('');
1652
+ console.log('期望格式(Loki):');
1653
+ console.log(' | 环境 | 名称 | URL | Token | 别名 | range |');
1654
+ process.exit(1);
1655
+ }
1656
+
1657
+ if (!isGlobal) ensureGitignore(['mysql-config.json', 'loki-config.json']);
1658
+
1659
+ console.log('');
1660
+ console.log(fmt('green', fmt('bold', '配置初始化完成!')));
1661
+ if (isGlobal) {
1662
+ console.log(fmt('cyan', '技能按 本地(.claude/) → 全局(~/.claude/) 顺序查找,本地优先。'));
1663
+ }
1664
+ }
1665
+
1666
+ /** 从 Markdown 内容中解析 MySQL 配置表格 */
1667
+ function parseMysqlFromMd(content) {
1668
+ // 找到 "MySQL" 标题后的表格
1669
+ const mysqlSection = extractSection(content, /mysql|数据库/i);
1670
+ if (!mysqlSection) return null;
1671
+
1672
+ const rows = parseTable(mysqlSection);
1673
+ if (rows.length === 0) return null;
1674
+
1675
+ const environments = {};
1676
+ for (const row of rows) {
1677
+ const env = row['环境'] || row['env'] || '';
1678
+ if (!env) continue;
1679
+
1680
+ const host = row['host'] || '';
1681
+ const port = parseInt(row['port'] || '3306', 10);
1682
+ const user = row['user'] || '';
1683
+ const password = row['password'] || '';
1684
+ const range = row['range'] || '';
1685
+ const desc = row['描述'] || row['desc'] || `${env}环境`;
1686
+
1687
+ if (!host || host.startsWith('YOUR_')) continue; // 跳过未填写的占位符
1688
+
1689
+ environments[env] = { host, port, user, password, _desc: desc };
1690
+ if (range) environments[env].range = range;
1691
+ }
1692
+
1693
+ if (Object.keys(environments).length === 0) return null;
1694
+
1695
+ // 提取默认环境
1696
+ const defaultMatch = mysqlSection.match(/默认环境[::]\s*(\S+)/);
1697
+ const defaultEnv = defaultMatch ? defaultMatch[1] : Object.keys(environments)[0];
1698
+
1699
+ return {
1700
+ environments,
1701
+ default: defaultEnv,
1702
+ _comment: '从 Markdown 文件解析生成。支持 range 字段,查找顺序:本地 > 全局 ~/.claude/',
1703
+ };
1704
+ }
1705
+
1706
+ /** 从 Markdown 内容中解析 Loki 配置表格 */
1707
+ function parseLokiFromMd(content) {
1708
+ const lokiSection = extractSection(content, /loki|日志/i);
1709
+ if (!lokiSection) return null;
1710
+
1711
+ const rows = parseTable(lokiSection);
1712
+ if (rows.length === 0) return null;
1713
+
1714
+ const environments = {};
1715
+ for (const row of rows) {
1716
+ const env = row['环境'] || row['env'] || '';
1717
+ if (!env) continue;
1718
+
1719
+ const name = row['名称'] || row['name'] || env;
1720
+ const url = row['url'] || row['URL'] || '';
1721
+ const token = row['token'] || row['Token'] || '';
1722
+ const aliasStr = row['别名'] || row['aliases'] || env;
1723
+ const aliases = aliasStr.split(',').map(s => s.trim()).filter(Boolean);
1724
+ const rangeStr = row['range'] || '';
1725
+
1726
+ if (!url || url.startsWith('YOUR_')) continue;
1727
+
1728
+ const envData = { name, url, token: token.startsWith('YOUR_') ? '' : token, aliases };
1729
+ if (rangeStr) {
1730
+ envData.range = rangeStr;
1731
+ envData.projects = expandRange(rangeStr);
1732
+ } else {
1733
+ envData.projects = [];
1734
+ }
1735
+
1736
+ environments[env] = envData;
1737
+ }
1738
+
1739
+ if (Object.keys(environments).length === 0) return null;
1740
+
1741
+ const defaultMatch = lokiSection.match(/默认环境[::]\s*(\S+)/);
1742
+ const activeEnv = defaultMatch ? defaultMatch[1] : Object.keys(environments)[0];
1743
+
1744
+ return {
1745
+ _comment: '从 Markdown 文件解析生成。支持 range 字段,查找顺序:本地 > 全局 ~/.claude/',
1746
+ _setup: 'Token:Grafana → Administration → Service accounts → Add(Viewer)→ Add token',
1747
+ active: activeEnv,
1748
+ environments,
1749
+ };
1750
+ }
1751
+
1752
+ /** 从 Markdown 中按标题提取段落 */
1753
+ function extractSection(content, titlePattern) {
1754
+ const lines = content.split('\n');
1755
+ let start = -1;
1756
+ let end = lines.length;
1757
+ for (let i = 0; i < lines.length; i++) {
1758
+ const line = lines[i];
1759
+ if (line.match(/^#+\s/) && titlePattern.test(line)) {
1760
+ start = i;
1761
+ const level = line.match(/^(#+)/)[1].length;
1762
+ // 找到同级或更高级标题作为结束
1763
+ for (let j = i + 1; j < lines.length; j++) {
1764
+ const nextMatch = lines[j].match(/^(#+)\s/);
1765
+ if (nextMatch && nextMatch[1].length <= level) {
1766
+ end = j;
1767
+ break;
1768
+ }
1769
+ }
1770
+ break;
1771
+ }
1772
+ }
1773
+ if (start === -1) return null;
1774
+ return lines.slice(start, end).join('\n');
1775
+ }
1776
+
1777
+ /** 解析 Markdown 表格为对象数组 */
1778
+ function parseTable(text) {
1779
+ const lines = text.split('\n');
1780
+ let headerLine = -1;
1781
+
1782
+ // 找到表头行(含 | 的行,下一行是分隔线 |---|)
1783
+ for (let i = 0; i < lines.length - 1; i++) {
1784
+ if (lines[i].includes('|') && lines[i + 1] && lines[i + 1].match(/^\|[\s-:|]+\|$/)) {
1785
+ headerLine = i;
1786
+ break;
1787
+ }
1788
+ }
1789
+ if (headerLine === -1) return [];
1790
+
1791
+ const parseRow = (line) => line.split('|').map(s => s.trim()).filter(Boolean);
1792
+ const headers = parseRow(lines[headerLine]);
1793
+ const rows = [];
1794
+
1795
+ for (let i = headerLine + 2; i < lines.length; i++) {
1796
+ const line = lines[i];
1797
+ if (!line.includes('|')) break;
1798
+ const cells = parseRow(line);
1799
+ if (cells.length === 0) break;
1800
+ const row = {};
1801
+ headers.forEach((h, idx) => { row[h] = cells[idx] || ''; });
1802
+ rows.push(row);
1803
+ }
1804
+
1805
+ return rows;
1806
+ }
1807
+
1574
1808
  // ── Config 工具函数 ─────────────────────────────────────────────────────────
1575
1809
 
1576
1810
  function getLokiConfigPath() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ai-engineering-init",
3
- "version": "1.16.4",
3
+ "version": "1.17.0",
4
4
  "description": "AI 工程化配置初始化工具 — 一键为 Claude Code、OpenAI Codex 等 AI 工具初始化 Skills 和项目规范",
5
5
  "keywords": [
6
6
  "claude-code",