@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 +98 -44
- package/guides/index.json +1 -1
- package/package.json +1 -1
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
|
|
5
|
-
import
|
|
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
|
-
//
|
|
55
|
+
// reader — Horus 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
|
-
"
|
|
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
|
|
761
|
-
#
|
|
762
|
-
#
|
|
763
|
-
|
|
764
|
-
|
|
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/
|
|
773
|
+
test: ["CMD", "wget", "--spider", "-q", "http://localhost:8400/health"]
|
|
796
774
|
interval: 30s
|
|
797
775
|
timeout: 5s
|
|
798
|
-
start_period:
|
|
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
|
-
|
|
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 ? [
|
|
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 =
|
|
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 =
|
|
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
|
|
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(
|
|
3844
|
+
console.error(chalk14.red(`Error: ${error.message}`));
|
|
3791
3845
|
} else {
|
|
3792
|
-
console.error(
|
|
3846
|
+
console.error(chalk14.red("An unexpected error occurred."));
|
|
3793
3847
|
}
|
|
3794
3848
|
process.exit(1);
|
|
3795
3849
|
}
|
package/guides/index.json
CHANGED