rushangle-cli 0.2.4 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rushangle-cli",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "SkillHub CLI - 数智凯航技能市场命令行工具",
5
5
  "bin": {
6
6
  "rushangle": "./bin/rushangle.js"
@@ -8,6 +8,46 @@ const api = require('../api');
8
8
 
9
9
  const VALID_TYPES = ['skill', 'mcp', 'code', 'doc'];
10
10
 
11
+ /**
12
+ * Simple YAML frontmatter parser for SKILL.md.
13
+ * Extracts key-value pairs from --- delimited frontmatter block.
14
+ */
15
+ function parseFrontmatter(content) {
16
+ const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
17
+ if (!match) return {};
18
+ const fm = {};
19
+ const lines = match[1].split(/\r?\n/);
20
+ for (const line of lines) {
21
+ const kv = line.match(/^(\w[\w-]*)\s*:\s*(.+)$/);
22
+ if (kv) {
23
+ const key = kv[1].trim();
24
+ let value = kv[2].trim();
25
+ // Strip quotes
26
+ if ((value.startsWith('"') && value.endsWith('"')) ||
27
+ (value.startsWith("'") && value.endsWith("'"))) {
28
+ value = value.slice(1, -1);
29
+ }
30
+ fm[key] = value;
31
+ }
32
+ }
33
+ return fm;
34
+ }
35
+
36
+ /**
37
+ * Read SKILL.md and extract frontmatter + body content.
38
+ */
39
+ function readSkillMd(absDir) {
40
+ for (const f of ['SKILL.md', 'skill.md', 'Skill.md']) {
41
+ const fp = path.join(absDir, f);
42
+ if (fs.existsSync(fp)) {
43
+ const content = fs.readFileSync(fp, 'utf-8');
44
+ const frontmatter = parseFrontmatter(content);
45
+ return { content, frontmatter, path: fp };
46
+ }
47
+ }
48
+ return { content: '', frontmatter: {}, path: null };
49
+ }
50
+
11
51
  async function askQuestion(query) {
12
52
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
13
53
  return new Promise(resolve => rl.question(query, ans => { rl.close(); resolve(ans.trim()); }));
@@ -108,21 +148,28 @@ module.exports = new Command('publish')
108
148
  }
109
149
  }
110
150
 
111
- const name = opts.name || meta.name || path.basename(absDir);
112
- const description = opts.description || meta.description || '';
113
- const version = opts.version || meta.version || '0.1.0';
151
+ // Read SKILL.md frontmatter for skill metadata
152
+ const skillMd = type === 'skill' ? readSkillMd(absDir) : null;
153
+ const fm = (skillMd && skillMd.frontmatter) || {};
154
+
155
+ const name = opts.name || meta.name || fm.name || path.basename(absDir);
156
+ const description = opts.description || meta.description || fm.description || '';
157
+ const version = opts.version || meta.version || fm.version || '0.1.0';
114
158
  const tags = opts.tags ? opts.tags.split(',').map(t => t.trim()).filter(Boolean)
115
159
  : (meta.tags || []);
116
160
 
117
161
  // Resolve category via server presets
118
162
  const { resolved: category } = await resolveCategory(type, opts.category || meta.category || '', api);
119
163
 
120
- // Read README if exists
164
+ // Read README if exists, otherwise fall back to SKILL.md body
121
165
  let readme = '';
122
166
  for (const f of ['README.md', 'readme.md', 'README', 'readme']) {
123
167
  const fp = path.join(absDir, f);
124
168
  if (fs.existsSync(fp)) { readme = fs.readFileSync(fp, 'utf-8'); break; }
125
169
  }
170
+ if (!readme && skillMd && skillMd.content) {
171
+ readme = skillMd.content.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n?/, '').trim();
172
+ }
126
173
 
127
174
  console.log(chalk.cyan(`正在发布 ${type}: ${name}@${version} ...`));
128
175