huaweicloud-devkit 0.1.26-dev.0 → 1.0.2-dev.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.
@@ -19,7 +19,7 @@ try {
19
19
  const BANNER = `
20
20
  ╔══════════════════════════════════════════════╗
21
21
  ║ HuaweiCloud DevKit v${pkgVersion}${' '.repeat(Math.max(0, 22 - String(pkgVersion).length))}║
22
- ║ https://github.com/huaweicloud-mate
22
+ ║ https://github.com/huaweicloud ║
23
23
  ╚══════════════════════════════════════════════╝
24
24
  `;
25
25
 
@@ -50,6 +50,10 @@ function codeartsProjectSkillsDir() { return join(codeartsProjectDir(), 'skills'
50
50
  function codeartsProjectMcpSettingsFile() { return join(codeartsProjectDir(), 'mcp', 'mcp_settings.json'); }
51
51
  function codeartsPluginsDir() { return join(homedir(), '.codeartsdoer', 'huaweicloud-plugins'); }
52
52
 
53
+ function workbuddySkillsDir() { return join(homedir(), '.workbuddy', 'skills'); }
54
+ function workbuddyMcpConfigFile() { return join(homedir(), '.workbuddy', 'mcp.json'); }
55
+ function workbuddyPluginsDir() { return join(homedir(), '.workbuddy', 'huaweicloud-plugins'); }
56
+
53
57
  // Detect CodeArts sandbox mode (bash_mode in permission config).
54
58
  function detectCodeartsSandbox() {
55
59
  try {
@@ -110,8 +114,13 @@ function copyDir(src, dest) {
110
114
 
111
115
  function removeIfExists(p) {
112
116
  if (existsSync(p)) {
113
- rmSync(p, { recursive: true, force: true });
114
- return true;
117
+ try {
118
+ rmSync(p, { recursive: true, force: true });
119
+ return true;
120
+ } catch (e) {
121
+ console.log(` \x1b[33m[WARN]\x1b[0m Could not remove ${p}: ${e.message}`);
122
+ return false;
123
+ }
115
124
  }
116
125
  return false;
117
126
  }
@@ -463,6 +472,93 @@ function codeartsStatus() {
463
472
  }
464
473
  }
465
474
 
475
+ async function installWorkBuddy() {
476
+ const skillsSrc = join(PLUGIN_ROOT, 'skills');
477
+ const srcDir = join(PLUGIN_ROOT, 'src');
478
+ const safetyDir = join(PLUGIN_ROOT, 'safety');
479
+ const pluginDest = workbuddyPluginsDir();
480
+
481
+ copyDir(skillsSrc, workbuddySkillsDir());
482
+ console.log(` Skills -> ${workbuddySkillsDir()}`);
483
+
484
+ copyDir(srcDir, join(pluginDest, 'src'));
485
+ console.log(` MCP Server -> ${join(pluginDest, 'src')}`);
486
+ copyDir(safetyDir, join(pluginDest, 'safety'));
487
+ console.log(` Safety Policy -> ${join(pluginDest, 'safety')}`);
488
+
489
+ // Write MCP config to ~/.workbuddy/mcp.json
490
+ const mcpPath = join(pluginDest, 'src', 'mcp-server.mjs').replace(/\\/g, '/');
491
+ const configPath = workbuddyMcpConfigFile();
492
+ let config = {};
493
+ if (existsSync(configPath)) {
494
+ try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {}
495
+ }
496
+ const env = { HUAWEICLOUD_AGENT_TOOLKIT_MODE: 'local' };
497
+ const hcloudBin = findHcloudBin();
498
+ if (hcloudBin) env.HCLOUD_BIN = hcloudBin.replace(/\\/g, '/');
499
+ config.mcpServers = config.mcpServers || {};
500
+ config.mcpServers['huaweicloud-devkit'] = {
501
+ command: 'node',
502
+ args: [mcpPath],
503
+ env,
504
+ };
505
+ mkdirSync(dirname(configPath), { recursive: true });
506
+ writeFileSync(configPath, JSON.stringify(config, null, 2));
507
+ console.log(` MCP config updated: ${configPath}`);
508
+ }
509
+
510
+ function uninstallWorkBuddy() {
511
+ const skillsDir = workbuddySkillsDir();
512
+ let removed = 0;
513
+ if (existsSync(skillsDir)) {
514
+ for (const entry of readdirSync(skillsDir, { withFileTypes: true })) {
515
+ if (entry.name.startsWith('huawei')) {
516
+ removeIfExists(join(skillsDir, entry.name));
517
+ removed++;
518
+ }
519
+ }
520
+ if (removed > 0) console.log(` Removed ${removed} skills`);
521
+ }
522
+
523
+ if (removeIfExists(workbuddyPluginsDir())) {
524
+ console.log(' Removed MCP server and safety policy');
525
+ }
526
+
527
+ const configPath = workbuddyMcpConfigFile();
528
+ if (existsSync(configPath)) {
529
+ let config = {};
530
+ try { config = JSON.parse(readFileSync(configPath, 'utf8')); } catch {}
531
+ if (config.mcpServers?.['huaweicloud-devkit']) {
532
+ delete config.mcpServers['huaweicloud-devkit'];
533
+ if (Object.keys(config.mcpServers).length === 0) delete config.mcpServers;
534
+ writeFileSync(configPath, JSON.stringify(config, null, 2));
535
+ console.log(` MCP config cleaned: ${configPath}`);
536
+ }
537
+ }
538
+ }
539
+
540
+ function workbuddyStatus() {
541
+ const pluginDir = workbuddyPluginsDir();
542
+ const skillsDir = workbuddySkillsDir();
543
+ console.log(` MCP Server: ${existsSync(join(pluginDir, 'src', 'mcp-server.mjs')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`);
544
+ console.log(` Safety Policy: ${existsSync(join(pluginDir, 'safety', 'policy.json')) ? '\x1b[32mInstalled\x1b[0m' : '\x1b[31mNot installed\x1b[0m'}`);
545
+ let skillCount = 0;
546
+ if (existsSync(skillsDir)) {
547
+ skillCount = readdirSync(skillsDir, { withFileTypes: true })
548
+ .filter((d) => d.isDirectory() && d.name.startsWith('huawei')).length;
549
+ }
550
+ console.log(` Skills: ${skillCount > 0 ? `\x1b[32m${skillCount} installed\x1b[0m` : '\x1b[31mNot installed\x1b[0m'}`);
551
+ const configPath = workbuddyMcpConfigFile();
552
+ if (existsSync(configPath)) {
553
+ try {
554
+ const config = JSON.parse(readFileSync(configPath, 'utf8'));
555
+ console.log(` MCP config: ${config.mcpServers?.['huaweicloud-devkit'] ? '\x1b[32mConfigured\x1b[0m' : '\x1b[31mNot configured\x1b[0m'}`);
556
+ } catch {
557
+ console.log(` MCP config: \x1b[31mInvalid\x1b[0m`);
558
+ }
559
+ }
560
+ }
561
+
466
562
  function opencodeStatus() {
467
563
  const pluginDir = opencodePluginsDir();
468
564
  const skillsDir = opencodeSkillsDir();
@@ -492,6 +588,7 @@ function parseTarget() {
492
588
  if (val === 'codex') return 'codex';
493
589
  if (val === 'codex-desktop') return 'codex-desktop';
494
590
  if (val === 'codearts') return 'codearts';
591
+ if (val === 'workbuddy') return 'workbuddy';
495
592
  if (val === 'all') return 'all';
496
593
  return 'opencode';
497
594
  }
@@ -514,6 +611,10 @@ async function cmdInstall() {
514
611
  console.log('\n[CodeArts]');
515
612
  await installCodeArts();
516
613
  }
614
+ if (target === 'workbuddy' || target === 'all') {
615
+ console.log('\n[WorkBuddy]');
616
+ await installWorkBuddy();
617
+ }
517
618
  if (target === 'codex' || target === 'all') {
518
619
  console.log('\n[Codex]');
519
620
  if (!hasCodexCLI()) {
@@ -539,7 +640,9 @@ async function cmdInstall() {
539
640
  } console.log(`\n\x1b[32mInstallation complete!\x1b[0m`);
540
641
  const appName = target === 'codearts' ? 'CodeArts'
541
642
  : target === 'codex-desktop' ? 'Codex Desktop'
542
- : target === 'codex' ? 'Codex' : 'OpenCode';
643
+ : target === 'codex' ? 'Codex'
644
+ : target === 'workbuddy' ? 'WorkBuddy'
645
+ : 'OpenCode';
543
646
  const pad = ' '.repeat(24 - appName.length);
544
647
  console.log(`\n\x1b[1m\x1b[33m╔══════════════════════════════════════════════════════╗`);
545
648
  console.log(`\x1b[1m\x1b[33m║ MCP 工具在重启 ${appName} 会话后才生效${pad}║`);
@@ -558,7 +661,9 @@ async function cmdInstall() {
558
661
  console.log(`\nAfter restart + hcloud setup, run: npx huaweicloud-devkit doctor`);
559
662
 
560
663
  // Write install marker for doctor to detect
561
- const markerDir = target === 'codearts' ? codeartsPluginsDir() : opencodePluginsDir();
664
+ const markerDir = target === 'codearts' ? codeartsPluginsDir()
665
+ : target === 'workbuddy' ? workbuddyPluginsDir()
666
+ : opencodePluginsDir();
562
667
  writeFileSync(join(markerDir, '.installed'), new Date().toISOString());
563
668
  if (target === 'opencode' || target === 'all') {
564
669
  console.log('Or describe your Huawei Cloud task in OpenCode');
@@ -569,6 +674,9 @@ async function cmdInstall() {
569
674
  if (target === 'codex' || target === 'all') {
570
675
  console.log('Or mention @huaweicloud-core in Codex');
571
676
  }
677
+ if (target === 'workbuddy' || target === 'all') {
678
+ console.log('Or describe your Huawei Cloud task in WorkBuddy');
679
+ }
572
680
  }
573
681
 
574
682
  async function cmdUninstall() {
@@ -584,6 +692,10 @@ async function cmdUninstall() {
584
692
  console.log('\n[CodeArts]');
585
693
  uninstallCodeArts();
586
694
  }
695
+ if (target === 'workbuddy' || target === 'all') {
696
+ console.log('\n[WorkBuddy]');
697
+ uninstallWorkBuddy();
698
+ }
587
699
  if (target === 'codex' || target === 'all') {
588
700
  console.log('\n[Codex]');
589
701
  uninstallCodexDesktop();
@@ -609,6 +721,10 @@ async function cmdStatus() {
609
721
  console.log('\n[CodeArts]');
610
722
  codeartsStatus();
611
723
  }
724
+ if (target === 'workbuddy' || target === 'all') {
725
+ console.log('\n[WorkBuddy]');
726
+ workbuddyStatus();
727
+ }
612
728
  if (target === 'codex' || target === 'all') {
613
729
  console.log('\n[Codex]');
614
730
  if (!hasCodexCLI()) {
@@ -636,13 +752,16 @@ async function cmdDoctor() {
636
752
  // Node.js
637
753
  check('Node.js >= 20', process.versions.node.split('.')[0] >= 20, 'Run: nvm install 20 && nvm use 20');
638
754
 
639
- // MCP server — check OpenCode and Codex Desktop paths
755
+ // MCP server — check OpenCode, Codex Desktop, CodeArts, and WorkBuddy paths
640
756
  const opencodePluginDir = opencodePluginsDir();
641
757
  const codexPluginDir = codexDesktopPluginsDir();
758
+ const workbuddyPluginDir = workbuddyPluginsDir();
642
759
  const mcpOk = existsSync(join(opencodePluginDir, 'src', 'mcp-server.mjs'))
643
- || existsSync(join(codexPluginDir, 'src', 'mcp-server.mjs'));
760
+ || existsSync(join(codexPluginDir, 'src', 'mcp-server.mjs'))
761
+ || existsSync(join(workbuddyPluginDir, 'src', 'mcp-server.mjs'));
644
762
  const mcpTarget = existsSync(join(opencodePluginDir, 'src', 'mcp-server.mjs')) ? 'OpenCode'
645
- : existsSync(join(codexPluginDir, 'src', 'mcp-server.mjs')) ? 'Codex Desktop' : '';
763
+ : existsSync(join(codexPluginDir, 'src', 'mcp-server.mjs')) ? 'Codex Desktop'
764
+ : existsSync(join(workbuddyPluginDir, 'src', 'mcp-server.mjs')) ? 'WorkBuddy' : '';
646
765
  check('MCP server installed', mcpOk, 'Run: npx huaweicloud-devkit install');
647
766
 
648
767
  if (mcpOk) {
@@ -650,10 +769,11 @@ async function cmdDoctor() {
650
769
  }
651
770
 
652
771
  const safetyOk = existsSync(join(opencodePluginDir, 'safety', 'policy.json'))
653
- || existsSync(join(codexPluginDir, 'safety', 'policy.json'));
772
+ || existsSync(join(codexPluginDir, 'safety', 'policy.json'))
773
+ || existsSync(join(workbuddyPluginDir, 'safety', 'policy.json'));
654
774
  check('Safety policy installed', safetyOk, 'Run: npx huaweicloud-devkit install');
655
775
 
656
- // MCP config — check OpenCode and Codex Desktop
776
+ // MCP config — check OpenCode, Codex Desktop, and WorkBuddy
657
777
  let mcpConfigured = false;
658
778
  let mcpCfgTarget = '';
659
779
  const opencodeCfg = opencodeConfigFile();
@@ -670,6 +790,13 @@ async function cmdDoctor() {
670
790
  if (cfg.mcp && cfg.mcp['huaweicloud-devkit']) { mcpConfigured = true; mcpCfgTarget = 'Codex Desktop'; }
671
791
  } catch {}
672
792
  }
793
+ const workbuddyCfg = workbuddyMcpConfigFile();
794
+ if (!mcpConfigured && existsSync(workbuddyCfg)) {
795
+ try {
796
+ const cfg = JSON.parse(readFileSync(workbuddyCfg, 'utf8'));
797
+ if (cfg.mcpServers && cfg.mcpServers['huaweicloud-devkit']) { mcpConfigured = true; mcpCfgTarget = 'WorkBuddy'; }
798
+ } catch {}
799
+ }
673
800
  check('MCP configured', mcpConfigured, mcpCfgTarget ? `Found in ${mcpCfgTarget} config` : 'Run: npx huaweicloud-devkit install');
674
801
 
675
802
  // hcloud CLI
@@ -699,36 +826,51 @@ async function cmdDoctor() {
699
826
  }
700
827
 
701
828
  // Skills
702
- const skillsOptions = [opencodeSkillsDir(), codexDesktopSkillsDir(), codeartsSkillsDir()];
703
- let skillCount = 0, skillsDir = '';
829
+ const skillsOptions = [opencodeSkillsDir(), codexDesktopSkillsDir(), codeartsSkillsDir(), workbuddySkillsDir()];
830
+ let skillCount = 0, skillsDir = '', missingSkills = [];
704
831
  for (const dir of skillsOptions) {
705
832
  if (!existsSync(dir)) continue;
706
- const count = readdirSync(dir, { withFileTypes: true })
707
- .filter((d) => d.isDirectory() && d.name.startsWith('huawei')).length;
833
+ const entries = readdirSync(dir, { withFileTypes: true })
834
+ .filter((d) => d.isDirectory() && d.name.startsWith('huawei'));
835
+ const count = entries.length;
708
836
  if (count > skillCount) { skillCount = count; skillsDir = dir; }
837
+ for (const d of entries) {
838
+ if (!existsSync(join(dir, d.name, 'SKILL.md'))) missingSkills.push(d.name);
839
+ }
709
840
  }
710
841
  const skillsOk = skillCount >= 6;
711
- check(`Skills installed (${skillCount})`, skillsOk, 'Run: npx huaweicloud-devkit-test install');
842
+ check(`Skills installed (${skillCount})`, skillsOk, 'Run: npx huaweicloud-devkit install');
843
+ if (missingSkills.length > 0) {
844
+ console.log(` \x1b[33m[WARN]\x1b[0m ${missingSkills.length} skill(s) missing SKILL.md: ${missingSkills.join(', ')} — Run: npx huaweicloud-devkit install`);
845
+ warn++;
846
+ }
712
847
 
713
848
  console.log(`\nResults: ${pass} pass, ${warn} warn, ${fail} fail`);
714
849
 
715
850
  if (mcpConfigured && !hcloudOk) {
716
- console.log('\n\x1b[33mMCP is configured but hcloud is not installed. Install hcloud then restart OpenCode.\x1b[0m');
851
+ console.log('\n\x1b[33mMCP is configured but hcloud is not installed. Install hcloud then restart your session.\x1b[0m');
717
852
  }
718
853
  if (fail > 0) {
719
- console.log('\x1b[33mFix failures above, then restart your OpenCode / Codex session.\x1b[0m');
854
+ console.log('\x1b[33mFix failures above, then restart your session.\x1b[0m');
720
855
  }
721
856
  if (fail === 0 && mcpConfigured) {
722
- console.log('\n\x1b[32mAll checks passed.\x1b[0m Restart OpenCode, then describe your Huawei Cloud task');
857
+ console.log('\n\x1b[32mAll checks passed.\x1b[0m Restart your session, then describe your Huawei Cloud task');
723
858
  }
724
859
 
725
- // Detect "installed but not restarted" scenario
726
- const installedMarker = join(opencodePluginsDir(), '.installed');
727
- if (mcpConfigured && existsSync(installedMarker)) {
728
- console.log(`\n\x1b[1m\x1b[31m╔══════════════════════════════════════════╗`);
729
- console.log(`\x1b[1m\x1b[31m║ 请重启 OpenCode!MCP 工具尚未激活 ║`);
730
- console.log(`\x1b[1m\x1b[31m║ 关闭当前会话 → 重新打开即可 ║`);
731
- console.log(`\x1b[1m\x1b[31m╚══════════════════════════════════════════╝\x1b[0m`);
860
+ // Detect "installed but not restarted" — check all supported agents
861
+ const installedMarkers = [
862
+ { path: join(opencodePluginsDir(), '.installed'), name: 'OpenCode' },
863
+ { path: join(workbuddyPluginsDir(), '.installed'), name: 'WorkBuddy' },
864
+ { path: join(codeartsPluginsDir(), '.installed'), name: 'CodeArts' },
865
+ ];
866
+ for (const marker of installedMarkers) {
867
+ if (existsSync(marker.path)) {
868
+ console.log(`\n\x1b[1m\x1b[31m╔══════════════════════════════════════════╗`);
869
+ console.log(`\x1b[1m\x1b[31m║ 请重启 ${marker.name}!MCP 工具尚未激活 ║`);
870
+ console.log(`\x1b[1m\x1b[31m║ 关闭当前会话 → 重新打开即可 ║`);
871
+ console.log(`\x1b[1m\x1b[31m╚══════════════════════════════════════════╝\x1b[0m`);
872
+ break;
873
+ }
732
874
  }
733
875
  }
734
876
 
@@ -737,7 +879,9 @@ async function cmdUpdate() {
737
879
  const target = parseTarget();
738
880
 
739
881
  if (target === 'opencode' || target === 'all') {
740
- if (!existsSync(join(opencodePluginsDir(), 'src', 'mcp-server.mjs')) && !codexStatus()) {
882
+ if (!existsSync(join(opencodePluginsDir(), 'src', 'mcp-server.mjs'))
883
+ && !existsSync(join(workbuddyPluginsDir(), 'src', 'mcp-server.mjs'))
884
+ && !codexStatus()) {
741
885
  console.log('\x1b[33mNot installed. Use "install" command first.\x1b[0m');
742
886
  return;
743
887
  }
@@ -908,7 +1052,7 @@ async function main() {
908
1052
  case '-h':
909
1053
  default:
910
1054
  console.log(BANNER);
911
- console.log('Usage: npx huaweicloud-devkit <command> [--target <opencode|codex|codearts|all>]\n');
1055
+ console.log('Usage: npx huaweicloud-devkit <command> [--target <opencode|codex|codearts|workbuddy|all>]\n');
912
1056
  console.log('Commands:');
913
1057
  console.log(' install Install skills, MCP server, safety policy');
914
1058
  console.log(' uninstall Remove installed files');
@@ -919,11 +1063,12 @@ async function main() {
919
1063
  console.log(' install-hcloud Show KooCLI install commands for your OS');
920
1064
  console.log(' help Show this help');
921
1065
  console.log('\nOptions:');
922
- console.log(' --target Target agent: opencode (default), codex, codearts, all');
1066
+ console.log(' --target Target agent: opencode (default), codex, codearts, workbuddy, all');
923
1067
  console.log('\nExamples:');
924
1068
  console.log(' npx huaweicloud-devkit install');
925
1069
  console.log(' npx huaweicloud-devkit install --target codex');
926
1070
  console.log(' npx huaweicloud-devkit install --target codearts');
1071
+ console.log(' npx huaweicloud-devkit install --target workbuddy');
927
1072
  console.log(' npx huaweicloud-devkit install --target all');
928
1073
  break;
929
1074
  }
@@ -17,10 +17,15 @@ function codeartsSkillsDir() {
17
17
  const home = homedir();
18
18
  return join(home, '.codeartsdoer', 'skills');
19
19
  }
20
+ function workbuddySkillsDir() {
21
+ const home = homedir();
22
+ return join(home, '.workbuddy', 'skills');
23
+ }
20
24
  function resolveSkillsRoot() {
21
25
  if (existsSync(SKILLS_ROOT_DEV)) return SKILLS_ROOT_DEV;
22
26
  if (existsSync(codeartsSkillsDir())) return codeartsSkillsDir();
23
27
  if (existsSync(opencodeSkillsDir())) return opencodeSkillsDir();
28
+ if (existsSync(workbuddySkillsDir())) return workbuddySkillsDir();
24
29
  return SKILLS_ROOT_DEV;
25
30
  }
26
31
  const SKILLS_ROOT = resolveSkillsRoot();