fedincms-cli 0.0.4 → 0.0.5

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/README.md CHANGED
@@ -1,6 +1,11 @@
1
1
  # fedincms-cli
2
2
 
3
- Fedin CMS Skill 的全局 CLI:通过 `fedincms init` 将 Skill 安装到当前项目,在 Cursor 中通过项目内脚本调用 Fedin CMS API,无需运行 MCP Server。
3
+ Fedin CMS Skill 的全局 CLI:通过 `fedincms init` 将 Skill 安装到当前项目,在 **Cursor、CodeBuddy、Claude Code** 等 AI 编程助手中通过项目内脚本调用 Fedin CMS API,无需运行 MCP Server。
4
+
5
+ ## 支持的环境
6
+
7
+ - **Cursor**:Skill 安装到 `.cursor/skills/fedin-cms/`,开箱即用。
8
+ - **CodeBuddy / Claude Code 等**:执行 `fedincms init` 后,将 `.cursor/skills/fedin-cms/` 整个目录复制到该编辑器所要求的 Skill 目录(请查阅对应文档),或在该编辑器中配置使用项目内脚本路径 `.cursor/skills/fedin-cms/scripts/fedin-cms-cli.js`(需在项目根执行)。
4
9
 
5
10
  ## 安装
6
11
 
@@ -26,17 +31,14 @@ npm install -g @your-scope/fedin-cms-cli
26
31
  2. 初始化:只需传入**站点密钥**(格式 `cms_<base64>`),会解密得到 websiteId 与 userId 并写入脚本:
27
32
  ```bash
28
33
  fedincms init cms_website_key=你的站点密钥
29
- # 或带可选 API 地址、密钥
30
- fedincms init cms_website_key=你的站点密钥 apiUrl=https://... anonKey=你的密钥
31
- # 长选项
32
- fedincms init --cms-website-key=你的站点密钥 --api-url=... --anon-key=...
34
+ fedincms init --cms-website-key=你的站点密钥
33
35
  ```
34
36
 
35
- 3. 在 Cursor 中打开该项目,AI 将根据 `.cursor/skills/fedin-cms/SKILL.md` 在需要时执行:
37
+ 3. 在 Cursor / CodeBuddy / Claude Code 等中打开该项目,AI 助手会根据 SKILL 说明在**项目根**执行:
36
38
  ```bash
37
39
  node .cursor/skills/fedin-cms/scripts/fedin-cms-cli.js <command> [args]
38
40
  ```
39
- 脚本为单文件(依赖已打包),无需在 `scripts/` 下执行 `npm install`。
41
+ 脚本为单文件(依赖已打包),无需在 `scripts/` 下执行 `npm install`。若在其它编辑器中已将 Skill 复制到别处,请使用该目录下的 `scripts/fedin-cms-cli.js` 路径。
40
42
 
41
43
  ## 发布到 npm
42
44
 
package/dist/cli.js CHANGED
@@ -3,10 +3,28 @@
3
3
 
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
+ const readline = require('readline');
7
+ const inquirer = require('inquirer');
6
8
 
7
9
  const TEMPLATE_DIR = path.join(__dirname, '..', 'templates', 'skill');
8
- const SKILL_DIR = '.cursor/skills/fedin-cms';
9
- const SKILL_SCRIPTS_DIR = '.cursor/skills/fedin-cms/scripts';
10
+
11
+ // --ai 预设:决定 Skill 安装到哪个目录;交互式时只展示 AI_CHOICES 列表
12
+ const AI_SKILL_BASE = {
13
+ cursor: '.cursor/skills',
14
+ codebuddy: '.codebuddy/skills',
15
+ claude: '.claude/skills',
16
+ };
17
+ const AI_CHOICES = ['cursor', 'codebuddy', 'claude'];
18
+
19
+ function getSkillDir(ai) {
20
+ const key = (ai || 'cursor').toLowerCase().trim();
21
+ const base = AI_SKILL_BASE[key];
22
+ if (!base) {
23
+ console.error('fedincms init: 不支持的 AI 助手: ' + ai + '。支持: cursor, codebuddy, claude');
24
+ process.exit(1);
25
+ }
26
+ return base + '/fedin-cms';
27
+ }
10
28
 
11
29
  /**
12
30
  * 解密站点密钥,得到 websiteId 和 userId
@@ -47,12 +65,16 @@ function parseArgs(argv) {
47
65
  const k = arg.slice(2, eq).replace(/-/g, '');
48
66
  const v = arg.slice(eq + 1);
49
67
  if (k === 'cmswebsitekey' || k === 'cms_website_key') args.cmsWebsiteKey = v;
68
+ else if (k === 'ai') args.ai = v;
69
+ } else if (arg === '--ai' && argv[i + 1]) {
70
+ args.ai = argv[++i];
50
71
  }
51
72
  continue;
52
73
  }
53
74
  if (arg.includes('=')) {
54
75
  const [k, v] = arg.split('=').map(s => s.trim());
55
76
  if (k === 'cms_website_key' || k === 'cmsWebsiteKey') args.cmsWebsiteKey = v;
77
+ else if (k === 'ai') args.ai = v;
56
78
  continue;
57
79
  }
58
80
  args._.push(arg);
@@ -74,6 +96,17 @@ function copyFile(src, dest, force) {
74
96
  return true;
75
97
  }
76
98
 
99
+ // 复制文件并替换内容中的 __FEDINCMS_SCRIPT_PATH__
100
+ function copyFileWithScriptPath(srcPath, destRel, scriptPath, force) {
101
+ const destFull = path.join(process.cwd(), destRel);
102
+ if (!force && fs.existsSync(destFull)) return false;
103
+ ensureDir(path.dirname(destFull));
104
+ let content = fs.readFileSync(srcPath, 'utf8');
105
+ content = content.replace(/__FEDINCMS_SCRIPT_PATH__/g, scriptPath);
106
+ fs.writeFileSync(destFull, content, 'utf8');
107
+ return true;
108
+ }
109
+
77
110
  // 复制脚本并替换占位符 __FEDINCMS_WEBSITE_ID__、__FEDINCMS_USER_ID__
78
111
  function copyScriptWithPlaceholders(srcPath, destRel, placeholders, force) {
79
112
  const destFull = path.join(process.cwd(), destRel);
@@ -94,9 +127,9 @@ function runInit(options) {
94
127
  process.exit(1);
95
128
  }
96
129
 
97
- const key = options.cmsWebsiteKey;
130
+ const key = (options.cmsWebsiteKey || '').trim();
98
131
  if (!key) {
99
- console.error('fedincms init: 缺少必填参数 cms_website_key。用法: fedincms init cms_website_key=xxxxx [apiUrl=...] [anonKey=...]');
132
+ console.error('fedincms init: 缺少站点密钥。用法: fedincms init cms_website_key=xxxxx [--ai cursor|codebuddy|claude],或直接运行 fedincms init 按提示输入。');
100
133
  process.exit(1);
101
134
  }
102
135
 
@@ -111,21 +144,25 @@ function runInit(options) {
111
144
  process.exit(1);
112
145
  }
113
146
 
114
- const skillDirFull = path.join(cwd, SKILL_DIR);
147
+ const skillDir = getSkillDir(options.ai);
148
+ const scriptsDir = skillDir + '/scripts';
149
+ const scriptPath = skillDir + '/scripts/fedin-cms-cli.js';
150
+
151
+ const skillDirFull = path.join(cwd, skillDir);
115
152
  const exists = fs.existsSync(skillDirFull);
116
153
  if (exists && !options.force) {
117
- console.log('fedincms: .cursor/skills/fedin-cms/ 已存在,跳过复制。使用 --force 覆盖。');
154
+ console.log('fedincms: ' + skillDir + '/ 已存在,跳过复制。使用 --force 覆盖。');
118
155
  } else {
119
- ensureDir(SKILL_SCRIPTS_DIR);
156
+ ensureDir(scriptsDir);
120
157
  const skillMd = path.join(templateBase, 'SKILL.md');
121
158
  const refMd = path.join(templateBase, 'reference.md');
122
159
  const scriptJs = path.join(templateBase, 'scripts', 'fedin-cms-cli.js');
123
- if (fs.existsSync(skillMd)) copyFile(skillMd, path.join(SKILL_DIR, 'SKILL.md'), options.force);
124
- if (fs.existsSync(refMd)) copyFile(refMd, path.join(SKILL_DIR, 'reference.md'), options.force);
125
- if (fs.existsSync(scriptJs)) copyScriptWithPlaceholders(scriptJs, path.join(SKILL_DIR, 'scripts', 'fedin-cms-cli.js'), { websiteId, userId }, options.force);
160
+ if (fs.existsSync(skillMd)) copyFileWithScriptPath(skillMd, skillDir + '/SKILL.md', scriptPath, options.force);
161
+ if (fs.existsSync(refMd)) copyFileWithScriptPath(refMd, skillDir + '/reference.md', scriptPath, options.force);
162
+ if (fs.existsSync(scriptJs)) copyScriptWithPlaceholders(scriptJs, skillDir + '/scripts/fedin-cms-cli.js', { websiteId, userId }, options.force);
126
163
  }
127
164
 
128
- console.log('Skill 已安装到 .cursor/skills/fedin-cms/');
165
+ console.log('Skill 已安装到 ' + skillDir + '/');
129
166
  console.log('站点 ID 与用户 ID 已从密钥解密并写入脚本。');
130
167
  console.log('脚本为单文件打包,无需在 scripts/ 下安装依赖。');
131
168
  }
@@ -135,27 +172,87 @@ function printHelp() {
135
172
  fedincms init - 将 Fedin CMS Skill 安装到当前项目
136
173
 
137
174
  用法:
138
- fedincms init cms_website_key=<站点密钥>
139
- fedincms init --cms-website-key=<站点密钥>
175
+ 交互式(推荐):
176
+ fedincms init
177
+ 按提示输入站点密钥,然后从列表选择 AI 助手(cursor / codebuddy / claude)
178
+
179
+ 命令行参数:
180
+ fedincms init cms_website_key=<站点密钥> [--ai cursor|codebuddy|claude]
181
+ fedincms init --cms-website-key=<站点密钥> --ai cursor
140
182
 
141
183
  参数:
142
- cms_website_key 必填。站点密钥(格式 cms_<base64>),解密后得到 websiteId 与 userId,并写入脚本占位符。
184
+ cms_website_key 站点密钥(格式 cms_<base64>),解密后得到 websiteId 与 userId
185
+ --ai 目标 AI 环境。cursor → .cursor/skills/fedin-cms/
186
+ codebuddy → .codebuddy/skills/fedin-cms/
187
+ claude → .claude/skills/fedin-cms/
143
188
 
144
189
  选项:
145
- --force, -f 已存在 .cursor/skills/fedin-cms/ 时覆盖复制并重新替换占位符。
190
+ --force, -f 已存在目标目录时覆盖复制。
146
191
  --help, -h 显示此帮助。
147
-
148
- 行为:
149
- - 解密 cms_website_key 得到 websiteId、userId
150
- - 创建 .cursor/skills/fedin-cms/ 并复制 SKILL.md、reference.md
151
- - 复制 scripts/fedin-cms-cli.js 并将占位符 __FEDINCMS_WEBSITE_ID__、__FEDINCMS_USER_ID__ 替换为解密后的值
152
192
  `);
153
193
  }
154
194
 
195
+ function ask(rl, prompt) {
196
+ return new Promise(function (resolve) {
197
+ rl.question(prompt, function (answer) {
198
+ resolve((answer || '').trim());
199
+ });
200
+ });
201
+ }
202
+
203
+ /**
204
+ * 列表选择:使用 inquirer list,上下键选择、回车确认。
205
+ * @param {string[]} items - 选项列表,如 ['cursor', 'codebuddy']
206
+ * @returns {Promise<string>} 选中的项
207
+ */
208
+ function askSelectList(items) {
209
+ return inquirer
210
+ .prompt([
211
+ {
212
+ type: 'list',
213
+ name: 'ai',
214
+ message: '请选择当前使用的 AI 助手或编辑器:',
215
+ choices: items,
216
+ default: items[0],
217
+ },
218
+ ])
219
+ .then(function (answers) {
220
+ return answers.ai;
221
+ });
222
+ }
223
+
224
+ function runInteractiveInit(options, done) {
225
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
226
+ ask(rl, '请输入站点密钥: ')
227
+ .then(function (key) {
228
+ if (!key) {
229
+ console.error('未输入站点密钥,已取消。');
230
+ rl.close();
231
+ process.exit(1);
232
+ }
233
+ rl.close();
234
+ return askSelectList(AI_CHOICES).then(function (ai) {
235
+ return { key: key, ai: ai };
236
+ });
237
+ })
238
+ .then(function (result) {
239
+ runInit({
240
+ cmsWebsiteKey: result.key,
241
+ ai: result.ai,
242
+ force: options.force,
243
+ });
244
+ if (typeof done === 'function') done();
245
+ })
246
+ .catch(function (err) {
247
+ console.error(err);
248
+ process.exit(1);
249
+ });
250
+ }
251
+
155
252
  function main() {
156
253
  const argv = process.argv.slice(2);
157
254
  if (argv.length === 0) {
158
- console.log('用法: fedincms init cms_website_key=xxxxx');
255
+ console.log('用法: fedincms init 或 fedincms init cms_website_key=xxxxx [--ai cursor|codebuddy|claude]');
159
256
  console.log('查看帮助: fedincms init --help');
160
257
  process.exit(0);
161
258
  }
@@ -171,7 +268,12 @@ function main() {
171
268
  printHelp();
172
269
  process.exit(0);
173
270
  }
174
- runInit(options);
271
+
272
+ if (options.cmsWebsiteKey) {
273
+ runInit(options);
274
+ } else {
275
+ runInteractiveInit(options);
276
+ }
175
277
  }
176
278
 
177
279
  main();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fedincms-cli",
3
- "version": "0.0.4",
3
+ "version": "0.0.5",
4
4
  "description": "Fedin CMS Skill CLI",
5
5
  "main": "dist/cli.js",
6
6
  "bin": {
@@ -14,6 +14,9 @@
14
14
  "build:skill": "node build-skill.js",
15
15
  "prepublishOnly": "npm run build:skill && node -e \"const fs=require('fs');const p='src/cli.js';if(fs.existsSync(p)){fs.mkdirSync('dist',{recursive:true});fs.copyFileSync(p,'dist/cli.js');}\""
16
16
  },
17
+ "dependencies": {
18
+ "inquirer": "^8.2.6"
19
+ },
17
20
  "devDependencies": {
18
21
  "axios": "^1.6.0",
19
22
  "dotenv": "^16.3.1",
@@ -14,7 +14,7 @@ description: 通过 Fedin CMS 脚本管理内容类型与条目:列出/获取/
14
14
  - **必须在项目根目录**执行脚本,以便从项目根加载 `.env`。
15
15
  - 唯一入口命令:
16
16
  ```bash
17
- node .cursor/skills/fedin-cms/scripts/fedin-cms-cli.js <command> [args]
17
+ node __FEDINCMS_SCRIPT_PATH__ <command> [args]
18
18
  ```
19
19
  - 所有 Fedin CMS 能力(列内容类型、查/增/删/改条目与 content type)**必须**通过上述脚本完成,不得手写请求 CMS API 的代码。
20
20
 
@@ -46,14 +46,14 @@ description: 通过 Fedin CMS 脚本管理内容类型与条目:列出/获取/
46
46
 
47
47
  ```bash
48
48
  # 列出所有内容类型
49
- node .cursor/skills/fedin-cms/scripts/fedin-cms-cli.js list_content_types
49
+ node __FEDINCMS_SCRIPT_PATH__ list_content_types
50
50
 
51
51
  # 获取 blog 类型单条
52
- node .cursor/skills/fedin-cms/scripts/fedin-cms-cli.js get_entry --uid=blog --id=123
52
+ node __FEDINCMS_SCRIPT_PATH__ get_entry --uid=blog --id=123
53
53
 
54
54
  # 获取 blog 列表(分页)
55
- node .cursor/skills/fedin-cms/scripts/fedin-cms-cli.js get_entries --uid=blog --options='{"pagination":{"page":1,"pageSize":10},"sort":["updated_at:desc"]}'
55
+ node __FEDINCMS_SCRIPT_PATH__ get_entries --uid=blog --options='{"pagination":{"page":1,"pageSize":10},"sort":["updated_at:desc"]}'
56
56
 
57
57
  # 创建条目
58
- node .cursor/skills/fedin-cms/scripts/fedin-cms-cli.js create_entry --uid=blog --data='{"title":"Hello","body":"World"}'
58
+ node __FEDINCMS_SCRIPT_PATH__ create_entry --uid=blog --data='{"title":"Hello","body":"World"}'
59
59
  ```
@@ -1,15 +1,11 @@
1
1
  # Fedin CMS 脚本命令参考
2
2
 
3
3
  所有命令均在**项目根目录**执行:
4
- `node .cursor/skills/fedin-cms/scripts/fedin-cms-cli.js <command> [args]`
4
+ `node __FEDINCMS_SCRIPT_PATH__ <command> [args]`
5
5
 
6
6
  ## 环境
7
7
 
8
- - **站点 ID 与用户 ID**:由 `fedincms init cms_website_key=xxx` 解密密钥后写入脚本,**不从环境变量读取**。
9
- - 脚本从项目根加载 `.env`,仅用于:
10
- - `SUPABASE_CMS_API_URL`(内容类型 API)
11
- - `SUPABASE_CONTENT_API_URL`(条目 API)
12
- - `SUPABASE_ANON_KEY`(API 密钥)
8
+ - **站点 ID 与用户 ID**:由 `fedincms init cms_website_key=xxx` 解密密钥后写入脚本。
13
9
  - 成功时结果 JSON 输出到 stdout;失败时错误信息输出到 stderr 并以非 0 退出码退出。
14
10
 
15
11
  ---