flowmind 1.0.0 → 1.0.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.
package/README.md CHANGED
@@ -317,6 +317,85 @@ flowmind "这个功能应该用 Redis 还是 MongoDB?"
317
317
 
318
318
  ---
319
319
 
320
+ ## 📖 CLI Reference
321
+
322
+ ### Skill Management
323
+
324
+ ```bash
325
+ # List all available skills
326
+ flowmind skills
327
+ flowmind skills --verbose # Show detailed info
328
+ flowmind skills --json # JSON output (for tool integration)
329
+ flowmind skills --category quality # Filter by category
330
+
331
+ # View single skill info
332
+ flowmind skill log-audit
333
+ flowmind skill log-audit --json # JSON output
334
+ flowmind skill log-audit --read # Read SKILL.md content
335
+ flowmind skill log-audit --config # Show configuration
336
+
337
+ # Modify skill configuration
338
+ flowmind skill log-audit --set defaultFormat sequential-list
339
+ flowmind skill code-review --set security.enabled true
340
+ ```
341
+
342
+ ### Resource Management
343
+
344
+ ```bash
345
+ # List resource files
346
+ flowmind resource --list
347
+ flowmind resource --list learning
348
+ flowmind resource --list --json # JSON output
349
+
350
+ # View/edit files
351
+ flowmind resource --show config.json
352
+ flowmind resource --edit config.json
353
+
354
+ # Show resource configuration
355
+ flowmind resource --config
356
+ flowmind resource --config --json
357
+ ```
358
+
359
+ ### Other Commands
360
+
361
+ ```bash
362
+ # Process request
363
+ flowmind process "your request"
364
+ flowmind process --skill log-audit "query logs"
365
+
366
+ # Manage learning
367
+ flowmind learn --list
368
+ flowmind learn --export learnings.json
369
+
370
+ # Scene management
371
+ flowmind scenes --list
372
+ flowmind scenes --add
373
+
374
+ # Show statistics
375
+ flowmind stats
376
+
377
+ # Configuration
378
+ flowmind config --list
379
+ flowmind config --set learning.enabled true
380
+ ```
381
+
382
+ ### Tool Integration (Codex/Claude)
383
+
384
+ All commands support `--json` flag for programmatic access:
385
+
386
+ ```bash
387
+ # Get skills list as JSON
388
+ flowmind skills --json
389
+
390
+ # Get skill info as JSON
391
+ flowmind skill log-audit --json
392
+
393
+ # Get resource list as JSON
394
+ flowmind resource --list --json
395
+ ```
396
+
397
+ ---
398
+
320
399
  ## 🏗️ Architecture
321
400
 
322
401
  ### System Architecture
@@ -614,8 +693,8 @@ flowmind init → Load component-config.json → Register adapters → Activate
614
693
  | `aliyun-dms-mcp-server` | `databaseManager` | `aliyun-dms` |
615
694
  | `friday-rds-redis-query` | `databaseQuery` | `aliyun-rds-query` |
616
695
  | `friday-aliyun-sz-rds-redis` | `redisMonitor` | `aliyun-redis` |
617
- | `aomi-yapi-mcp` | `apiDoc` | `yapi` |
618
- | `aomi-yuque-mcp` | `knowledgeBase` | `yuque` |
696
+ | `yapi-mcp` | `apiDoc` | `yapi` |
697
+ | `yuque-mcp` | `knowledgeBase` | `yuque` |
619
698
  | `friday-auto-flow` | `workflow` | `friday-flow` |
620
699
  | `friday-auto-report` | `report` | `friday-report` |
621
700
 
package/README_CN.md CHANGED
@@ -317,6 +317,85 @@ flowmind "这个功能应该用 Redis 还是 MongoDB?"
317
317
 
318
318
  ---
319
319
 
320
+ ## 📖 CLI 命令参考
321
+
322
+ ### 技能管理
323
+
324
+ ```bash
325
+ # 列出所有可用技能
326
+ flowmind skills
327
+ flowmind skills --verbose # 显示详细信息
328
+ flowmind skills --json # JSON 输出(用于工具集成)
329
+ flowmind skills --category quality # 按分类过滤
330
+
331
+ # 查看单个技能信息
332
+ flowmind skill log-audit
333
+ flowmind skill log-audit --json # JSON 输出
334
+ flowmind skill log-audit --read # 读取 SKILL.md 内容
335
+ flowmind skill log-audit --config # 显示配置
336
+
337
+ # 修改技能配置
338
+ flowmind skill log-audit --set defaultFormat sequential-list
339
+ flowmind skill code-review --set security.enabled true
340
+ ```
341
+
342
+ ### 资源管理
343
+
344
+ ```bash
345
+ # 列出资源文件
346
+ flowmind resource --list
347
+ flowmind resource --list learning
348
+ flowmind resource --list --json # JSON 输出
349
+
350
+ # 查看/编辑文件
351
+ flowmind resource --show config.json
352
+ flowmind resource --edit config.json
353
+
354
+ # 显示资源配置
355
+ flowmind resource --config
356
+ flowmind resource --config --json
357
+ ```
358
+
359
+ ### 其他命令
360
+
361
+ ```bash
362
+ # 处理请求
363
+ flowmind process "your request"
364
+ flowmind process --skill log-audit "query logs"
365
+
366
+ # 管理学习记录
367
+ flowmind learn --list
368
+ flowmind learn --export learnings.json
369
+
370
+ # 场景管理
371
+ flowmind scenes --list
372
+ flowmind scenes --add
373
+
374
+ # 显示统计信息
375
+ flowmind stats
376
+
377
+ # 配置管理
378
+ flowmind config --list
379
+ flowmind config --set learning.enabled true
380
+ ```
381
+
382
+ ### 工具集成(Codex/Claude)
383
+
384
+ 所有命令支持 `--json` 参数用于程序化访问:
385
+
386
+ ```bash
387
+ # 获取技能列表 JSON
388
+ flowmind skills --json
389
+
390
+ # 获取技能信息 JSON
391
+ flowmind skill log-audit --json
392
+
393
+ # 获取资源列表 JSON
394
+ flowmind resource --list --json
395
+ ```
396
+
397
+ ---
398
+
320
399
  ## 🏗️ 架构
321
400
 
322
401
  ### 系统架构
@@ -613,8 +692,8 @@ flowmind init → 加载 component-config.json → 注册适配器 → 激活默
613
692
  | `aliyun-dms-mcp-server` | `databaseManager` | `aliyun-dms` |
614
693
  | `friday-rds-redis-query` | `databaseQuery` | `aliyun-rds-query` |
615
694
  | `friday-aliyun-sz-rds-redis` | `redisMonitor` | `aliyun-redis` |
616
- | `aomi-yapi-mcp` | `apiDoc` | `yapi` |
617
- | `aomi-yuque-mcp` | `knowledgeBase` | `yuque` |
695
+ | `yapi-mcp` | `apiDoc` | `yapi` |
696
+ | `yuque-mcp` | `knowledgeBase` | `yuque` |
618
697
  | `friday-auto-flow` | `workflow` | `friday-flow` |
619
698
  | `friday-auto-report` | `report` | `friday-report` |
620
699
 
package/bin/flowmind.js CHANGED
@@ -215,15 +215,145 @@ program
215
215
  }
216
216
  });
217
217
 
218
- // Skills command
218
+ // Skills command (enhanced)
219
219
  program
220
220
  .command('skills')
221
221
  .description('List available skills')
222
- .action(async () => {
222
+ .option('-j, --json', 'Output as JSON (for tool integration)')
223
+ .option('-v, --verbose', 'Show detailed information')
224
+ .option('-c, --category <category>', 'Filter by category')
225
+ .action(async (options) => {
223
226
  try {
224
227
  const fm = await initFlowMind();
225
228
  const skills = fm.skills.list();
226
- displaySkills(skills);
229
+
230
+ // Filter by category if specified
231
+ const filtered = options.category
232
+ ? skills.filter(s => s.category === options.category)
233
+ : skills;
234
+
235
+ if (options.json) {
236
+ // JSON output for codex/claude integration
237
+ console.log(JSON.stringify({ skills: filtered }, null, 2));
238
+ } else {
239
+ displaySkills(filtered, options.verbose);
240
+ }
241
+ } catch (error) {
242
+ console.error(chalk.red('Error:'), error.message);
243
+ }
244
+ });
245
+
246
+ // Skill command (view/modify single skill)
247
+ program
248
+ .command('skill <name>')
249
+ .description('View or modify skill configuration')
250
+ .option('-i, --info', 'Show skill info (default)')
251
+ .option('-c, --config', 'Show/edit skill configuration')
252
+ .option('-s, --set <key> <value>', 'Set config value')
253
+ .option('-r, --read', 'Read SKILL.md content')
254
+ .option('-e, --edit', 'Open SKILL.md in editor')
255
+ .option('-j, --json', 'Output as JSON (for tool integration)')
256
+ .action(async (name, options) => {
257
+ try {
258
+ const fm = await initFlowMind();
259
+ const skill = fm.skills.get(name);
260
+
261
+ if (!skill) {
262
+ console.error(chalk.red(`Skill not found: ${name}`));
263
+ console.log(chalk.cyan('\nAvailable skills:'));
264
+ fm.skills.list().forEach(s => console.log(` - ${s.name}`));
265
+ return;
266
+ }
267
+
268
+ // Default to info if no option specified
269
+ if (!options.config && !options.read && !options.edit && !options.set) {
270
+ options.info = true;
271
+ }
272
+
273
+ if (options.info) {
274
+ await showSkillInfo(skill, options.json);
275
+ } else if (options.read) {
276
+ await readSkillMd(skill);
277
+ } else if (options.edit) {
278
+ await editSkillMd(skill);
279
+ } else if (options.config) {
280
+ await showSkillConfig(skill, fm, options.json);
281
+ } else if (options.set) {
282
+ // options.set is the key, need value from next arg
283
+ const value = options.set;
284
+ const key = options.set;
285
+ // Get key and value from command line
286
+ const args = process.argv.slice(3);
287
+ if (args.length >= 2) {
288
+ await setSkillConfig(skill, fm, args[0], args[1]);
289
+ } else {
290
+ console.error(chalk.red('Usage: flowmind skill <name> --set <key> <value>'));
291
+ }
292
+ }
293
+ } catch (error) {
294
+ console.error(chalk.red('Error:'), error.message);
295
+ }
296
+ });
297
+
298
+ // Resource command (local resource files)
299
+ program
300
+ .command('resource')
301
+ .alias('res')
302
+ .description('Manage local resource files')
303
+ .option('-l, --list [dir]', 'List resource directory')
304
+ .option('-s, --show <file>', 'Show file content')
305
+ .option('-e, --edit <file>', 'Edit file')
306
+ .option('-c, --config', 'Show resource configuration')
307
+ .option('-j, --json', 'Output as JSON (for tool integration)')
308
+ .action(async (options) => {
309
+ try {
310
+ const fm = await initFlowMind();
311
+ const resourceConfig = fm.config.get('resources', {});
312
+ const configDir = path.join(process.env.HOME || process.env.USERPROFILE, '.flowmind');
313
+
314
+ if (options.config) {
315
+ // Show resource configuration
316
+ if (options.json) {
317
+ console.log(JSON.stringify({ resources: resourceConfig }, null, 2));
318
+ } else {
319
+ displayResourceConfig(resourceConfig);
320
+ }
321
+ } else if (options.show) {
322
+ // Show file content
323
+ const filePath = resolveResourcePath(options.show, configDir);
324
+ if (await fs.pathExists(filePath)) {
325
+ const content = await fs.readFile(filePath, 'utf-8');
326
+ if (options.json) {
327
+ console.log(JSON.stringify({ file: options.show, content }, null, 2));
328
+ } else {
329
+ console.log(chalk.cyan(`\n📄 ${options.show}`));
330
+ console.log(chalk.gray('─'.repeat(50)));
331
+ console.log(content);
332
+ }
333
+ } else {
334
+ console.error(chalk.red(`File not found: ${options.show}`));
335
+ }
336
+ } else if (options.edit) {
337
+ // Edit file
338
+ const filePath = resolveResourcePath(options.edit, configDir);
339
+ await openInEditor(filePath);
340
+ } else {
341
+ // List directory (default)
342
+ const targetDir = options.list && typeof options.list === 'string'
343
+ ? resolveResourcePath(options.list, configDir)
344
+ : configDir;
345
+
346
+ if (await fs.pathExists(targetDir)) {
347
+ const files = await listResourceFiles(targetDir, configDir);
348
+ if (options.json) {
349
+ console.log(JSON.stringify({ directory: targetDir, files }, null, 2));
350
+ } else {
351
+ displayResourceFiles(files, targetDir);
352
+ }
353
+ } else {
354
+ console.error(chalk.red(`Directory not found: ${targetDir}`));
355
+ }
356
+ }
227
357
  } catch (error) {
228
358
  console.error(chalk.red('Error:'), error.message);
229
359
  }
@@ -432,15 +562,19 @@ function displayScenes(scenes) {
432
562
  console.log(chalk.cyan('└─────────────────────────────────────────────────────┘'));
433
563
  }
434
564
 
435
- function displaySkills(skills) {
565
+ function displaySkills(skills, verbose = false) {
436
566
  console.log(chalk.cyan('\n┌─────────────────────────────────────────────────────┐'));
437
567
  console.log(chalk.cyan('│ Available Skills │'));
438
568
  console.log(chalk.cyan('├─────────────────────────────────────────────────────┤'));
439
569
 
440
570
  for (const skill of skills) {
441
- console.log(chalk.cyan(`│ ${skill.name}`));
571
+ console.log(chalk.cyan(`│ ${chalk.bold(skill.name)}`));
442
572
  if (skill.description) {
443
- console.log(chalk.cyan(`│ ${skill.description.substring(0, 50)}...`));
573
+ const desc = verbose ? skill.description : skill.description.substring(0, 50) + '...';
574
+ console.log(chalk.cyan(`│ ${desc}`));
575
+ }
576
+ if (verbose && skill.category) {
577
+ console.log(chalk.cyan(`│ Category: ${skill.category}`));
444
578
  }
445
579
  console.log(chalk.cyan('├─────────────────────────────────────────────────────┤'));
446
580
  }
@@ -448,6 +582,256 @@ function displaySkills(skills) {
448
582
  console.log(chalk.cyan('└─────────────────────────────────────────────────────┘'));
449
583
  }
450
584
 
585
+ // Skill info display
586
+ async function showSkillInfo(skill, asJson = false) {
587
+ const info = {
588
+ name: skill.name,
589
+ path: skill.path,
590
+ description: skill.definition?.description || 'No description',
591
+ version: skill.definition?.version || skill.definition?.metadata?.version || '1.0.0',
592
+ author: skill.definition?.author || skill.definition?.metadata?.author || 'unknown',
593
+ category: skill.definition?.category || skill.definition?.metadata?.category || 'general',
594
+ componentDependencies: skill.definition?.componentDependencies || [],
595
+ triggers: skill.definition?.triggers || []
596
+ };
597
+
598
+ if (asJson) {
599
+ console.log(JSON.stringify(info, null, 2));
600
+ } else {
601
+ console.log(chalk.cyan('\n┌─────────────────────────────────────────────────────┐'));
602
+ console.log(chalk.cyan(`│ ${chalk.bold(info.name)}`));
603
+ console.log(chalk.cyan('├─────────────────────────────────────────────────────┤'));
604
+ console.log(chalk.cyan(`│ Description: ${info.description}`));
605
+ console.log(chalk.cyan(`│ Version: ${info.version}`));
606
+ console.log(chalk.cyan(`│ Author: ${info.author}`));
607
+ console.log(chalk.cyan(`│ Category: ${info.category}`));
608
+ console.log(chalk.cyan(`│ Path: ${info.path}`));
609
+
610
+ if (info.componentDependencies.length > 0) {
611
+ console.log(chalk.cyan('├─────────────────────────────────────────────────────┤'));
612
+ console.log(chalk.cyan('│ Component Dependencies:'));
613
+ for (const dep of info.componentDependencies) {
614
+ console.log(chalk.cyan(`│ - ${dep}`));
615
+ }
616
+ }
617
+
618
+ if (info.triggers.length > 0) {
619
+ console.log(chalk.cyan('├─────────────────────────────────────────────────────┤'));
620
+ console.log(chalk.cyan('│ Trigger Patterns:'));
621
+ for (const trigger of info.triggers.slice(0, 10)) {
622
+ console.log(chalk.cyan(`│ - ${trigger}`));
623
+ }
624
+ if (info.triggers.length > 10) {
625
+ console.log(chalk.cyan(`│ ... and ${info.triggers.length - 10} more`));
626
+ }
627
+ }
628
+
629
+ console.log(chalk.cyan('└─────────────────────────────────────────────────────┘'));
630
+ }
631
+ }
632
+
633
+ // Read SKILL.md content
634
+ async function readSkillMd(skill) {
635
+ const skillMdPath = path.join(skill.path, 'SKILL.md');
636
+ if (await fs.pathExists(skillMdPath)) {
637
+ const content = await fs.readFile(skillMdPath, 'utf-8');
638
+ console.log(chalk.cyan(`\n📄 ${skill.name}/SKILL.md`));
639
+ console.log(chalk.gray('─'.repeat(50)));
640
+ console.log(content);
641
+ } else {
642
+ console.error(chalk.red('SKILL.md not found'));
643
+ }
644
+ }
645
+
646
+ // Edit SKILL.md
647
+ async function editSkillMd(skill) {
648
+ const skillMdPath = path.join(skill.path, 'SKILL.md');
649
+ await openInEditor(skillMdPath);
650
+ }
651
+
652
+ // Show skill configuration
653
+ async function showSkillConfig(skill, fm, asJson = false) {
654
+ const configKey = skill.name;
655
+ const config = fm.config.get(configKey, {});
656
+
657
+ if (asJson) {
658
+ console.log(JSON.stringify({ skill: skill.name, config }, null, 2));
659
+ } else {
660
+ console.log(chalk.cyan(`\n┌─────────────────────────────────────────────────────┐`));
661
+ console.log(chalk.cyan(`│ ${skill.name} Configuration`));
662
+ console.log(chalk.cyan('├─────────────────────────────────────────────────────┤'));
663
+
664
+ if (Object.keys(config).length === 0) {
665
+ console.log(chalk.cyan('│ No configuration set'));
666
+ console.log(chalk.cyan('│'));
667
+ console.log(chalk.cyan('│ Default configuration from SKILL.md:'));
668
+
669
+ // Extract default config from SKILL.md
670
+ const skillMdPath = path.join(skill.path, 'SKILL.md');
671
+ if (await fs.pathExists(skillMdPath)) {
672
+ const content = await fs.readFile(skillMdPath, 'utf-8');
673
+ const configMatch = content.match(/## Configuration\n([\s\S]*?)(?=\n##|$)/);
674
+ if (configMatch) {
675
+ const configSection = configMatch[1].trim();
676
+ const lines = configSection.split('\n').slice(0, 15);
677
+ for (const line of lines) {
678
+ console.log(chalk.cyan(`│ ${line}`));
679
+ }
680
+ }
681
+ }
682
+ } else {
683
+ const configJson = JSON.stringify(config, null, 2);
684
+ configJson.split('\n').forEach(line => {
685
+ console.log(chalk.cyan(`│ ${line}`));
686
+ });
687
+ }
688
+
689
+ console.log(chalk.cyan('└─────────────────────────────────────────────────────┘'));
690
+ }
691
+ }
692
+
693
+ // Set skill configuration
694
+ async function setSkillConfig(skill, fm, key, value) {
695
+ const configKey = skill.name;
696
+ const config = fm.config.get(configKey, {});
697
+
698
+ // Support nested keys like "security.enabled"
699
+ const keys = key.split('.');
700
+ let current = config;
701
+ for (let i = 0; i < keys.length - 1; i++) {
702
+ if (!current[keys[i]]) current[keys[i]] = {};
703
+ current = current[keys[i]];
704
+ }
705
+
706
+ // Try to parse value as JSON, fallback to string
707
+ let parsedValue;
708
+ try {
709
+ parsedValue = JSON.parse(value);
710
+ } catch {
711
+ parsedValue = value;
712
+ }
713
+
714
+ current[keys[keys.length - 1]] = parsedValue;
715
+ fm.config.set(configKey, config);
716
+ await fm.config.save();
717
+
718
+ console.log(chalk.green(`✓ Set ${skill.name}.${key} = ${value}`));
719
+ }
720
+
721
+ // Resource file helpers
722
+ function resolveResourcePath(filePath, configDir) {
723
+ // If absolute path, use as-is
724
+ if (path.isAbsolute(filePath)) {
725
+ return filePath;
726
+ }
727
+ // Otherwise resolve relative to config dir
728
+ return path.join(configDir, filePath);
729
+ }
730
+
731
+ async function listResourceFiles(dir, configDir) {
732
+ const files = [];
733
+ const entries = await fs.readdir(dir);
734
+
735
+ for (const entry of entries) {
736
+ const fullPath = path.join(dir, entry);
737
+ const stat = await fs.stat(fullPath);
738
+ const relativePath = path.relative(configDir, fullPath);
739
+
740
+ if (stat.isDirectory()) {
741
+ files.push({
742
+ name: entry,
743
+ path: relativePath,
744
+ type: 'directory',
745
+ size: 0
746
+ });
747
+ } else {
748
+ files.push({
749
+ name: entry,
750
+ path: relativePath,
751
+ type: 'file',
752
+ size: stat.size,
753
+ modified: stat.mtime
754
+ });
755
+ }
756
+ }
757
+
758
+ return files.sort((a, b) => {
759
+ if (a.type === b.type) return a.name.localeCompare(b.name);
760
+ return a.type === 'directory' ? -1 : 1;
761
+ });
762
+ }
763
+
764
+ function displayResourceFiles(files, dir) {
765
+ console.log(chalk.cyan(`\n📁 ${dir}`));
766
+ console.log(chalk.gray('─'.repeat(50)));
767
+
768
+ if (files.length === 0) {
769
+ console.log(chalk.cyan(' (empty)'));
770
+ return;
771
+ }
772
+
773
+ for (const file of files) {
774
+ if (file.type === 'directory') {
775
+ console.log(chalk.blue(` 📁 ${file.name}/`));
776
+ } else {
777
+ const size = formatFileSize(file.size);
778
+ console.log(chalk.white(` 📄 ${file.name}`) + chalk.gray(` (${size})`));
779
+ }
780
+ }
781
+ }
782
+
783
+ function displayResourceConfig(config) {
784
+ console.log(chalk.cyan('\n┌─────────────────────────────────────────────────────┐'));
785
+ console.log(chalk.cyan('│ Resource Configuration │'));
786
+ console.log(chalk.cyan('├─────────────────────────────────────────────────────┤'));
787
+
788
+ const entries = Object.entries(config);
789
+ if (entries.length === 0) {
790
+ console.log(chalk.cyan('│ No resources configured'));
791
+ } else {
792
+ for (const [key, value] of entries) {
793
+ const enabled = value.enabled !== false;
794
+ const status = enabled ? chalk.green('✓') : chalk.red('✗');
795
+ console.log(chalk.cyan(`│ ${status} ${key}`));
796
+ if (value.path) {
797
+ console.log(chalk.cyan(`│ Path: ${value.path}`));
798
+ }
799
+ }
800
+ }
801
+
802
+ console.log(chalk.cyan('└─────────────────────────────────────────────────────┘'));
803
+ }
804
+
805
+ function formatFileSize(bytes) {
806
+ if (bytes === 0) return '0 B';
807
+ const k = 1024;
808
+ const sizes = ['B', 'KB', 'MB', 'GB'];
809
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
810
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(1)) + ' ' + sizes[i];
811
+ }
812
+
813
+ async function openInEditor(filePath) {
814
+ const { spawn } = require('child_process');
815
+ const editor = process.env.EDITOR || process.env.VISUAL || 'vi';
816
+
817
+ console.log(chalk.cyan(`Opening ${filePath} in ${editor}...`));
818
+
819
+ return new Promise((resolve, reject) => {
820
+ const child = spawn(editor, [filePath], {
821
+ stdio: 'inherit',
822
+ shell: true
823
+ });
824
+
825
+ child.on('close', (code) => {
826
+ if (code === 0) {
827
+ resolve();
828
+ } else {
829
+ reject(new Error(`Editor exited with code ${code}`));
830
+ }
831
+ });
832
+ });
833
+ }
834
+
451
835
  // Parse arguments
452
836
  program.parse(process.argv);
453
837
 
@@ -41,14 +41,14 @@ const McpServerMapping = Object.freeze({
41
41
  },
42
42
 
43
43
  // API Documentation
44
- 'aomi-yapi-mcp': {
44
+ 'yapi-mcp': {
45
45
  type: ComponentType.API_DOC,
46
46
  provider: 'yapi',
47
47
  description: 'YApi API documentation management'
48
48
  },
49
49
 
50
50
  // Knowledge Base
51
- 'aomi-yuque-mcp': {
51
+ 'yuque-mcp': {
52
52
  type: ComponentType.KNOWLEDGE_BASE,
53
53
  provider: 'yuque',
54
54
  description: 'Yuque knowledge base management'
@@ -77,8 +77,8 @@ const ProviderToMcp = Object.freeze({
77
77
  'databaseManager:aliyun-dms': 'aliyun-dms-mcp-server',
78
78
  'databaseQuery:aliyun-rds-query': 'friday-rds-redis-query',
79
79
  'redisMonitor:aliyun-redis': 'friday-aliyun-sz-rds-redis',
80
- 'apiDoc:yapi': 'aomi-yapi-mcp',
81
- 'knowledgeBase:yuque': 'aomi-yuque-mcp',
80
+ 'apiDoc:yapi': 'yapi-mcp',
81
+ 'knowledgeBase:yuque': 'yuque-mcp',
82
82
  'workflow:friday-flow': 'friday-auto-flow',
83
83
  'report:friday-report': 'friday-auto-report'
84
84
  });
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * YApi API Documentation Adapter
3
- * Wraps the aomi-yapi-mcp MCP server for YApi platform management
3
+ * Wraps the yapi-mcp MCP server for YApi platform management
4
4
  */
5
5
 
6
6
  const ApiDocAdapter = require('../../adapters/api-doc-adapter');
@@ -24,7 +24,7 @@ class YapiAdapter extends ApiDocAdapter {
24
24
  }
25
25
 
26
26
  get mcpServer() {
27
- return 'aomi-yapi-mcp';
27
+ return 'yapi-mcp';
28
28
  }
29
29
 
30
30
  async searchApis(params) {
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * Yuque Knowledge Base Adapter
3
- * Wraps the aomi-yuque-mcp MCP server for Yuque platform management
3
+ * Wraps the yuque-mcp MCP server for Yuque platform management
4
4
  */
5
5
 
6
6
  const KnowledgeBaseAdapter = require('../../adapters/knowledge-base-adapter');
@@ -23,7 +23,7 @@ class YuqueAdapter extends KnowledgeBaseAdapter {
23
23
  }
24
24
 
25
25
  get mcpServer() {
26
- return 'aomi-yuque-mcp';
26
+ return 'yuque-mcp';
27
27
  }
28
28
 
29
29
  async getRepos(params) {
@@ -96,10 +96,35 @@ class SkillLoader {
96
96
 
97
97
  // Parse YAML-like frontmatter
98
98
  const lines = frontmatter.split('\n');
99
+ let currentKey = null;
100
+ let currentObj = null;
101
+
99
102
  for (const line of lines) {
103
+ // Top-level key: value
100
104
  const match = line.match(/^(\w+):\s*(.+)$/);
101
105
  if (match) {
102
- definition[match[1]] = match[2].replace(/^["']|["']$/g, '');
106
+ const key = match[1];
107
+ const value = match[2].replace(/^["']|["']$/g, '');
108
+ definition[key] = value;
109
+ currentKey = key;
110
+ currentObj = null;
111
+ continue;
112
+ }
113
+
114
+ // Nested object (e.g., metadata:)
115
+ const nestedMatch = line.match(/^(\w+):\s*$/);
116
+ if (nestedMatch) {
117
+ currentKey = nestedMatch[1];
118
+ definition[currentKey] = {};
119
+ currentObj = definition[currentKey];
120
+ continue;
121
+ }
122
+
123
+ // Nested key: value (indented)
124
+ const nestedValueMatch = line.match(/^\s+(\w+):\s*(.+)$/);
125
+ if (nestedValueMatch && currentObj) {
126
+ currentObj[nestedValueMatch[1]] = nestedValueMatch[2].replace(/^["']|["']$/g, '');
127
+ continue;
103
128
  }
104
129
  }
105
130
 
@@ -114,7 +139,7 @@ class SkillLoader {
114
139
  }
115
140
 
116
141
  // Extract trigger patterns
117
- const triggerMatch = content.match(/## Trigger Conditions\n([\s\S]*?)(?=\n##|$)/);
142
+ const triggerMatch = content.match(/## Trigger Patterns\n([\s\S]*?)(?=\n##|$)/);
118
143
  if (triggerMatch) {
119
144
  definition.triggers = this.extractTriggers(triggerMatch[1]);
120
145
  }
@@ -266,7 +291,10 @@ class SkillLoader {
266
291
  return Array.from(this.skills.values()).map(s => ({
267
292
  name: s.name,
268
293
  description: s.definition?.description,
269
- category: s.definition?.category
294
+ category: s.definition?.category || s.definition?.metadata?.category,
295
+ version: s.definition?.version || s.definition?.metadata?.version,
296
+ author: s.definition?.author || s.definition?.metadata?.author,
297
+ path: s.path
270
298
  }));
271
299
  }
272
300
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "flowmind",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "The AI Agent That Learns How You Work - Stop repeating yourself, FlowMind learns your workflows and applies them automatically.",
5
5
  "main": "core/index.js",
6
6
  "bin": {
@@ -19,8 +19,8 @@ const DEFAULT_MAPPING = {
19
19
  'aliyun-dms-mcp-server': { type: 'databaseManager', provider: 'aliyun-dms' },
20
20
  'friday-rds-redis-query': { type: 'databaseQuery', provider: 'aliyun-rds-query' },
21
21
  'friday-aliyun-sz-rds-redis': { type: 'redisMonitor', provider: 'aliyun-redis' },
22
- 'aomi-yapi-mcp': { type: 'apiDoc', provider: 'yapi' },
23
- 'aomi-yuque-mcp': { type: 'knowledgeBase', provider: 'yuque' },
22
+ 'yapi-mcp': { type: 'apiDoc', provider: 'yapi' },
23
+ 'yuque-mcp': { type: 'knowledgeBase', provider: 'yuque' },
24
24
  'friday-auto-flow': { type: 'workflow', provider: 'friday-flow' },
25
25
  'friday-auto-report': { type: 'report', provider: 'friday-report' }
26
26
  };
@@ -68,7 +68,7 @@ This skill uses the **apiDoc** component. The actual API documentation provider
68
68
 
69
69
  | Provider | MCP Server | Description |
70
70
  |----------|------------|-------------|
71
- | yapi | aomi-yapi-mcp | YApi API documentation platform |
71
+ | yapi | yapi-mcp | YApi API documentation platform |
72
72
 
73
73
  Configuration is managed in `flowmind.config.json` under `components.apiDoc`.
74
74
 
@@ -69,7 +69,7 @@ This skill uses the **knowledgeBase** component. The actual knowledge base provi
69
69
 
70
70
  | Provider | MCP Server | Description |
71
71
  |----------|------------|-------------|
72
- | yuque | aomi-yuque-mcp | Yuque knowledge base |
72
+ | yuque | yuque-mcp | Yuque knowledge base |
73
73
  | notion | notion-mcp | Notion workspace |
74
74
 
75
75
  Configuration is managed in `flowmind.config.json` under `components.knowledgeBase`.
@@ -105,12 +105,12 @@ FlowMind:
105
105
  ┌─────────────────────────────────────────────────────┐
106
106
  │ Yuque Design Sync Report │
107
107
  ├─────────────────────────────────────────────────────┤
108
- │ Repository: aomi-design-docs
108
+ │ Repository: design-docs
109
109
  │ Document: 订单模块详细设计 │
110
110
  ├─────────────────────────────────────────────────────┤
111
111
  │ Action: Update │
112
112
  │ Status: ✅ Success │
113
- │ URL: https://yuque.com/aomi/design/order-module
113
+ │ URL: https://yuque.com/your-org/design/order-module
114
114
  ├─────────────────────────────────────────────────────┤
115
115
  │ Sections Synced: 6 │
116
116
  │ • API Design: ✅ │
@@ -134,9 +134,9 @@ FlowMind:
134
134
  │ Keyword: 订单 │
135
135
  │ Results: 5 documents │
136
136
  ├─────────────────────────────────────────────────────┤
137
- │ 1. 订单模块详细设计 (aomi-design-docs)
137
+ │ 1. 订单模块详细设计 (design-docs)
138
138
  │ Updated: 2026-06-20 │
139
- │ 2. 订单流程图 (aomi-design-docs)
139
+ │ 2. 订单流程图 (design-docs)
140
140
  │ Updated: 2026-06-18 │
141
141
  │ 3. 订单API文档 (api-docs) │
142
142
  │ Updated: 2026-06-15 │
@@ -148,7 +148,7 @@ FlowMind:
148
148
  ```json
149
149
  {
150
150
  "yuque-sync-design": {
151
- "defaultRepo": "aomi-design-docs",
151
+ "defaultRepo": "design-docs",
152
152
  "syncFormat": "markdown",
153
153
  "autoCreateToc": true,
154
154
  "archiveAfterSync": true