@arkhera30/cli 0.4.0 → 0.5.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/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  // src/index.ts
4
- import { Command as Command13 } from "commander";
5
- import chalk13 from "chalk";
4
+ import { Command as Command14 } from "commander";
5
+ import chalk14 from "chalk";
6
6
 
7
7
  // src/commands/setup.ts
8
8
  import { Command as Command2 } from "commander";
@@ -52,7 +52,7 @@ var DEFAULT_PORTS = {
52
52
  vault_router: 8050,
53
53
  // internal routing layer
54
54
  ui: 8400,
55
- // horus-uiuser-facing web interface
55
+ // readerHorus Reader SPA
56
56
  forge: 8200,
57
57
  typesense: 8108,
58
58
  // Typesense search engine
@@ -68,7 +68,7 @@ var SERVICES = [
68
68
  // replaces 'vault'
69
69
  "vault-mcp",
70
70
  "forge",
71
- "horus-ui",
71
+ "reader",
72
72
  "typesense",
73
73
  "neo4j"
74
74
  ];
@@ -673,11 +673,6 @@ var FORGE_SERVICE = ` # \u2500\u2500 Forge \u2500\u2500\u2500\u2500\u2500\u2500
673
673
  - FORGE_SCAN_PATHS=\${FORGE_SCAN_PATHS:-/data/repos}
674
674
  - FORGE_SESSION_TTL_MS=\${FORGE_SESSION_TTL_MS:-1800000}
675
675
  - GITHUB_TOKEN=\${GITHUB_TOKEN:-}
676
- # Fix git "dubious ownership" on bind-mounted repos (bug 4a32728f).
677
- # Container UID differs from host UID that owns mounted repos.
678
- - GIT_CONFIG_COUNT=1
679
- - GIT_CONFIG_KEY_0=safe.directory
680
- - GIT_CONFIG_VALUE_0=*
681
676
  - TYPESENSE_HOST=typesense
682
677
  - TYPESENSE_PORT=8108
683
678
  - TYPESENSE_API_KEY=\${TYPESENSE_API_KEY:-horus-local-key}
@@ -757,30 +752,13 @@ var TYPESENSE_SERVICE = ` # \u2500\u2500 Typesense \u2500\u2500\u2500\u2500\u25
757
752
  retries: 3
758
753
  start_period: 5s
759
754
  restart: unless-stopped`;
760
- var HORUS_UI_SERVICE = ` # \u2500\u2500 Horus UI \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
761
- # Web interface \u2014 React SPA served by Express proxy on port 8400.
762
- # Proxies /api/anvil, /api/vault, /api/forge to the respective services.
763
- # Stores dashboard configs and preferences in _system/ui/ (not indexed by Anvil).
764
- horus-ui:
765
- image: ghcr.io/arjunkhera/horus/horus-ui:latest
755
+ var READER_SERVICE = ` # \u2500\u2500 Reader \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
756
+ # Horus Reader \u2014 CDN React SPA served by nginx:alpine on port 8400.
757
+ # Pure static files; connects directly to Anvil REST API from the browser.
758
+ reader:
759
+ image: ghcr.io/arjunkhera/horus/reader:latest
766
760
  ports:
767
761
  - "\${UI_PORT:-8400}:8400"
768
- volumes:
769
- - \${HORUS_DATA_PATH}/notes:/data/notes:rw
770
- environment:
771
- - PORT=8400
772
- - HORUS_DATA_PATH=/data/notes
773
- - ANVIL_URL=http://anvil:8100
774
- - VAULT_URL=http://vault-mcp:8300
775
- - FORGE_URL=http://forge:8200
776
- - NODE_ENV=production
777
- depends_on:
778
- anvil:
779
- condition: service_healthy
780
- vault-mcp:
781
- condition: service_healthy
782
- forge:
783
- condition: service_healthy
784
762
  networks:
785
763
  - horus-net
786
764
  restart: unless-stopped
@@ -788,14 +766,14 @@ var HORUS_UI_SERVICE = ` # \u2500\u2500 Horus UI \u2500\u2500\u2500\u2500\u2500
788
766
  deploy:
789
767
  resources:
790
768
  limits:
791
- memory: 256m
792
- reservations:
793
769
  memory: 64m
770
+ reservations:
771
+ memory: 32m
794
772
  healthcheck:
795
- test: ["CMD", "wget", "--spider", "-q", "http://localhost:8400/api/health"]
773
+ test: ["CMD", "wget", "--spider", "-q", "http://localhost:8400/health"]
796
774
  interval: 30s
797
775
  timeout: 5s
798
- start_period: 30s
776
+ start_period: 10s
799
777
  retries: 3`;
800
778
  function generateTestComposeFile(config) {
801
779
  const vaultEntries = Object.entries(config.vaults).sort(([a], [b]) => a.localeCompare(b));
@@ -853,7 +831,7 @@ services:
853
831
  volumes:
854
832
  - "\${TEST_DATA_PATH:-/tmp/horus-test}/typesense-data:/data"
855
833
 
856
- horus-ui:
834
+ reader:
857
835
  ports:
858
836
  - "\${TEST_PORT_UI:-9260}:8400"
859
837
 
@@ -1009,7 +987,7 @@ ${vaultRouterDependsOn}
1009
987
  "",
1010
988
  TYPESENSE_SERVICE,
1011
989
  "",
1012
- ...config.enable_ui !== false ? [HORUS_UI_SERVICE, ""] : [],
990
+ ...config.enable_ui !== false ? [READER_SERVICE, ""] : [],
1013
991
  "# \u2500\u2500 Networks \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500",
1014
992
  "networks:",
1015
993
  " horus-net:",
@@ -1138,12 +1116,14 @@ async function registerWithClaudeCode(mcpServers) {
1138
1116
  async function syncSkills(runtime) {
1139
1117
  const home = homedir3();
1140
1118
  const skillsBase = join2(home, ".claude", "skills");
1141
- const skills = ["horus-anvil", "horus-vault", "horus-forge"];
1119
+ const skills = ["horus-anvil", "horus-vault", "horus-forge", "horus-context", "capture", "triage"];
1142
1120
  const forgeContainer = "horus-forge-1";
1121
+ const homeResult = await runtime.exec(forgeContainer, "sh", "-c", "echo $HOME");
1122
+ const containerHome = homeResult.stdout.trim();
1143
1123
  for (const skill of skills) {
1144
1124
  const destDir = join2(skillsBase, skill);
1145
1125
  mkdirSync2(destDir, { recursive: true });
1146
- const src = `/home/forge/.claude/skills/${skill}/SKILL.md`;
1126
+ const src = `${containerHome}/.claude/skills/${skill}/SKILL.md`;
1147
1127
  const dest = join2(destDir, "SKILL.md");
1148
1128
  const result = await runtime.exec(forgeContainer, "cat", src);
1149
1129
  if (result.exitCode === 0 && result.stdout.trim()) {
@@ -1155,11 +1135,13 @@ async function syncSkillsForCursor(runtime) {
1155
1135
  const home = homedir3();
1156
1136
  const rulesDir = join2(home, ".cursor", "rules");
1157
1137
  const skillsBase = join2(home, ".cursor", "skills-cursor");
1158
- const skills = ["horus-anvil", "horus-vault", "horus-forge"];
1138
+ const skills = ["horus-anvil", "horus-vault", "horus-forge", "horus-context", "capture", "triage"];
1159
1139
  const forgeContainer = "horus-forge-1";
1160
1140
  mkdirSync2(rulesDir, { recursive: true });
1141
+ const homeResult = await runtime.exec(forgeContainer, "sh", "-c", "echo $HOME");
1142
+ const containerHome = homeResult.stdout.trim();
1161
1143
  for (const skill of skills) {
1162
- const src = `/home/forge/.claude/skills/${skill}/SKILL.md`;
1144
+ const src = `${containerHome}/.claude/skills/${skill}/SKILL.md`;
1163
1145
  const result = await runtime.exec(forgeContainer, "cat", src);
1164
1146
  if (result.exitCode === 0 && result.stdout.trim()) {
1165
1147
  const ruleDest = join2(rulesDir, `${skill}.mdc`);
@@ -3764,8 +3746,79 @@ var guideCommand = new Command12("guide").description("Print a bundled Horus gui
3764
3746
  printGuideBody2(guidesDir, match.file);
3765
3747
  });
3766
3748
 
3749
+ // src/commands/repo.ts
3750
+ import { Command as Command13 } from "commander";
3751
+ import chalk13 from "chalk";
3752
+ import ora9 from "ora";
3753
+ var repoCommand = new Command13("repo").description("Manage the Forge repository index");
3754
+ repoCommand.command("rindex").alias("scan").description("Trigger a full repository index rescan via Forge").action(async () => {
3755
+ if (!configExists()) {
3756
+ console.log(chalk13.red("Horus is not set up yet."));
3757
+ console.log(chalk13.dim("Run `horus setup` first."));
3758
+ process.exit(1);
3759
+ }
3760
+ const config = loadConfig();
3761
+ const forgePort = config.ports.forge ?? 8200;
3762
+ const forgeUrl = `http://localhost:${forgePort}/mcp`;
3763
+ const spinner = ora9("Scanning repositories...").start();
3764
+ let body;
3765
+ try {
3766
+ const res = await fetch(forgeUrl, {
3767
+ method: "POST",
3768
+ headers: { "Content-Type": "application/json" },
3769
+ body: JSON.stringify({
3770
+ jsonrpc: "2.0",
3771
+ id: 1,
3772
+ method: "tools/call",
3773
+ params: { name: "forge_repo_scan", arguments: {} }
3774
+ })
3775
+ });
3776
+ body = await res.text();
3777
+ if (!res.ok) {
3778
+ spinner.fail(`Forge returned HTTP ${res.status}`);
3779
+ console.error(chalk13.red(body));
3780
+ process.exit(1);
3781
+ }
3782
+ } catch (err) {
3783
+ spinner.fail("Could not reach Forge");
3784
+ console.error(chalk13.red(`Is Horus running? (horus up)`));
3785
+ console.error(chalk13.dim(err.message));
3786
+ process.exit(1);
3787
+ }
3788
+ let parsed;
3789
+ try {
3790
+ parsed = JSON.parse(body);
3791
+ } catch {
3792
+ spinner.fail("Unexpected response from Forge");
3793
+ console.error(body);
3794
+ process.exit(1);
3795
+ }
3796
+ if (parsed.error) {
3797
+ spinner.fail("Scan failed");
3798
+ console.error(chalk13.red(parsed.error.message ?? JSON.stringify(parsed.error)));
3799
+ process.exit(1);
3800
+ }
3801
+ let result = {};
3802
+ try {
3803
+ const text = parsed.result?.content?.[0]?.text ?? "{}";
3804
+ result = JSON.parse(text);
3805
+ } catch {
3806
+ }
3807
+ spinner.succeed("Repository scan complete");
3808
+ console.log("");
3809
+ console.log(` ${chalk13.bold("Scan paths:")} ${(result.scanPaths ?? []).length}`);
3810
+ console.log(` ${chalk13.bold("Repos found:")} ${result.reposFound ?? 0}`);
3811
+ if (result.repos && result.repos.length > 0) {
3812
+ console.log("");
3813
+ for (const repo of result.repos) {
3814
+ console.log(` ${chalk13.green("\u2713")} ${chalk13.bold(repo.name)} ${chalk13.dim(repo.localPath)}`);
3815
+ }
3816
+ }
3817
+ console.log("");
3818
+ });
3819
+
3767
3820
  // src/index.ts
3768
- var program = new Command13();
3821
+ var program = new Command14();
3769
3822
  program.name("horus").description("CLI for managing the Horus Docker Compose stack").version(CLI_VERSION);
3770
3823
  program.addCommand(setupCommand);
3771
3824
  program.addCommand(upCommand);
@@ -3779,6 +3832,7 @@ program.addCommand(backupCommand);
3779
3832
  program.addCommand(testEnvCommand);
3780
3833
  program.addCommand(helpCommand);
3781
3834
  program.addCommand(guideCommand);
3835
+ program.addCommand(repoCommand);
3782
3836
  program.exitOverride();
3783
3837
  try {
3784
3838
  await program.parseAsync(process.argv);
@@ -3787,9 +3841,9 @@ try {
3787
3841
  process.exit(0);
3788
3842
  }
3789
3843
  if (error instanceof Error) {
3790
- console.error(chalk13.red(`Error: ${error.message}`));
3844
+ console.error(chalk14.red(`Error: ${error.message}`));
3791
3845
  } else {
3792
- console.error(chalk13.red("An unexpected error occurred."));
3846
+ console.error(chalk14.red("An unexpected error occurred."));
3793
3847
  }
3794
3848
  process.exit(1);
3795
3849
  }
package/guides/index.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "schema_version": 1,
3
- "built_at": "2026-04-11T11:27:07.553Z",
3
+ "built_at": "2026-04-30T03:37:49.113Z",
4
4
  "guide_count": 5,
5
5
  "guides": [
6
6
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arkhera30/cli",
3
- "version": "0.4.0",
3
+ "version": "0.5.1",
4
4
  "description": "CLI for managing the Horus AI development stack",
5
5
  "type": "module",
6
6
  "bin": {