blun-king-cli 1.0.0 → 1.1.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.
Files changed (2) hide show
  1. package/bin/blun.js +309 -0
  2. package/package.json +1 -1
package/bin/blun.js CHANGED
@@ -185,6 +185,16 @@ async function handleCommand(input) {
185
185
  console.log(" /set telegram Configure Telegram bridge");
186
186
  console.log(" /set verbose Toggle verbose logging");
187
187
  console.log("");
188
+ console.log(C.yellow + " DEV" + C.reset);
189
+ console.log(" /sh <cmd> Run shell command");
190
+ console.log(" /git <cmd> Git commands (status, log, diff, add, commit, push, pull, branch, checkout, clone, init)");
191
+ console.log(" /ssh add/del/list Connect & manage SSH hosts");
192
+ console.log(" /ssh <host> <cmd> Run command on remote host");
193
+ console.log(" /deploy <method> Deploy: git, ssh, sync, pm2");
194
+ console.log(" /read <file> Read a file");
195
+ console.log(" /write <file> Write/create a file");
196
+ console.log(" /init Init project (AGENT.md, .gitignore, git init)");
197
+ console.log("");
188
198
  console.log(C.yellow + " SYSTEM" + C.reset);
189
199
  console.log(" /status Runtime status");
190
200
  console.log(" /versions Prompt versions");
@@ -272,6 +282,34 @@ async function handleCommand(input) {
272
282
  cmdFiles();
273
283
  break;
274
284
 
285
+ case "/sh":
286
+ cmdShell(args);
287
+ break;
288
+
289
+ case "/git":
290
+ cmdGit(args);
291
+ break;
292
+
293
+ case "/ssh":
294
+ cmdSsh(args);
295
+ break;
296
+
297
+ case "/deploy":
298
+ cmdDeploy(args);
299
+ break;
300
+
301
+ case "/read":
302
+ cmdRead(args);
303
+ break;
304
+
305
+ case "/write":
306
+ cmdWrite(args);
307
+ break;
308
+
309
+ case "/init":
310
+ cmdInit();
311
+ break;
312
+
275
313
  case "/exit":
276
314
  case "/quit":
277
315
  case "/q":
@@ -733,6 +771,277 @@ function cmdFiles() {
733
771
  } catch(e) { printError(e.message); }
734
772
  }
735
773
 
774
+ // ── Shell Execution ──
775
+ function cmdShell(command) {
776
+ if (!command) { printError("Usage: /sh <command>"); return; }
777
+ try {
778
+ console.log(C.dim + "$ " + command + C.reset);
779
+ var output = execSync(command, { cwd: config.workdir, encoding: "utf8", timeout: 30000, stdio: ["pipe", "pipe", "pipe"] });
780
+ console.log(output);
781
+ } catch(e) {
782
+ if (e.stdout) console.log(e.stdout);
783
+ if (e.stderr) console.log(C.red + e.stderr + C.reset);
784
+ printError("Exit code: " + (e.status || "unknown"));
785
+ }
786
+ }
787
+
788
+ // ── Git Commands ──
789
+ function cmdGit(args) {
790
+ if (!args) {
791
+ // Show git status
792
+ cmdShell("git status --short");
793
+ return;
794
+ }
795
+ var sub = args.split(/\s+/)[0];
796
+ var rest = args.slice(sub.length).trim();
797
+
798
+ switch(sub) {
799
+ case "status": cmdShell("git status"); break;
800
+ case "log": cmdShell("git log --oneline -10"); break;
801
+ case "diff": cmdShell("git diff --stat"); break;
802
+ case "add":
803
+ cmdShell("git add " + (rest || "."));
804
+ printSuccess("Files staged.");
805
+ break;
806
+ case "commit":
807
+ if (!rest) { printError("Usage: /git commit <message>"); return; }
808
+ cmdShell('git commit -m "' + rest.replace(/"/g, '\\"') + '"');
809
+ break;
810
+ case "push":
811
+ var remote = rest || "origin";
812
+ try {
813
+ var branch = execSync("git branch --show-current", { cwd: config.workdir, encoding: "utf8" }).trim();
814
+ printInfo("Pushing " + branch + " to " + remote + "...");
815
+ cmdShell("git push " + remote + " " + branch);
816
+ printSuccess("Pushed!");
817
+ } catch(e) { printError("Push failed: " + e.message); }
818
+ break;
819
+ case "pull":
820
+ cmdShell("git pull " + (rest || ""));
821
+ break;
822
+ case "branch":
823
+ cmdShell("git branch " + (rest || "-a"));
824
+ break;
825
+ case "checkout":
826
+ if (!rest) { printError("Usage: /git checkout <branch>"); return; }
827
+ cmdShell("git checkout " + rest);
828
+ break;
829
+ case "clone":
830
+ if (!rest) { printError("Usage: /git clone <url>"); return; }
831
+ cmdShell("git clone " + rest);
832
+ break;
833
+ case "init":
834
+ cmdShell("git init");
835
+ break;
836
+ case "remote":
837
+ cmdShell("git remote -v");
838
+ break;
839
+ default:
840
+ cmdShell("git " + args);
841
+ }
842
+ }
843
+
844
+ // ── SSH Commands ──
845
+ function cmdSsh(args) {
846
+ if (!args) {
847
+ // Show saved connections
848
+ var sshFile = path.join(CONFIG_DIR, "ssh.json");
849
+ if (fs.existsSync(sshFile)) {
850
+ var connections = JSON.parse(fs.readFileSync(sshFile, "utf8"));
851
+ console.log("");
852
+ console.log(C.bold + "SSH Connections:" + C.reset);
853
+ for (var name in connections) {
854
+ var c = connections[name];
855
+ console.log(" " + C.yellow + name + C.reset + " — " + c.user + "@" + c.host + (c.port !== 22 ? ":" + c.port : ""));
856
+ }
857
+ console.log("");
858
+ console.log(C.dim + " /ssh <name> <command> — Run command" + C.reset);
859
+ console.log(C.dim + " /ssh add <name> <user@host> — Add connection" + C.reset);
860
+ console.log(C.dim + " /ssh del <name> — Remove connection" + C.reset);
861
+ } else {
862
+ printInfo("No SSH connections saved. Use /ssh add <name> <user@host>");
863
+ }
864
+ return;
865
+ }
866
+
867
+ var parts = args.split(/\s+/);
868
+ var sshFile = path.join(CONFIG_DIR, "ssh.json");
869
+ var connections = {};
870
+ if (fs.existsSync(sshFile)) {
871
+ try { connections = JSON.parse(fs.readFileSync(sshFile, "utf8")); } catch(e) {}
872
+ }
873
+
874
+ if (parts[0] === "add" && parts[1] && parts[2]) {
875
+ var match = parts[2].match(/^([^@]+)@([^:]+)(?::(\d+))?$/);
876
+ if (!match) { printError("Format: user@host or user@host:port"); return; }
877
+ connections[parts[1]] = { user: match[1], host: match[2], port: parseInt(match[3] || "22"), key: parts[3] || null };
878
+ fs.writeFileSync(sshFile, JSON.stringify(connections, null, 2));
879
+ printSuccess("SSH connection '" + parts[1] + "' saved.");
880
+ return;
881
+ }
882
+
883
+ if (parts[0] === "del" && parts[1]) {
884
+ delete connections[parts[1]];
885
+ fs.writeFileSync(sshFile, JSON.stringify(connections, null, 2));
886
+ printSuccess("SSH connection '" + parts[1] + "' removed.");
887
+ return;
888
+ }
889
+
890
+ // Execute command on saved connection
891
+ var connName = parts[0];
892
+ var remoteCmd = parts.slice(1).join(" ");
893
+ if (!connections[connName]) { printError("Unknown connection: " + connName + ". Use /ssh to list."); return; }
894
+ if (!remoteCmd) { printError("Usage: /ssh " + connName + " <command>"); return; }
895
+
896
+ var conn = connections[connName];
897
+ var sshCmd = "ssh";
898
+ if (conn.key) sshCmd += " -i " + conn.key;
899
+ sshCmd += " -o ConnectTimeout=10 -o StrictHostKeyChecking=no";
900
+ sshCmd += " -p " + conn.port;
901
+ sshCmd += " " + conn.user + "@" + conn.host;
902
+ sshCmd += ' "' + remoteCmd.replace(/"/g, '\\"') + '"';
903
+
904
+ printInfo("SSH " + connName + ": " + remoteCmd);
905
+ cmdShell(sshCmd);
906
+ }
907
+
908
+ // ── Deploy Command ──
909
+ async function cmdDeploy(args) {
910
+ if (!args) {
911
+ console.log("");
912
+ console.log(C.bold + "Deploy Options:" + C.reset);
913
+ console.log(" /deploy git — git add + commit + push");
914
+ console.log(" /deploy ssh <name> <cmd> — Run deploy command via SSH");
915
+ console.log(" /deploy sync <name> — rsync workdir to server");
916
+ console.log(" /deploy pm2 <name> <app> — pm2 restart on server");
917
+ console.log("");
918
+ return;
919
+ }
920
+
921
+ var parts = args.split(/\s+/);
922
+ var action = parts[0];
923
+
924
+ if (action === "git") {
925
+ printInfo("Deploying via git...");
926
+ try {
927
+ var status = execSync("git status --porcelain", { cwd: config.workdir, encoding: "utf8" }).trim();
928
+ if (status) {
929
+ cmdShell("git add .");
930
+ var msg = parts.slice(1).join(" ") || "deploy: " + new Date().toISOString().slice(0, 16);
931
+ cmdShell('git commit -m "' + msg + '"');
932
+ }
933
+ var branch = execSync("git branch --show-current", { cwd: config.workdir, encoding: "utf8" }).trim();
934
+ cmdShell("git push origin " + branch);
935
+ printSuccess("Deployed via git push!");
936
+ } catch(e) { printError("Deploy failed: " + e.message); }
937
+
938
+ } else if (action === "ssh" && parts[1]) {
939
+ cmdSsh(parts.slice(1).join(" "));
940
+
941
+ } else if (action === "sync" && parts[1]) {
942
+ var sshFile = path.join(CONFIG_DIR, "ssh.json");
943
+ if (!fs.existsSync(sshFile)) { printError("No SSH connections. Use /ssh add first."); return; }
944
+ var connections = JSON.parse(fs.readFileSync(sshFile, "utf8"));
945
+ var conn = connections[parts[1]];
946
+ if (!conn) { printError("Unknown connection: " + parts[1]); return; }
947
+ var dest = parts[2] || "/root/" + path.basename(config.workdir);
948
+ var rsyncCmd = "rsync -avz --exclude node_modules --exclude .git";
949
+ if (conn.key) rsyncCmd += " -e 'ssh -i " + conn.key + " -p " + conn.port + "'";
950
+ rsyncCmd += " " + config.workdir + "/ " + conn.user + "@" + conn.host + ":" + dest + "/";
951
+ printInfo("Syncing to " + parts[1] + ":" + dest);
952
+ cmdShell(rsyncCmd);
953
+
954
+ } else if (action === "pm2" && parts[1] && parts[2]) {
955
+ cmdSsh(parts[1] + " pm2 restart " + parts[2]);
956
+
957
+ } else {
958
+ printError("Unknown deploy action. Use /deploy for help.");
959
+ }
960
+ }
961
+
962
+ // ── Read/Write Files ──
963
+ function cmdRead(filePath) {
964
+ if (!filePath) { printError("Usage: /read <file>"); return; }
965
+ var full = path.resolve(config.workdir, filePath);
966
+ if (!fs.existsSync(full)) { printError("File not found: " + full); return; }
967
+ var content = fs.readFileSync(full, "utf8");
968
+ console.log("");
969
+ console.log(C.bold + "File: " + full + C.reset);
970
+ console.log(C.dim + "─".repeat(50) + C.reset);
971
+ var lines = content.split("\n");
972
+ for (var i = 0; i < lines.length; i++) {
973
+ console.log(C.dim + String(i + 1).padStart(4) + " │ " + C.reset + lines[i]);
974
+ }
975
+ console.log("");
976
+ }
977
+
978
+ function cmdWrite(args) {
979
+ if (!args) { printError("Usage: /write <file> <content> or /write <file> (then type content, end with EOF)"); return; }
980
+ var parts = args.split(/\s+/);
981
+ var filePath = parts[0];
982
+ var content = parts.slice(1).join(" ");
983
+ var full = path.resolve(config.workdir, filePath);
984
+
985
+ if (content) {
986
+ fs.writeFileSync(full, content);
987
+ printSuccess("Written: " + full + " (" + content.length + " bytes)");
988
+ } else {
989
+ printInfo("Type content (end with line 'EOF'):");
990
+ // This will be handled differently in interactive mode
991
+ printError("For multi-line write, use: /generate instead");
992
+ }
993
+ }
994
+
995
+ // ── Project Init ──
996
+ function cmdInit(args) {
997
+ var name = args || path.basename(config.workdir);
998
+ var projectDir = args ? path.join(config.workdir, args) : config.workdir;
999
+
1000
+ if (args && !fs.existsSync(projectDir)) {
1001
+ fs.mkdirSync(projectDir, { recursive: true });
1002
+ }
1003
+
1004
+ // Create project memory file
1005
+ var memFile = path.join(projectDir, "AGENT.md");
1006
+ if (!fs.existsSync(memFile)) {
1007
+ var agentContent = "# " + name + "\n\n" +
1008
+ "## Projekt\n" +
1009
+ "Beschreibung: \n\n" +
1010
+ "## Stack\n" +
1011
+ "- \n\n" +
1012
+ "## Regeln\n" +
1013
+ "- \n\n" +
1014
+ "## Status\n" +
1015
+ "Erstellt: " + new Date().toISOString().split("T")[0] + "\n";
1016
+ fs.writeFileSync(memFile, agentContent);
1017
+ printSuccess("AGENT.md erstellt in " + projectDir);
1018
+ } else {
1019
+ printInfo("AGENT.md existiert bereits.");
1020
+ }
1021
+
1022
+ // Git init if not already
1023
+ try {
1024
+ execSync("git rev-parse --git-dir", { cwd: projectDir, stdio: "pipe" });
1025
+ printInfo("Git repo already initialized.");
1026
+ } catch(e) {
1027
+ execSync("git init", { cwd: projectDir, stdio: "pipe" });
1028
+ printSuccess("Git repo initialized.");
1029
+ }
1030
+
1031
+ // Create .gitignore if missing
1032
+ var gitignore = path.join(projectDir, ".gitignore");
1033
+ if (!fs.existsSync(gitignore)) {
1034
+ fs.writeFileSync(gitignore, "node_modules/\n.env\n*.log\n.blun/\n");
1035
+ printSuccess(".gitignore erstellt.");
1036
+ }
1037
+
1038
+ if (args) {
1039
+ config.workdir = projectDir;
1040
+ saveConfig(config);
1041
+ printSuccess("Workdir set to: " + projectDir);
1042
+ }
1043
+ }
1044
+
736
1045
  // ── Main Loop ──
737
1046
  async function main() {
738
1047
  // Handle CLI args
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "blun-king-cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "BLUN King CLI — Premium KI Console",
5
5
  "bin": {
6
6
  "blun": "./bin/blun.js"