@arkhera30/cli 0.1.14 → 0.1.16

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.
Files changed (2) hide show
  1. package/dist/index.js +43 -8
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -611,6 +611,16 @@ function mergeAndWriteConfig(configPath, mcpServers) {
611
611
  mkdirSync2(dir, { recursive: true });
612
612
  writeFileSync3(configPath, JSON.stringify(existing, null, 2) + "\n", "utf-8");
613
613
  }
614
+ function getMcpRemoteWrapperPath() {
615
+ return join3(homedir3(), ".forge", "bin", "mcp-remote-wrapper");
616
+ }
617
+ function buildStdioServers(config, wrapperPath, host) {
618
+ return {
619
+ anvil: { command: wrapperPath, args: [`http://${host}:${config.ports.anvil}/mcp`] },
620
+ vault: { command: wrapperPath, args: [`http://${host}:${config.ports.vault_mcp}/mcp`] },
621
+ forge: { command: wrapperPath, args: [`http://${host}:${config.ports.forge}/mcp`] }
622
+ };
623
+ }
614
624
  async function isClaudeCliAvailable() {
615
625
  try {
616
626
  const result = await execa2("claude", ["--version"], { reject: false });
@@ -624,6 +634,7 @@ async function registerWithClaudeCode(mcpServers) {
624
634
  const failed = [];
625
635
  for (const [name, entry] of Object.entries(mcpServers)) {
626
636
  const baseUrl = entry.url.replace(/\/sse$/, "");
637
+ await execa2("claude", ["mcp", "remove", "--scope", "user", name], { reject: false });
627
638
  const result = await execa2(
628
639
  "claude",
629
640
  ["mcp", "add", "--transport", "http", "--scope", "user", name, baseUrl],
@@ -656,21 +667,25 @@ async function syncSkills(runtime) {
656
667
  async function syncSkillsForCursor(runtime) {
657
668
  const home = homedir3();
658
669
  const rulesDir = join3(home, ".cursor", "rules");
670
+ const skillsBase = join3(home, ".cursor", "skills-cursor");
659
671
  const skills = ["horus-anvil", "horus-vault", "horus-forge"];
660
672
  const forgeContainer = "horus-forge-1";
661
673
  mkdirSync2(rulesDir, { recursive: true });
662
674
  for (const skill of skills) {
663
675
  const src = `/home/forge/.claude/skills/${skill}/SKILL.md`;
664
- const dest = join3(rulesDir, `${skill}.mdc`);
665
676
  const result = await runtime.exec(forgeContainer, "cat", src);
666
677
  if (result.exitCode === 0 && result.stdout.trim()) {
678
+ const ruleDest = join3(rulesDir, `${skill}.mdc`);
667
679
  const frontmatter = `---
668
680
  description: Horus ${skill} reference
669
681
  alwaysApply: true
670
682
  ---
671
683
 
672
684
  `;
673
- writeFileSync3(dest, frontmatter + result.stdout, "utf-8");
685
+ writeFileSync3(ruleDest, frontmatter + result.stdout, "utf-8");
686
+ const skillDir = join3(skillsBase, skill);
687
+ mkdirSync2(skillDir, { recursive: true });
688
+ writeFileSync3(join3(skillDir, "SKILL.md"), result.stdout, "utf-8");
674
689
  }
675
690
  }
676
691
  }
@@ -693,18 +708,38 @@ function printNextSteps(targets) {
693
708
  console.log("");
694
709
  }
695
710
  async function runConnect(config, runtime, targets, host = "localhost") {
696
- const mcpServers = {
711
+ const httpServers = {
697
712
  anvil: { url: `http://${host}:${config.ports.anvil}/sse` },
698
713
  vault: { url: `http://${host}:${config.ports.vault_mcp}/sse` },
699
714
  forge: { url: `http://${host}:${config.ports.forge}/sse` }
700
715
  };
701
716
  const configured = [];
702
717
  for (const target of targets) {
703
- if (target === "claude-code") {
718
+ if (target === "claude-desktop") {
719
+ const desktopSpinner = ora(`Configuring ${chalk.cyan("claude-desktop")}...`).start();
720
+ const wrapperPath = getMcpRemoteWrapperPath();
721
+ if (!existsSync4(wrapperPath)) {
722
+ desktopSpinner.fail("mcp-remote-wrapper not found");
723
+ console.log(chalk.dim(`Expected at: ${wrapperPath}`));
724
+ console.log(chalk.dim("Install it with: npx --yes mcp-remote --help"));
725
+ console.log(chalk.dim("Then place the wrapper script at the path above."));
726
+ continue;
727
+ }
728
+ try {
729
+ const stdioServers = buildStdioServers(config, wrapperPath, host);
730
+ const configPath = getConfigPath(target);
731
+ mergeAndWriteConfig(configPath, stdioServers);
732
+ desktopSpinner.succeed(`Configured ${chalk.cyan("claude-desktop")} \u2014 ${chalk.dim(configPath)}`);
733
+ configured.push(target);
734
+ } catch (error) {
735
+ desktopSpinner.fail("Failed to configure claude-desktop");
736
+ console.log(chalk.dim(error.message));
737
+ }
738
+ } else if (target === "claude-code") {
704
739
  const cliSpinner = ora("Registering MCP servers with Claude Code CLI...").start();
705
740
  const cliAvailable = await isClaudeCliAvailable();
706
741
  if (cliAvailable) {
707
- const { registered, failed } = await registerWithClaudeCode(mcpServers);
742
+ const { registered, failed } = await registerWithClaudeCode(httpServers);
708
743
  if (failed.length === 0) {
709
744
  cliSpinner.succeed(
710
745
  `Registered with Claude Code: ${registered.map((n) => chalk.cyan(n)).join(", ")}`
@@ -720,7 +755,7 @@ async function runConnect(config, runtime, targets, host = "localhost") {
720
755
  }
721
756
  } else {
722
757
  cliSpinner.warn("claude CLI not found on PATH \u2014 register manually:");
723
- for (const [name, entry] of Object.entries(mcpServers)) {
758
+ for (const [name, entry] of Object.entries(httpServers)) {
724
759
  const baseUrl = entry.url.replace(/\/sse$/, "");
725
760
  console.log(
726
761
  chalk.dim(` claude mcp add --transport http --scope user ${name} ${baseUrl}`)
@@ -731,7 +766,7 @@ async function runConnect(config, runtime, targets, host = "localhost") {
731
766
  const configPath = getConfigPath(target);
732
767
  const writeSpinner = ora(`Configuring ${chalk.cyan(target)}...`).start();
733
768
  try {
734
- mergeAndWriteConfig(configPath, mcpServers);
769
+ mergeAndWriteConfig(configPath, httpServers);
735
770
  writeSpinner.succeed(`Configured ${chalk.cyan(target)} \u2014 ${chalk.dim(configPath)}`);
736
771
  configured.push(target);
737
772
  } catch (error) {
@@ -754,7 +789,7 @@ async function runConnect(config, runtime, targets, host = "localhost") {
754
789
  const cursorRulesSpinner = ora("Syncing horus-core rules for Cursor...").start();
755
790
  try {
756
791
  await syncSkillsForCursor(runtime);
757
- cursorRulesSpinner.succeed("horus-core rules synced to ~/.cursor/rules/");
792
+ cursorRulesSpinner.succeed("horus-core rules synced to ~/.cursor/rules/ and skills to ~/.cursor/skills-cursor/");
758
793
  } catch (error) {
759
794
  cursorRulesSpinner.warn("Could not sync Cursor rules (Forge container may not be running)");
760
795
  console.log(chalk.dim(error.message));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkhera30/cli",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "CLI for managing the Horus AI development stack",
5
5
  "type": "module",
6
6
  "bin": {