skillo 0.1.5 → 0.2.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/cli.js +651 -123
- package/dist/cli.js.map +1 -1
- package/dist/daemon-runner.js +338 -22
- package/dist/daemon-runner.js.map +1 -1
- package/dist/tray-62I5KIFE.js +278 -0
- package/dist/tray-62I5KIFE.js.map +1 -0
- package/package.json +3 -1
- package/scripts/postinstall.mjs +57 -0
package/dist/cli.js
CHANGED
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
} from "./chunk-WJKZWKER.js";
|
|
23
23
|
|
|
24
24
|
// src/cli.ts
|
|
25
|
-
import { Command as
|
|
25
|
+
import { Command as Command14 } from "commander";
|
|
26
26
|
|
|
27
27
|
// src/commands/init.ts
|
|
28
28
|
import { existsSync as existsSync2 } from "fs";
|
|
@@ -593,15 +593,444 @@ var initCommand = new Command("init").description("Initialize Skillo configurati
|
|
|
593
593
|
});
|
|
594
594
|
|
|
595
595
|
// src/commands/status.ts
|
|
596
|
-
import { existsSync as
|
|
596
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
597
597
|
import { Command as Command2 } from "commander";
|
|
598
|
+
|
|
599
|
+
// src/utils/os-service.ts
|
|
600
|
+
import { existsSync as existsSync3, writeFileSync as writeFileSync2, unlinkSync, mkdirSync, readdirSync } from "fs";
|
|
601
|
+
import { join } from "path";
|
|
602
|
+
import { homedir, platform } from "os";
|
|
603
|
+
import { execSync } from "child_process";
|
|
604
|
+
var DAEMON_LABEL = "one.skillo.daemon";
|
|
605
|
+
var TRAY_LABEL = "one.skillo.tray";
|
|
606
|
+
var WIN_REG_KEY = "HKCU\\Software\\Microsoft\\Windows\\CurrentVersion\\Run";
|
|
607
|
+
var WIN_DAEMON_VALUE = "SkilloDaemon";
|
|
608
|
+
var WIN_TRAY_VALUE = "SkilloTray";
|
|
609
|
+
var isWin = platform() === "win32";
|
|
610
|
+
var pathSep = isWin ? ";" : ":";
|
|
611
|
+
function getLaunchAgentsDir() {
|
|
612
|
+
return join(homedir(), "Library", "LaunchAgents");
|
|
613
|
+
}
|
|
614
|
+
function getSystemdUserDir() {
|
|
615
|
+
return join(homedir(), ".config", "systemd", "user");
|
|
616
|
+
}
|
|
617
|
+
function getDaemonPlistPath() {
|
|
618
|
+
return join(getLaunchAgentsDir(), `${DAEMON_LABEL}.plist`);
|
|
619
|
+
}
|
|
620
|
+
function getTrayPlistPath() {
|
|
621
|
+
return join(getLaunchAgentsDir(), `${TRAY_LABEL}.plist`);
|
|
622
|
+
}
|
|
623
|
+
function getDaemonServicePath() {
|
|
624
|
+
return join(getSystemdUserDir(), "skillo-daemon.service");
|
|
625
|
+
}
|
|
626
|
+
function getTrayServicePath() {
|
|
627
|
+
return join(getSystemdUserDir(), "skillo-tray.service");
|
|
628
|
+
}
|
|
629
|
+
function getSkilloDataDir() {
|
|
630
|
+
return join(homedir(), ".skillo");
|
|
631
|
+
}
|
|
632
|
+
function getWinDaemonVbsPath() {
|
|
633
|
+
return join(getSkilloDataDir(), "skillo-daemon.vbs");
|
|
634
|
+
}
|
|
635
|
+
function findSkilloBin() {
|
|
636
|
+
try {
|
|
637
|
+
const whichCmd = isWin ? "where skillo" : "which skillo";
|
|
638
|
+
const result = execSync(whichCmd, { encoding: "utf-8" }).trim();
|
|
639
|
+
const firstLine = result.split(/\r?\n/)[0].trim();
|
|
640
|
+
if (firstLine) return firstLine;
|
|
641
|
+
} catch {
|
|
642
|
+
}
|
|
643
|
+
if (isWin) {
|
|
644
|
+
const appData = process.env.APPDATA || join(homedir(), "AppData", "Roaming");
|
|
645
|
+
const candidates = [
|
|
646
|
+
join(appData, "npm", "skillo.cmd"),
|
|
647
|
+
join(homedir(), "AppData", "Local", "fnm_multishells")
|
|
648
|
+
];
|
|
649
|
+
for (const c of candidates) {
|
|
650
|
+
if (existsSync3(c)) return c;
|
|
651
|
+
}
|
|
652
|
+
} else {
|
|
653
|
+
const candidates = [
|
|
654
|
+
"/usr/local/bin/skillo",
|
|
655
|
+
"/usr/bin/skillo"
|
|
656
|
+
];
|
|
657
|
+
for (const c of candidates) {
|
|
658
|
+
if (existsSync3(c)) return c;
|
|
659
|
+
}
|
|
660
|
+
}
|
|
661
|
+
return "skillo";
|
|
662
|
+
}
|
|
663
|
+
function buildPath() {
|
|
664
|
+
const paths = /* @__PURE__ */ new Set();
|
|
665
|
+
const home = homedir();
|
|
666
|
+
(process.env.PATH || "").split(pathSep).forEach((p) => paths.add(p));
|
|
667
|
+
if (isWin) {
|
|
668
|
+
const appData = process.env.APPDATA || join(home, "AppData", "Roaming");
|
|
669
|
+
paths.add(join(appData, "npm"));
|
|
670
|
+
paths.add(join(home, "AppData", "Local", "Programs", "nodejs"));
|
|
671
|
+
const nvmHome = process.env.NVM_HOME;
|
|
672
|
+
if (nvmHome) paths.add(nvmHome);
|
|
673
|
+
const nvmSymlink = process.env.NVM_SYMLINK;
|
|
674
|
+
if (nvmSymlink) paths.add(nvmSymlink);
|
|
675
|
+
const fnmDir = join(home, ".fnm");
|
|
676
|
+
if (existsSync3(fnmDir)) paths.add(fnmDir);
|
|
677
|
+
} else {
|
|
678
|
+
const nvmDir = process.env.NVM_DIR || join(home, ".nvm");
|
|
679
|
+
if (existsSync3(nvmDir)) {
|
|
680
|
+
try {
|
|
681
|
+
const defaultAlias = join(nvmDir, "alias", "default");
|
|
682
|
+
if (existsSync3(defaultAlias)) {
|
|
683
|
+
const versionsDir = join(nvmDir, "versions", "node");
|
|
684
|
+
if (existsSync3(versionsDir)) {
|
|
685
|
+
const versions = readdirSync(versionsDir);
|
|
686
|
+
for (const v of versions) {
|
|
687
|
+
paths.add(join(versionsDir, v, "bin"));
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
} catch {
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
paths.add(join(home, ".fnm", "current", "bin"));
|
|
695
|
+
paths.add("/opt/homebrew/bin");
|
|
696
|
+
paths.add("/usr/local/bin");
|
|
697
|
+
paths.add("/usr/bin");
|
|
698
|
+
paths.add("/bin");
|
|
699
|
+
paths.add(join(home, ".npm-global", "bin"));
|
|
700
|
+
paths.add(join(home, ".local", "bin"));
|
|
701
|
+
}
|
|
702
|
+
return [...paths].filter(Boolean).join(pathSep);
|
|
703
|
+
}
|
|
704
|
+
function generateDaemonPlist(skilloBin, envPath) {
|
|
705
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
706
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
707
|
+
<plist version="1.0">
|
|
708
|
+
<dict>
|
|
709
|
+
<key>Label</key>
|
|
710
|
+
<string>${DAEMON_LABEL}</string>
|
|
711
|
+
<key>ProgramArguments</key>
|
|
712
|
+
<array>
|
|
713
|
+
<string>${skilloBin}</string>
|
|
714
|
+
<string>start</string>
|
|
715
|
+
<string>--foreground</string>
|
|
716
|
+
</array>
|
|
717
|
+
<key>RunAtLoad</key>
|
|
718
|
+
<true/>
|
|
719
|
+
<key>KeepAlive</key>
|
|
720
|
+
<dict>
|
|
721
|
+
<key>SuccessfulExit</key>
|
|
722
|
+
<false/>
|
|
723
|
+
</dict>
|
|
724
|
+
<key>ThrottleInterval</key>
|
|
725
|
+
<integer>10</integer>
|
|
726
|
+
<key>EnvironmentVariables</key>
|
|
727
|
+
<dict>
|
|
728
|
+
<key>PATH</key>
|
|
729
|
+
<string>${envPath}</string>
|
|
730
|
+
<key>HOME</key>
|
|
731
|
+
<string>${homedir()}</string>
|
|
732
|
+
</dict>
|
|
733
|
+
<key>StandardOutPath</key>
|
|
734
|
+
<string>${join(homedir(), ".skillo", "launchd-stdout.log")}</string>
|
|
735
|
+
<key>StandardErrorPath</key>
|
|
736
|
+
<string>${join(homedir(), ".skillo", "launchd-stderr.log")}</string>
|
|
737
|
+
</dict>
|
|
738
|
+
</plist>`;
|
|
739
|
+
}
|
|
740
|
+
function generateTrayPlist(skilloBin, envPath) {
|
|
741
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
742
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
743
|
+
<plist version="1.0">
|
|
744
|
+
<dict>
|
|
745
|
+
<key>Label</key>
|
|
746
|
+
<string>${TRAY_LABEL}</string>
|
|
747
|
+
<key>ProgramArguments</key>
|
|
748
|
+
<array>
|
|
749
|
+
<string>${skilloBin}</string>
|
|
750
|
+
<string>tray</string>
|
|
751
|
+
</array>
|
|
752
|
+
<key>RunAtLoad</key>
|
|
753
|
+
<true/>
|
|
754
|
+
<key>LimitLoadToSessionType</key>
|
|
755
|
+
<string>Aqua</string>
|
|
756
|
+
<key>KeepAlive</key>
|
|
757
|
+
<dict>
|
|
758
|
+
<key>SuccessfulExit</key>
|
|
759
|
+
<false/>
|
|
760
|
+
</dict>
|
|
761
|
+
<key>ThrottleInterval</key>
|
|
762
|
+
<integer>10</integer>
|
|
763
|
+
<key>EnvironmentVariables</key>
|
|
764
|
+
<dict>
|
|
765
|
+
<key>PATH</key>
|
|
766
|
+
<string>${envPath}</string>
|
|
767
|
+
<key>HOME</key>
|
|
768
|
+
<string>${homedir()}</string>
|
|
769
|
+
</dict>
|
|
770
|
+
<key>StandardOutPath</key>
|
|
771
|
+
<string>${join(homedir(), ".skillo", "launchd-tray-stdout.log")}</string>
|
|
772
|
+
<key>StandardErrorPath</key>
|
|
773
|
+
<string>${join(homedir(), ".skillo", "launchd-tray-stderr.log")}</string>
|
|
774
|
+
</dict>
|
|
775
|
+
</plist>`;
|
|
776
|
+
}
|
|
777
|
+
function generateDaemonUnit(skilloBin, envPath) {
|
|
778
|
+
return `[Unit]
|
|
779
|
+
Description=Skillo Daemon
|
|
780
|
+
After=network.target
|
|
781
|
+
|
|
782
|
+
[Service]
|
|
783
|
+
Type=simple
|
|
784
|
+
ExecStart=${skilloBin} start --foreground
|
|
785
|
+
Restart=on-failure
|
|
786
|
+
RestartSec=10
|
|
787
|
+
Environment=PATH=${envPath}
|
|
788
|
+
Environment=HOME=${homedir()}
|
|
789
|
+
|
|
790
|
+
[Install]
|
|
791
|
+
WantedBy=default.target
|
|
792
|
+
`;
|
|
793
|
+
}
|
|
794
|
+
function generateTrayUnit(skilloBin, envPath) {
|
|
795
|
+
return `[Unit]
|
|
796
|
+
Description=Skillo Tray Icon
|
|
797
|
+
After=graphical-session.target
|
|
798
|
+
PartOf=graphical-session.target
|
|
799
|
+
|
|
800
|
+
[Service]
|
|
801
|
+
Type=simple
|
|
802
|
+
ExecStart=${skilloBin} tray
|
|
803
|
+
Restart=on-failure
|
|
804
|
+
RestartSec=10
|
|
805
|
+
Environment=PATH=${envPath}
|
|
806
|
+
Environment=HOME=${homedir()}
|
|
807
|
+
Environment=DISPLAY=:0
|
|
808
|
+
|
|
809
|
+
[Install]
|
|
810
|
+
WantedBy=graphical-session.target
|
|
811
|
+
`;
|
|
812
|
+
}
|
|
813
|
+
function generateDaemonVbs(skilloBin) {
|
|
814
|
+
const escaped = skilloBin.replace(/\\/g, "\\\\");
|
|
815
|
+
return `' Skillo Daemon \u2014 hidden launcher\r
|
|
816
|
+
Set WshShell = CreateObject("WScript.Shell")\r
|
|
817
|
+
WshShell.Run """${escaped}"" start --foreground", 0, False\r
|
|
818
|
+
`;
|
|
819
|
+
}
|
|
820
|
+
function winRegAdd(valueName, data) {
|
|
821
|
+
execSync(
|
|
822
|
+
`reg add "${WIN_REG_KEY}" /v "${valueName}" /t REG_SZ /d "${data}" /f`,
|
|
823
|
+
{ stdio: "ignore" }
|
|
824
|
+
);
|
|
825
|
+
}
|
|
826
|
+
function winRegDelete(valueName) {
|
|
827
|
+
try {
|
|
828
|
+
execSync(
|
|
829
|
+
`reg delete "${WIN_REG_KEY}" /v "${valueName}" /f`,
|
|
830
|
+
{ stdio: "ignore" }
|
|
831
|
+
);
|
|
832
|
+
} catch {
|
|
833
|
+
}
|
|
834
|
+
}
|
|
835
|
+
function winRegExists(valueName) {
|
|
836
|
+
try {
|
|
837
|
+
execSync(`reg query "${WIN_REG_KEY}" /v "${valueName}"`, { stdio: "ignore" });
|
|
838
|
+
return true;
|
|
839
|
+
} catch {
|
|
840
|
+
return false;
|
|
841
|
+
}
|
|
842
|
+
}
|
|
843
|
+
async function installService(options) {
|
|
844
|
+
const os2 = platform();
|
|
845
|
+
const skilloBin = findSkilloBin();
|
|
846
|
+
const envPath = buildPath();
|
|
847
|
+
const includeTray = options?.includeTray ?? true;
|
|
848
|
+
try {
|
|
849
|
+
if (os2 === "darwin") {
|
|
850
|
+
const agentsDir = getLaunchAgentsDir();
|
|
851
|
+
if (!existsSync3(agentsDir)) mkdirSync(agentsDir, { recursive: true });
|
|
852
|
+
const daemonPlist = getDaemonPlistPath();
|
|
853
|
+
writeFileSync2(daemonPlist, generateDaemonPlist(skilloBin, envPath), "utf-8");
|
|
854
|
+
try {
|
|
855
|
+
execSync(`launchctl unload "${daemonPlist}" 2>/dev/null`);
|
|
856
|
+
} catch {
|
|
857
|
+
}
|
|
858
|
+
execSync(`launchctl load "${daemonPlist}"`);
|
|
859
|
+
if (includeTray) {
|
|
860
|
+
const trayPlist = getTrayPlistPath();
|
|
861
|
+
writeFileSync2(trayPlist, generateTrayPlist(skilloBin, envPath), "utf-8");
|
|
862
|
+
try {
|
|
863
|
+
execSync(`launchctl unload "${trayPlist}" 2>/dev/null`);
|
|
864
|
+
} catch {
|
|
865
|
+
}
|
|
866
|
+
execSync(`launchctl load "${trayPlist}"`);
|
|
867
|
+
}
|
|
868
|
+
return { success: true };
|
|
869
|
+
} else if (os2 === "linux") {
|
|
870
|
+
const systemdDir = getSystemdUserDir();
|
|
871
|
+
if (!existsSync3(systemdDir)) mkdirSync(systemdDir, { recursive: true });
|
|
872
|
+
writeFileSync2(getDaemonServicePath(), generateDaemonUnit(skilloBin, envPath), "utf-8");
|
|
873
|
+
execSync("systemctl --user daemon-reload");
|
|
874
|
+
execSync("systemctl --user enable skillo-daemon.service");
|
|
875
|
+
execSync("systemctl --user start skillo-daemon.service");
|
|
876
|
+
if (includeTray) {
|
|
877
|
+
writeFileSync2(getTrayServicePath(), generateTrayUnit(skilloBin, envPath), "utf-8");
|
|
878
|
+
execSync("systemctl --user daemon-reload");
|
|
879
|
+
execSync("systemctl --user enable skillo-tray.service");
|
|
880
|
+
try {
|
|
881
|
+
execSync("systemctl --user start skillo-tray.service");
|
|
882
|
+
} catch {
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
return { success: true };
|
|
886
|
+
} else if (os2 === "win32") {
|
|
887
|
+
const dataDir = getSkilloDataDir();
|
|
888
|
+
if (!existsSync3(dataDir)) mkdirSync(dataDir, { recursive: true });
|
|
889
|
+
const vbsPath = getWinDaemonVbsPath();
|
|
890
|
+
writeFileSync2(vbsPath, generateDaemonVbs(skilloBin), "utf-8");
|
|
891
|
+
winRegAdd(WIN_DAEMON_VALUE, `wscript.exe "${vbsPath}"`);
|
|
892
|
+
if (includeTray) {
|
|
893
|
+
winRegAdd(WIN_TRAY_VALUE, `"${skilloBin}" tray`);
|
|
894
|
+
}
|
|
895
|
+
return { success: true };
|
|
896
|
+
} else {
|
|
897
|
+
return { success: false, error: `Unsupported platform: ${os2}. Auto-start is available on macOS, Linux, and Windows.` };
|
|
898
|
+
}
|
|
899
|
+
} catch (error) {
|
|
900
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
901
|
+
}
|
|
902
|
+
}
|
|
903
|
+
async function uninstallService() {
|
|
904
|
+
const os2 = platform();
|
|
905
|
+
try {
|
|
906
|
+
if (os2 === "darwin") {
|
|
907
|
+
const daemonPlist = getDaemonPlistPath();
|
|
908
|
+
if (existsSync3(daemonPlist)) {
|
|
909
|
+
try {
|
|
910
|
+
execSync(`launchctl unload "${daemonPlist}" 2>/dev/null`);
|
|
911
|
+
} catch {
|
|
912
|
+
}
|
|
913
|
+
unlinkSync(daemonPlist);
|
|
914
|
+
}
|
|
915
|
+
const trayPlist = getTrayPlistPath();
|
|
916
|
+
if (existsSync3(trayPlist)) {
|
|
917
|
+
try {
|
|
918
|
+
execSync(`launchctl unload "${trayPlist}" 2>/dev/null`);
|
|
919
|
+
} catch {
|
|
920
|
+
}
|
|
921
|
+
unlinkSync(trayPlist);
|
|
922
|
+
}
|
|
923
|
+
return { success: true };
|
|
924
|
+
} else if (os2 === "linux") {
|
|
925
|
+
const daemonService = getDaemonServicePath();
|
|
926
|
+
if (existsSync3(daemonService)) {
|
|
927
|
+
try {
|
|
928
|
+
execSync("systemctl --user stop skillo-daemon.service 2>/dev/null");
|
|
929
|
+
} catch {
|
|
930
|
+
}
|
|
931
|
+
try {
|
|
932
|
+
execSync("systemctl --user disable skillo-daemon.service 2>/dev/null");
|
|
933
|
+
} catch {
|
|
934
|
+
}
|
|
935
|
+
unlinkSync(daemonService);
|
|
936
|
+
}
|
|
937
|
+
const trayService = getTrayServicePath();
|
|
938
|
+
if (existsSync3(trayService)) {
|
|
939
|
+
try {
|
|
940
|
+
execSync("systemctl --user stop skillo-tray.service 2>/dev/null");
|
|
941
|
+
} catch {
|
|
942
|
+
}
|
|
943
|
+
try {
|
|
944
|
+
execSync("systemctl --user disable skillo-tray.service 2>/dev/null");
|
|
945
|
+
} catch {
|
|
946
|
+
}
|
|
947
|
+
unlinkSync(trayService);
|
|
948
|
+
}
|
|
949
|
+
try {
|
|
950
|
+
execSync("systemctl --user daemon-reload");
|
|
951
|
+
} catch {
|
|
952
|
+
}
|
|
953
|
+
return { success: true };
|
|
954
|
+
} else if (os2 === "win32") {
|
|
955
|
+
winRegDelete(WIN_DAEMON_VALUE);
|
|
956
|
+
winRegDelete(WIN_TRAY_VALUE);
|
|
957
|
+
const vbsPath = getWinDaemonVbsPath();
|
|
958
|
+
if (existsSync3(vbsPath)) {
|
|
959
|
+
try {
|
|
960
|
+
unlinkSync(vbsPath);
|
|
961
|
+
} catch {
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
return { success: true };
|
|
965
|
+
} else {
|
|
966
|
+
return { success: false, error: `Unsupported platform: ${os2}` };
|
|
967
|
+
}
|
|
968
|
+
} catch (error) {
|
|
969
|
+
return { success: false, error: error instanceof Error ? error.message : String(error) };
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
function getServiceStatus() {
|
|
973
|
+
const os2 = platform();
|
|
974
|
+
if (os2 === "darwin") {
|
|
975
|
+
const daemonInstalled = existsSync3(getDaemonPlistPath());
|
|
976
|
+
const trayInstalled = existsSync3(getTrayPlistPath());
|
|
977
|
+
let daemonLoaded = false;
|
|
978
|
+
let trayLoaded = false;
|
|
979
|
+
try {
|
|
980
|
+
const output = execSync("launchctl list", { encoding: "utf-8" });
|
|
981
|
+
daemonLoaded = output.includes(DAEMON_LABEL);
|
|
982
|
+
trayLoaded = output.includes(TRAY_LABEL);
|
|
983
|
+
} catch {
|
|
984
|
+
}
|
|
985
|
+
return {
|
|
986
|
+
platform: "macos",
|
|
987
|
+
daemon: { installed: daemonInstalled, loaded: daemonLoaded },
|
|
988
|
+
tray: { installed: trayInstalled, loaded: trayLoaded }
|
|
989
|
+
};
|
|
990
|
+
} else if (os2 === "linux") {
|
|
991
|
+
const daemonInstalled = existsSync3(getDaemonServicePath());
|
|
992
|
+
const trayInstalled = existsSync3(getTrayServicePath());
|
|
993
|
+
let daemonLoaded = false;
|
|
994
|
+
let trayLoaded = false;
|
|
995
|
+
try {
|
|
996
|
+
execSync("systemctl --user is-active skillo-daemon.service", { encoding: "utf-8" });
|
|
997
|
+
daemonLoaded = true;
|
|
998
|
+
} catch {
|
|
999
|
+
}
|
|
1000
|
+
try {
|
|
1001
|
+
execSync("systemctl --user is-active skillo-tray.service", { encoding: "utf-8" });
|
|
1002
|
+
trayLoaded = true;
|
|
1003
|
+
} catch {
|
|
1004
|
+
}
|
|
1005
|
+
return {
|
|
1006
|
+
platform: "linux",
|
|
1007
|
+
daemon: { installed: daemonInstalled, loaded: daemonLoaded },
|
|
1008
|
+
tray: { installed: trayInstalled, loaded: trayLoaded }
|
|
1009
|
+
};
|
|
1010
|
+
} else if (os2 === "win32") {
|
|
1011
|
+
const daemonInstalled = winRegExists(WIN_DAEMON_VALUE);
|
|
1012
|
+
const trayInstalled = winRegExists(WIN_TRAY_VALUE);
|
|
1013
|
+
return {
|
|
1014
|
+
platform: "windows",
|
|
1015
|
+
daemon: { installed: daemonInstalled, loaded: daemonInstalled },
|
|
1016
|
+
tray: { installed: trayInstalled, loaded: trayInstalled }
|
|
1017
|
+
};
|
|
1018
|
+
}
|
|
1019
|
+
return {
|
|
1020
|
+
platform: "unsupported",
|
|
1021
|
+
daemon: { installed: false, loaded: false },
|
|
1022
|
+
tray: { installed: false, loaded: false }
|
|
1023
|
+
};
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
// src/commands/status.ts
|
|
598
1027
|
function isDaemonRunning() {
|
|
599
1028
|
const pidFile = getPidFile();
|
|
600
|
-
if (!
|
|
1029
|
+
if (!existsSync4(pidFile)) {
|
|
601
1030
|
return { running: false, pid: null };
|
|
602
1031
|
}
|
|
603
1032
|
try {
|
|
604
|
-
const pidStr =
|
|
1033
|
+
const pidStr = readFileSync3(pidFile, "utf-8").trim();
|
|
605
1034
|
const pid = parseInt(pidStr, 10);
|
|
606
1035
|
if (isNaN(pid)) {
|
|
607
1036
|
return { running: false, pid: null };
|
|
@@ -625,11 +1054,18 @@ var statusCommand = new Command2("status").description("Show daemon status and s
|
|
|
625
1054
|
logger_default.stopped("Daemon is not running");
|
|
626
1055
|
logger_default.blank();
|
|
627
1056
|
logger_default.dim("Start with: skillo start");
|
|
628
|
-
return;
|
|
629
1057
|
}
|
|
1058
|
+
const serviceStatus = getServiceStatus();
|
|
1059
|
+
if (serviceStatus.daemon.installed) {
|
|
1060
|
+
logger_default.running("Auto-start: enabled");
|
|
1061
|
+
} else {
|
|
1062
|
+
logger_default.stopped("Auto-start: disabled");
|
|
1063
|
+
logger_default.dim(" Enable with: skillo service install");
|
|
1064
|
+
}
|
|
1065
|
+
if (!running) return;
|
|
630
1066
|
logger_default.blank();
|
|
631
1067
|
const dbPath = getDbPath();
|
|
632
|
-
if (!
|
|
1068
|
+
if (!existsSync4(dbPath)) {
|
|
633
1069
|
logger_default.warn("Database not found. Run 'skillo init' first.");
|
|
634
1070
|
return;
|
|
635
1071
|
}
|
|
@@ -646,10 +1082,10 @@ var statusCommand = new Command2("status").description("Show daemon status and s
|
|
|
646
1082
|
]);
|
|
647
1083
|
logger_default.blank();
|
|
648
1084
|
const logFile = getLogFile();
|
|
649
|
-
if (
|
|
1085
|
+
if (existsSync4(logFile)) {
|
|
650
1086
|
logger_default.dim("Recent log entries:");
|
|
651
1087
|
try {
|
|
652
|
-
const content =
|
|
1088
|
+
const content = readFileSync3(logFile, "utf-8");
|
|
653
1089
|
const lines = content.split("\n").filter(Boolean).slice(-5);
|
|
654
1090
|
lines.forEach((line) => {
|
|
655
1091
|
logger_default.dim(` ${line}`);
|
|
@@ -661,7 +1097,7 @@ var statusCommand = new Command2("status").description("Show daemon status and s
|
|
|
661
1097
|
});
|
|
662
1098
|
|
|
663
1099
|
// src/commands/config.ts
|
|
664
|
-
import { existsSync as
|
|
1100
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "fs";
|
|
665
1101
|
import { Command as Command3 } from "commander";
|
|
666
1102
|
import YAML from "yaml";
|
|
667
1103
|
var configCommand = new Command3("config").description(
|
|
@@ -669,12 +1105,12 @@ var configCommand = new Command3("config").description(
|
|
|
669
1105
|
);
|
|
670
1106
|
configCommand.command("show").description("Show current configuration").action(async () => {
|
|
671
1107
|
const configFile = getConfigFile();
|
|
672
|
-
if (!
|
|
1108
|
+
if (!existsSync5(configFile)) {
|
|
673
1109
|
logger_default.error("Configuration not found.");
|
|
674
1110
|
logger_default.dim("Run 'skillo init' first.");
|
|
675
1111
|
process.exit(1);
|
|
676
1112
|
}
|
|
677
|
-
const content =
|
|
1113
|
+
const content = readFileSync4(configFile, "utf-8");
|
|
678
1114
|
logger_default.blank();
|
|
679
1115
|
logger_default.bold(`Configuration file: ${configFile}`);
|
|
680
1116
|
logger_default.blank();
|
|
@@ -682,7 +1118,7 @@ configCommand.command("show").description("Show current configuration").action(a
|
|
|
682
1118
|
});
|
|
683
1119
|
configCommand.command("get <key>").description("Get a configuration value (use dot notation, e.g., patternDetection.minCount)").action(async (key) => {
|
|
684
1120
|
const configFile = getConfigFile();
|
|
685
|
-
if (!
|
|
1121
|
+
if (!existsSync5(configFile)) {
|
|
686
1122
|
logger_default.error("Configuration not found.");
|
|
687
1123
|
process.exit(1);
|
|
688
1124
|
}
|
|
@@ -700,7 +1136,7 @@ configCommand.command("get <key>").description("Get a configuration value (use d
|
|
|
700
1136
|
});
|
|
701
1137
|
configCommand.command("set <key> <value>").description("Set a configuration value").action(async (key, value) => {
|
|
702
1138
|
const configFile = getConfigFile();
|
|
703
|
-
if (!
|
|
1139
|
+
if (!existsSync5(configFile)) {
|
|
704
1140
|
logger_default.error("Configuration not found.");
|
|
705
1141
|
process.exit(1);
|
|
706
1142
|
}
|
|
@@ -742,7 +1178,7 @@ configCommand.command("path").description("Show configuration file path").action
|
|
|
742
1178
|
});
|
|
743
1179
|
|
|
744
1180
|
// src/commands/patterns.ts
|
|
745
|
-
import { existsSync as
|
|
1181
|
+
import { existsSync as existsSync6 } from "fs";
|
|
746
1182
|
import { Command as Command4 } from "commander";
|
|
747
1183
|
import chalk2 from "chalk";
|
|
748
1184
|
var patternsCommand = new Command4("patterns").description(
|
|
@@ -750,7 +1186,7 @@ var patternsCommand = new Command4("patterns").description(
|
|
|
750
1186
|
);
|
|
751
1187
|
patternsCommand.command("list").description("List detected patterns").option("-t, --type <type>", "Filter by type (terminal, conversation)").option("-s, --status <status>", "Filter by status (active, converted, ignored)").option("-n, --limit <number>", "Number of patterns to show", "20").action(async (options) => {
|
|
752
1188
|
const dbPath = getDbPath();
|
|
753
|
-
if (!
|
|
1189
|
+
if (!existsSync6(dbPath)) {
|
|
754
1190
|
logger_default.error("Database not found. Run 'skillo init' first.");
|
|
755
1191
|
process.exit(1);
|
|
756
1192
|
}
|
|
@@ -787,7 +1223,7 @@ patternsCommand.command("list").description("List detected patterns").option("-t
|
|
|
787
1223
|
});
|
|
788
1224
|
patternsCommand.command("show <id>").description("Show pattern details").action(async (id) => {
|
|
789
1225
|
const dbPath = getDbPath();
|
|
790
|
-
if (!
|
|
1226
|
+
if (!existsSync6(dbPath)) {
|
|
791
1227
|
logger_default.error("Database not found. Run 'skillo init' first.");
|
|
792
1228
|
process.exit(1);
|
|
793
1229
|
}
|
|
@@ -830,7 +1266,7 @@ patternsCommand.command("show <id>").description("Show pattern details").action(
|
|
|
830
1266
|
});
|
|
831
1267
|
patternsCommand.command("ignore <id>").description("Ignore a pattern (never suggest again)").option("-r, --reason <reason>", "Reason for ignoring").action(async (id, options) => {
|
|
832
1268
|
const dbPath = getDbPath();
|
|
833
|
-
if (!
|
|
1269
|
+
if (!existsSync6(dbPath)) {
|
|
834
1270
|
logger_default.error("Database not found. Run 'skillo init' first.");
|
|
835
1271
|
process.exit(1);
|
|
836
1272
|
}
|
|
@@ -852,11 +1288,11 @@ patternsCommand.command("ignore <id>").description("Ignore a pattern (never sugg
|
|
|
852
1288
|
});
|
|
853
1289
|
patternsCommand.command("generate <id>").description("Generate a skill from a pattern").option("-n, --name <name>", "Custom skill name").option("--dry-run", "Preview without creating").action(async (id, options) => {
|
|
854
1290
|
const { getApiClient: getApiClient2 } = await import("./api-client-WO6NUCIJ.js");
|
|
855
|
-
const { writeFileSync:
|
|
856
|
-
const { join:
|
|
1291
|
+
const { writeFileSync: writeFileSync8, mkdirSync: mkdirSync3 } = await import("fs");
|
|
1292
|
+
const { join: join7 } = await import("path");
|
|
857
1293
|
const { getSkillsDir: getSkillsDir2, ensureDirectory: ensureDirectory2 } = await import("./paths-INOKEM66.js");
|
|
858
1294
|
const dbPath = getDbPath();
|
|
859
|
-
if (!
|
|
1295
|
+
if (!existsSync6(dbPath)) {
|
|
860
1296
|
logger_default.error("Database not found. Run 'skillo init' first.");
|
|
861
1297
|
process.exit(1);
|
|
862
1298
|
}
|
|
@@ -907,10 +1343,10 @@ patternsCommand.command("generate <id>").description("Generate a skill from a pa
|
|
|
907
1343
|
} else {
|
|
908
1344
|
const skillsDir = getSkillsDir2();
|
|
909
1345
|
ensureDirectory2(skillsDir);
|
|
910
|
-
const skillDir =
|
|
911
|
-
const skillFile =
|
|
912
|
-
|
|
913
|
-
|
|
1346
|
+
const skillDir = join7(skillsDir, skill.slug);
|
|
1347
|
+
const skillFile = join7(skillDir, "SKILL.md");
|
|
1348
|
+
mkdirSync3(skillDir, { recursive: true });
|
|
1349
|
+
writeFileSync8(skillFile, skill.content, "utf-8");
|
|
914
1350
|
await db.updatePatternStatus(id, "converted");
|
|
915
1351
|
logger_default.success(`Skill saved to: ~/.claude/skills/${skill.slug}/SKILL.md`);
|
|
916
1352
|
logger_default.blank();
|
|
@@ -925,7 +1361,7 @@ patternsCommand.command("clear").description("Clear all patterns").option("-f, -
|
|
|
925
1361
|
return;
|
|
926
1362
|
}
|
|
927
1363
|
const dbPath = getDbPath();
|
|
928
|
-
if (!
|
|
1364
|
+
if (!existsSync6(dbPath)) {
|
|
929
1365
|
logger_default.error("Database not found.");
|
|
930
1366
|
process.exit(1);
|
|
931
1367
|
}
|
|
@@ -933,8 +1369,8 @@ patternsCommand.command("clear").description("Clear all patterns").option("-f, -
|
|
|
933
1369
|
});
|
|
934
1370
|
|
|
935
1371
|
// src/commands/skills.ts
|
|
936
|
-
import { existsSync as
|
|
937
|
-
import { join } from "path";
|
|
1372
|
+
import { existsSync as existsSync7, readdirSync as readdirSync2, rmSync } from "fs";
|
|
1373
|
+
import { join as join2 } from "path";
|
|
938
1374
|
import { Command as Command5 } from "commander";
|
|
939
1375
|
import chalk3 from "chalk";
|
|
940
1376
|
var skillsCommand = new Command5("skills").description(
|
|
@@ -942,7 +1378,7 @@ var skillsCommand = new Command5("skills").description(
|
|
|
942
1378
|
);
|
|
943
1379
|
skillsCommand.command("list").description("List all skills").option("-s, --source <source>", "Filter by source (pattern, imported, manual)").option("--sort <by>", "Sort by (name, created, usage)", "name").action(async (options) => {
|
|
944
1380
|
const dbPath = getDbPath();
|
|
945
|
-
if (!
|
|
1381
|
+
if (!existsSync7(dbPath)) {
|
|
946
1382
|
logger_default.error("Database not found. Run 'skillo init' first.");
|
|
947
1383
|
process.exit(1);
|
|
948
1384
|
}
|
|
@@ -955,11 +1391,11 @@ skillsCommand.command("list").description("List all skills").option("-s, --sourc
|
|
|
955
1391
|
db.close();
|
|
956
1392
|
const skillsDir = getSkillsDir();
|
|
957
1393
|
const fsSkills = [];
|
|
958
|
-
if (
|
|
959
|
-
const entries =
|
|
1394
|
+
if (existsSync7(skillsDir)) {
|
|
1395
|
+
const entries = readdirSync2(skillsDir, { withFileTypes: true });
|
|
960
1396
|
entries.filter((e) => e.isDirectory() && !e.name.startsWith(".")).forEach((e) => {
|
|
961
|
-
const skillMd =
|
|
962
|
-
if (
|
|
1397
|
+
const skillMd = join2(skillsDir, e.name, "SKILL.md");
|
|
1398
|
+
if (existsSync7(skillMd)) {
|
|
963
1399
|
fsSkills.push(e.name);
|
|
964
1400
|
}
|
|
965
1401
|
});
|
|
@@ -999,7 +1435,7 @@ skillsCommand.command("list").description("List all skills").option("-s, --sourc
|
|
|
999
1435
|
});
|
|
1000
1436
|
skillsCommand.command("show <name>").description("Show skill details").action(async (name) => {
|
|
1001
1437
|
const dbPath = getDbPath();
|
|
1002
|
-
if (!
|
|
1438
|
+
if (!existsSync7(dbPath)) {
|
|
1003
1439
|
logger_default.error("Database not found. Run 'skillo init' first.");
|
|
1004
1440
|
process.exit(1);
|
|
1005
1441
|
}
|
|
@@ -1008,12 +1444,12 @@ skillsCommand.command("show <name>").description("Show skill details").action(as
|
|
|
1008
1444
|
const skill = await db.getSkill(name);
|
|
1009
1445
|
db.close();
|
|
1010
1446
|
if (!skill) {
|
|
1011
|
-
const skillPath =
|
|
1012
|
-
if (
|
|
1447
|
+
const skillPath = join2(getSkillsDir(), name, "SKILL.md");
|
|
1448
|
+
if (existsSync7(skillPath)) {
|
|
1013
1449
|
logger_default.blank();
|
|
1014
1450
|
logger_default.bold(name);
|
|
1015
1451
|
logger_default.blank();
|
|
1016
|
-
logger_default.dim(` Path: ${
|
|
1452
|
+
logger_default.dim(` Path: ${join2(getSkillsDir(), name)}`);
|
|
1017
1453
|
logger_default.dim(` Status: Not registered in database`);
|
|
1018
1454
|
logger_default.blank();
|
|
1019
1455
|
return;
|
|
@@ -1040,15 +1476,15 @@ skillsCommand.command("show <name>").description("Show skill details").action(as
|
|
|
1040
1476
|
});
|
|
1041
1477
|
skillsCommand.command("delete <name>").description("Delete a skill").option("-f, --force", "Skip confirmation").action(async (name, options) => {
|
|
1042
1478
|
const dbPath = getDbPath();
|
|
1043
|
-
if (!
|
|
1479
|
+
if (!existsSync7(dbPath)) {
|
|
1044
1480
|
logger_default.error("Database not found.");
|
|
1045
1481
|
process.exit(1);
|
|
1046
1482
|
}
|
|
1047
1483
|
const db = new SkilloDatabase();
|
|
1048
1484
|
await db.initialize();
|
|
1049
1485
|
const skill = await db.getSkill(name);
|
|
1050
|
-
const skillDir = skill?.path ||
|
|
1051
|
-
if (!skill && !
|
|
1486
|
+
const skillDir = skill?.path || join2(getSkillsDir(), name);
|
|
1487
|
+
if (!skill && !existsSync7(skillDir)) {
|
|
1052
1488
|
db.close();
|
|
1053
1489
|
logger_default.error(`Skill not found: ${name}`);
|
|
1054
1490
|
process.exit(1);
|
|
@@ -1059,7 +1495,7 @@ skillsCommand.command("delete <name>").description("Delete a skill").option("-f,
|
|
|
1059
1495
|
db.close();
|
|
1060
1496
|
return;
|
|
1061
1497
|
}
|
|
1062
|
-
if (
|
|
1498
|
+
if (existsSync7(skillDir)) {
|
|
1063
1499
|
rmSync(skillDir, { recursive: true, force: true });
|
|
1064
1500
|
}
|
|
1065
1501
|
if (skill) {
|
|
@@ -1073,7 +1509,7 @@ skillsCommand.command("path").description("Show skills directory path").action(a
|
|
|
1073
1509
|
});
|
|
1074
1510
|
skillsCommand.command("open").description("Open skills directory in file manager").action(async () => {
|
|
1075
1511
|
const skillsDir = getSkillsDir();
|
|
1076
|
-
if (!
|
|
1512
|
+
if (!existsSync7(skillsDir)) {
|
|
1077
1513
|
logger_default.error("Skills directory not found. Run 'skillo init' first.");
|
|
1078
1514
|
process.exit(1);
|
|
1079
1515
|
}
|
|
@@ -1088,10 +1524,10 @@ skillsCommand.command("open").description("Open skills directory in file manager
|
|
|
1088
1524
|
});
|
|
1089
1525
|
|
|
1090
1526
|
// src/commands/shell.ts
|
|
1091
|
-
import { existsSync as
|
|
1527
|
+
import { existsSync as existsSync8, writeFileSync as writeFileSync3, readFileSync as readFileSync5, appendFileSync } from "fs";
|
|
1092
1528
|
import { spawn } from "child_process";
|
|
1093
|
-
import { homedir } from "os";
|
|
1094
|
-
import { join as
|
|
1529
|
+
import { homedir as homedir2 } from "os";
|
|
1530
|
+
import { join as join3 } from "path";
|
|
1095
1531
|
import { Command as Command6 } from "commander";
|
|
1096
1532
|
function normalizeCommand(cmd) {
|
|
1097
1533
|
const variables = {};
|
|
@@ -1128,7 +1564,7 @@ function normalizeCommand(cmd) {
|
|
|
1128
1564
|
return { normalized: normalized.trim(), variables };
|
|
1129
1565
|
}
|
|
1130
1566
|
var shellCommand = new Command6("shell").description("Start a tracked terminal session").option("-s, --shell <shell>", "Shell to use (default: $SHELL or cmd on Windows)").option("-p, --project <path>", "Project path to track").action(async (options) => {
|
|
1131
|
-
if (!
|
|
1567
|
+
if (!existsSync8(getConfigFile())) {
|
|
1132
1568
|
logger_default.error("Skillo not initialized. Run 'skillo init' first.");
|
|
1133
1569
|
process.exit(1);
|
|
1134
1570
|
}
|
|
@@ -1224,7 +1660,7 @@ var recordCommand = new Command6("record").description("Record a command (used b
|
|
|
1224
1660
|
);
|
|
1225
1661
|
var setupShellCommand = new Command6("setup-shell").description("Set up shell integration for automatic command tracking").option("--bash", "Set up Bash integration").option("--zsh", "Set up Zsh integration").option("--powershell", "Set up PowerShell integration").option("--fish", "Set up Fish integration").option("--uninstall", "Remove shell integration").action(async (options) => {
|
|
1226
1662
|
logger_default.blank();
|
|
1227
|
-
const home =
|
|
1663
|
+
const home = homedir2();
|
|
1228
1664
|
const dataDir = getDataDir();
|
|
1229
1665
|
ensureDirectory(dataDir);
|
|
1230
1666
|
let shell = null;
|
|
@@ -1272,7 +1708,7 @@ var setupShellCommand = new Command6("setup-shell").description("Set up shell in
|
|
|
1272
1708
|
logger_default.blank();
|
|
1273
1709
|
});
|
|
1274
1710
|
async function installShellIntegration(shell, home, dataDir) {
|
|
1275
|
-
const scriptPath =
|
|
1711
|
+
const scriptPath = join3(dataDir, "shell-integration");
|
|
1276
1712
|
ensureDirectory(scriptPath);
|
|
1277
1713
|
if (shell === "powershell") {
|
|
1278
1714
|
const psScript = `# Skillo CLI Integration
|
|
@@ -1420,18 +1856,18 @@ $null = Register-EngineEvent -SourceIdentifier PowerShell.Exiting -Action {
|
|
|
1420
1856
|
|
|
1421
1857
|
Write-Host "Skillo: Command tracking enabled" -ForegroundColor DarkGray
|
|
1422
1858
|
`;
|
|
1423
|
-
const psScriptFile =
|
|
1424
|
-
|
|
1859
|
+
const psScriptFile = join3(scriptPath, "skillo.ps1");
|
|
1860
|
+
writeFileSync3(psScriptFile, psScript, "utf-8");
|
|
1425
1861
|
logger_default.success(`Created ${psScriptFile}`);
|
|
1426
|
-
const profilePath =
|
|
1427
|
-
const profileDir =
|
|
1862
|
+
const profilePath = join3(home, "Documents", "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1");
|
|
1863
|
+
const profileDir = join3(home, "Documents", "WindowsPowerShell");
|
|
1428
1864
|
ensureDirectory(profileDir);
|
|
1429
1865
|
const sourceLine = `
|
|
1430
1866
|
# Skillo CLI Integration
|
|
1431
1867
|
. "${psScriptFile}"
|
|
1432
1868
|
`;
|
|
1433
|
-
if (
|
|
1434
|
-
const content =
|
|
1869
|
+
if (existsSync8(profilePath)) {
|
|
1870
|
+
const content = readFileSync5(profilePath, "utf-8");
|
|
1435
1871
|
if (!content.includes("Skillo CLI Integration")) {
|
|
1436
1872
|
appendFileSync(profilePath, sourceLine);
|
|
1437
1873
|
logger_default.success("Added to PowerShell profile");
|
|
@@ -1439,7 +1875,7 @@ Write-Host "Skillo: Command tracking enabled" -ForegroundColor DarkGray
|
|
|
1439
1875
|
logger_default.dim("Already in PowerShell profile");
|
|
1440
1876
|
}
|
|
1441
1877
|
} else {
|
|
1442
|
-
|
|
1878
|
+
writeFileSync3(profilePath, sourceLine, "utf-8");
|
|
1443
1879
|
logger_default.success("Created PowerShell profile");
|
|
1444
1880
|
}
|
|
1445
1881
|
} else if (shell === "bash") {
|
|
@@ -1509,16 +1945,16 @@ Write-Host "Skillo: Command tracking enabled" -ForegroundColor DarkGray
|
|
|
1509
1945
|
"",
|
|
1510
1946
|
'echo -e "\\033[90mSkillo: Command tracking enabled\\033[0m"'
|
|
1511
1947
|
].join("\n");
|
|
1512
|
-
const bashScriptFile =
|
|
1513
|
-
|
|
1948
|
+
const bashScriptFile = join3(scriptPath, "skillo.bash");
|
|
1949
|
+
writeFileSync3(bashScriptFile, bashScript, "utf-8");
|
|
1514
1950
|
logger_default.success(`Created ${bashScriptFile}`);
|
|
1515
|
-
const bashrcPath =
|
|
1951
|
+
const bashrcPath = join3(home, ".bashrc");
|
|
1516
1952
|
const sourceLine = `
|
|
1517
1953
|
# Skillo CLI Integration
|
|
1518
1954
|
[ -f "${bashScriptFile}" ] && source "${bashScriptFile}"
|
|
1519
1955
|
`;
|
|
1520
|
-
if (
|
|
1521
|
-
const content =
|
|
1956
|
+
if (existsSync8(bashrcPath)) {
|
|
1957
|
+
const content = readFileSync5(bashrcPath, "utf-8");
|
|
1522
1958
|
if (!content.includes("Skillo CLI Integration")) {
|
|
1523
1959
|
appendFileSync(bashrcPath, sourceLine);
|
|
1524
1960
|
logger_default.success("Added to ~/.bashrc");
|
|
@@ -1526,7 +1962,7 @@ Write-Host "Skillo: Command tracking enabled" -ForegroundColor DarkGray
|
|
|
1526
1962
|
logger_default.dim("Already in ~/.bashrc");
|
|
1527
1963
|
}
|
|
1528
1964
|
} else {
|
|
1529
|
-
|
|
1965
|
+
writeFileSync3(bashrcPath, sourceLine, "utf-8");
|
|
1530
1966
|
logger_default.success("Created ~/.bashrc");
|
|
1531
1967
|
}
|
|
1532
1968
|
} else if (shell === "zsh") {
|
|
@@ -1592,16 +2028,16 @@ Write-Host "Skillo: Command tracking enabled" -ForegroundColor DarkGray
|
|
|
1592
2028
|
"",
|
|
1593
2029
|
'echo -e "\\033[90mSkillo: Command tracking enabled\\033[0m"'
|
|
1594
2030
|
].join("\n");
|
|
1595
|
-
const zshScriptFile =
|
|
1596
|
-
|
|
2031
|
+
const zshScriptFile = join3(scriptPath, "skillo.zsh");
|
|
2032
|
+
writeFileSync3(zshScriptFile, zshScript, "utf-8");
|
|
1597
2033
|
logger_default.success(`Created ${zshScriptFile}`);
|
|
1598
|
-
const zshrcPath =
|
|
2034
|
+
const zshrcPath = join3(home, ".zshrc");
|
|
1599
2035
|
const sourceLine = `
|
|
1600
2036
|
# Skillo CLI Integration
|
|
1601
2037
|
[ -f "${zshScriptFile}" ] && source "${zshScriptFile}"
|
|
1602
2038
|
`;
|
|
1603
|
-
if (
|
|
1604
|
-
const content =
|
|
2039
|
+
if (existsSync8(zshrcPath)) {
|
|
2040
|
+
const content = readFileSync5(zshrcPath, "utf-8");
|
|
1605
2041
|
if (!content.includes("Skillo CLI Integration")) {
|
|
1606
2042
|
appendFileSync(zshrcPath, sourceLine);
|
|
1607
2043
|
logger_default.success("Added to ~/.zshrc");
|
|
@@ -1609,7 +2045,7 @@ Write-Host "Skillo: Command tracking enabled" -ForegroundColor DarkGray
|
|
|
1609
2045
|
logger_default.dim("Already in ~/.zshrc");
|
|
1610
2046
|
}
|
|
1611
2047
|
} else {
|
|
1612
|
-
|
|
2048
|
+
writeFileSync3(zshrcPath, sourceLine, "utf-8");
|
|
1613
2049
|
logger_default.success("Created ~/.zshrc");
|
|
1614
2050
|
}
|
|
1615
2051
|
} else if (shell === "fish") {
|
|
@@ -1627,20 +2063,20 @@ Write-Host "Skillo: Command tracking enabled" -ForegroundColor DarkGray
|
|
|
1627
2063
|
"",
|
|
1628
2064
|
'echo -e "\\033[90mSkillo: Command tracking enabled\\033[0m"'
|
|
1629
2065
|
].join("\n");
|
|
1630
|
-
const fishScriptFile =
|
|
1631
|
-
|
|
2066
|
+
const fishScriptFile = join3(scriptPath, "skillo.fish");
|
|
2067
|
+
writeFileSync3(fishScriptFile, fishScript, "utf-8");
|
|
1632
2068
|
logger_default.success(`Created ${fishScriptFile}`);
|
|
1633
|
-
const fishConfigDir =
|
|
2069
|
+
const fishConfigDir = join3(home, ".config", "fish");
|
|
1634
2070
|
ensureDirectory(fishConfigDir);
|
|
1635
|
-
const fishConfigPath =
|
|
2071
|
+
const fishConfigPath = join3(fishConfigDir, "config.fish");
|
|
1636
2072
|
const sourceLine = `
|
|
1637
2073
|
# Skillo CLI Integration
|
|
1638
2074
|
if test -f "${fishScriptFile}"
|
|
1639
2075
|
source "${fishScriptFile}"
|
|
1640
2076
|
end
|
|
1641
2077
|
`;
|
|
1642
|
-
if (
|
|
1643
|
-
const content =
|
|
2078
|
+
if (existsSync8(fishConfigPath)) {
|
|
2079
|
+
const content = readFileSync5(fishConfigPath, "utf-8");
|
|
1644
2080
|
if (!content.includes("Skillo CLI Integration")) {
|
|
1645
2081
|
appendFileSync(fishConfigPath, sourceLine);
|
|
1646
2082
|
logger_default.success("Added to ~/.config/fish/config.fish");
|
|
@@ -1648,41 +2084,41 @@ end
|
|
|
1648
2084
|
logger_default.dim("Already in fish config");
|
|
1649
2085
|
}
|
|
1650
2086
|
} else {
|
|
1651
|
-
|
|
2087
|
+
writeFileSync3(fishConfigPath, sourceLine, "utf-8");
|
|
1652
2088
|
logger_default.success("Created fish config");
|
|
1653
2089
|
}
|
|
1654
2090
|
}
|
|
1655
2091
|
}
|
|
1656
2092
|
async function uninstallShellIntegration(shell, home) {
|
|
1657
2093
|
const removeFromFile = (filePath) => {
|
|
1658
|
-
if (!
|
|
1659
|
-
let content =
|
|
2094
|
+
if (!existsSync8(filePath)) return;
|
|
2095
|
+
let content = readFileSync5(filePath, "utf-8");
|
|
1660
2096
|
content = content.replace(/\n# Skillo CLI Integration\n[^\n]*\n/g, "\n");
|
|
1661
|
-
|
|
2097
|
+
writeFileSync3(filePath, content, "utf-8");
|
|
1662
2098
|
};
|
|
1663
2099
|
if (shell === "powershell") {
|
|
1664
|
-
const profilePath =
|
|
2100
|
+
const profilePath = join3(home, "Documents", "WindowsPowerShell", "Microsoft.PowerShell_profile.ps1");
|
|
1665
2101
|
removeFromFile(profilePath);
|
|
1666
2102
|
} else if (shell === "bash") {
|
|
1667
|
-
removeFromFile(
|
|
2103
|
+
removeFromFile(join3(home, ".bashrc"));
|
|
1668
2104
|
} else if (shell === "zsh") {
|
|
1669
|
-
removeFromFile(
|
|
2105
|
+
removeFromFile(join3(home, ".zshrc"));
|
|
1670
2106
|
} else if (shell === "fish") {
|
|
1671
|
-
removeFromFile(
|
|
2107
|
+
removeFromFile(join3(home, ".config", "fish", "config.fish"));
|
|
1672
2108
|
}
|
|
1673
2109
|
}
|
|
1674
2110
|
|
|
1675
2111
|
// src/commands/daemon.ts
|
|
1676
|
-
import { existsSync as
|
|
2112
|
+
import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync4, unlinkSync as unlinkSync2 } from "fs";
|
|
1677
2113
|
import { fork } from "child_process";
|
|
1678
2114
|
import { Command as Command7 } from "commander";
|
|
1679
2115
|
function isDaemonRunning2() {
|
|
1680
2116
|
const pidFile = getPidFile();
|
|
1681
|
-
if (!
|
|
2117
|
+
if (!existsSync9(pidFile)) {
|
|
1682
2118
|
return { running: false, pid: null };
|
|
1683
2119
|
}
|
|
1684
2120
|
try {
|
|
1685
|
-
const pidStr =
|
|
2121
|
+
const pidStr = readFileSync6(pidFile, "utf-8").trim();
|
|
1686
2122
|
const pid = parseInt(pidStr, 10);
|
|
1687
2123
|
if (isNaN(pid)) {
|
|
1688
2124
|
return { running: false, pid: null };
|
|
@@ -1692,7 +2128,7 @@ function isDaemonRunning2() {
|
|
|
1692
2128
|
return { running: true, pid };
|
|
1693
2129
|
} catch {
|
|
1694
2130
|
try {
|
|
1695
|
-
|
|
2131
|
+
unlinkSync2(pidFile);
|
|
1696
2132
|
} catch {
|
|
1697
2133
|
}
|
|
1698
2134
|
return { running: false, pid: null };
|
|
@@ -1713,13 +2149,13 @@ function startDaemonProcess() {
|
|
|
1713
2149
|
});
|
|
1714
2150
|
child.unref();
|
|
1715
2151
|
if (child.pid) {
|
|
1716
|
-
|
|
2152
|
+
writeFileSync4(getPidFile(), String(child.pid));
|
|
1717
2153
|
return child.pid;
|
|
1718
2154
|
}
|
|
1719
2155
|
return null;
|
|
1720
2156
|
}
|
|
1721
2157
|
var startCommand = new Command7("start").description("Start the Skillo daemon").option("-f, --foreground", "Run in foreground (don't daemonize)").action(async (options) => {
|
|
1722
|
-
if (!
|
|
2158
|
+
if (!existsSync9(getConfigFile())) {
|
|
1723
2159
|
logger_default.error("Skillo not initialized.");
|
|
1724
2160
|
logger_default.dim("Run 'skillo init' first.");
|
|
1725
2161
|
process.exit(1);
|
|
@@ -1745,6 +2181,10 @@ var startCommand = new Command7("start").description("Start the Skillo daemon").
|
|
|
1745
2181
|
const daemonPid = startDaemonProcess();
|
|
1746
2182
|
if (daemonPid) {
|
|
1747
2183
|
logger_default.success(`Daemon started (PID: ${daemonPid})`);
|
|
2184
|
+
const serviceResult = await installService({ includeTray: false });
|
|
2185
|
+
if (serviceResult.success) {
|
|
2186
|
+
logger_default.dim("Auto-start service installed. Daemon will survive reboots.");
|
|
2187
|
+
}
|
|
1748
2188
|
logger_default.blank();
|
|
1749
2189
|
logger_default.dim("Use 'skillo status' to check daemon status");
|
|
1750
2190
|
logger_default.dim("Use 'skillo stop' to stop the daemon");
|
|
@@ -1761,6 +2201,10 @@ var stopCommand = new Command7("stop").description("Stop the Skillo daemon").act
|
|
|
1761
2201
|
return;
|
|
1762
2202
|
}
|
|
1763
2203
|
logger_default.info(`Stopping daemon (PID: ${pid})...`);
|
|
2204
|
+
const serviceResult = await uninstallService();
|
|
2205
|
+
if (serviceResult.success) {
|
|
2206
|
+
logger_default.dim("Auto-start service removed.");
|
|
2207
|
+
}
|
|
1764
2208
|
try {
|
|
1765
2209
|
process.kill(pid, "SIGTERM");
|
|
1766
2210
|
logger_default.success("Daemon stopped");
|
|
@@ -1775,16 +2219,16 @@ var stopCommand = new Command7("stop").description("Stop the Skillo daemon").act
|
|
|
1775
2219
|
}
|
|
1776
2220
|
}
|
|
1777
2221
|
const pidFile = getPidFile();
|
|
1778
|
-
if (
|
|
2222
|
+
if (existsSync9(pidFile)) {
|
|
1779
2223
|
try {
|
|
1780
|
-
|
|
2224
|
+
unlinkSync2(pidFile);
|
|
1781
2225
|
} catch {
|
|
1782
2226
|
}
|
|
1783
2227
|
}
|
|
1784
2228
|
});
|
|
1785
2229
|
var logsCommand = new Command7("logs").description("Show daemon logs").option("-n, --lines <number>", "Number of lines to show", "50").option("-f, --follow", "Follow log output").action(async (options) => {
|
|
1786
2230
|
const logFile = getLogFile();
|
|
1787
|
-
if (!
|
|
2231
|
+
if (!existsSync9(logFile)) {
|
|
1788
2232
|
logger_default.dim("No logs found.");
|
|
1789
2233
|
return;
|
|
1790
2234
|
}
|
|
@@ -1803,23 +2247,70 @@ var logsCommand = new Command7("logs").description("Show daemon logs").option("-
|
|
|
1803
2247
|
}
|
|
1804
2248
|
});
|
|
1805
2249
|
function showLogs(logFile, numLines) {
|
|
1806
|
-
const content =
|
|
2250
|
+
const content = readFileSync6(logFile, "utf-8");
|
|
1807
2251
|
const lines = content.split("\n").filter(Boolean);
|
|
1808
2252
|
const lastLines = lines.slice(-numLines);
|
|
1809
2253
|
lastLines.forEach((line) => console.log(line));
|
|
1810
2254
|
}
|
|
2255
|
+
var serviceCommand = new Command7("service").description("Manage auto-start service").addCommand(
|
|
2256
|
+
new Command7("install").description("Install auto-start service (LaunchAgent on macOS, systemd on Linux)").option("--no-tray", "Skip installing tray icon service").action(async (options) => {
|
|
2257
|
+
logger_default.info("Installing auto-start service...");
|
|
2258
|
+
const result = await installService({ includeTray: options.tray !== false });
|
|
2259
|
+
if (result.success) {
|
|
2260
|
+
logger_default.success("Auto-start service installed.");
|
|
2261
|
+
logger_default.dim("Daemon will start automatically on login and restart on crash.");
|
|
2262
|
+
if (options.tray !== false) {
|
|
2263
|
+
logger_default.dim("Tray icon will appear in GUI sessions.");
|
|
2264
|
+
}
|
|
2265
|
+
} else {
|
|
2266
|
+
logger_default.error(`Failed to install service: ${result.error}`);
|
|
2267
|
+
}
|
|
2268
|
+
})
|
|
2269
|
+
).addCommand(
|
|
2270
|
+
new Command7("uninstall").description("Remove auto-start service").action(async () => {
|
|
2271
|
+
logger_default.info("Removing auto-start service...");
|
|
2272
|
+
const result = await uninstallService();
|
|
2273
|
+
if (result.success) {
|
|
2274
|
+
logger_default.success("Auto-start service removed.");
|
|
2275
|
+
} else {
|
|
2276
|
+
logger_default.error(`Failed to remove service: ${result.error}`);
|
|
2277
|
+
}
|
|
2278
|
+
})
|
|
2279
|
+
).addCommand(
|
|
2280
|
+
new Command7("status").description("Show auto-start service status").action(async () => {
|
|
2281
|
+
const status = getServiceStatus();
|
|
2282
|
+
logger_default.blank();
|
|
2283
|
+
logger_default.info(`Platform: ${status.platform}`);
|
|
2284
|
+
logger_default.blank();
|
|
2285
|
+
if (status.daemon.installed) {
|
|
2286
|
+
logger_default.running(`Daemon service: installed${status.daemon.loaded ? " & loaded" : " (not loaded)"}`);
|
|
2287
|
+
} else {
|
|
2288
|
+
logger_default.stopped("Daemon service: not installed");
|
|
2289
|
+
}
|
|
2290
|
+
if (status.tray.installed) {
|
|
2291
|
+
logger_default.running(`Tray service: installed${status.tray.loaded ? " & loaded" : " (not loaded)"}`);
|
|
2292
|
+
} else {
|
|
2293
|
+
logger_default.stopped("Tray service: not installed");
|
|
2294
|
+
}
|
|
2295
|
+
logger_default.blank();
|
|
2296
|
+
if (!status.daemon.installed) {
|
|
2297
|
+
logger_default.dim("Run 'skillo service install' to enable auto-start.");
|
|
2298
|
+
}
|
|
2299
|
+
logger_default.blank();
|
|
2300
|
+
})
|
|
2301
|
+
);
|
|
1811
2302
|
async function runDaemonForeground() {
|
|
1812
2303
|
const pidFile = getPidFile();
|
|
1813
|
-
|
|
2304
|
+
writeFileSync4(pidFile, String(process.pid));
|
|
1814
2305
|
logger_default.dim(`Daemon PID: ${process.pid}`);
|
|
1815
2306
|
logger_default.dim(`Log file: ${getLogFile()}`);
|
|
1816
2307
|
logger_default.blank();
|
|
1817
2308
|
const cleanup = () => {
|
|
1818
2309
|
logger_default.blank();
|
|
1819
2310
|
logger_default.info("Shutting down daemon...");
|
|
1820
|
-
if (
|
|
2311
|
+
if (existsSync9(pidFile)) {
|
|
1821
2312
|
try {
|
|
1822
|
-
|
|
2313
|
+
unlinkSync2(pidFile);
|
|
1823
2314
|
} catch {
|
|
1824
2315
|
}
|
|
1825
2316
|
}
|
|
@@ -1871,7 +2362,7 @@ async function runDaemonForeground() {
|
|
|
1871
2362
|
|
|
1872
2363
|
// src/commands/session.ts
|
|
1873
2364
|
import { Command as Command8 } from "commander";
|
|
1874
|
-
import { existsSync as
|
|
2365
|
+
import { existsSync as existsSync10, readFileSync as readFileSync7, writeFileSync as writeFileSync5, unlinkSync as unlinkSync3 } from "fs";
|
|
1875
2366
|
var sessionCommand = new Command8("session").description("Manage terminal sessions");
|
|
1876
2367
|
sessionCommand.command("start").description("Start a new terminal session (for standalone terminals)").option("--shell <shell>", "Shell type (powershell, bash, zsh, cmd)", "powershell").action(async (options) => {
|
|
1877
2368
|
try {
|
|
@@ -1924,18 +2415,18 @@ var sessionAutoCommand = new Command8("session-auto").description("Auto-detect p
|
|
|
1924
2415
|
const sessionsDir = getActiveSessionsDir();
|
|
1925
2416
|
const sessionFile = `${sessionsDir}/${terminalPid}.json`;
|
|
1926
2417
|
let currentSession = null;
|
|
1927
|
-
if (
|
|
2418
|
+
if (existsSync10(sessionFile)) {
|
|
1928
2419
|
try {
|
|
1929
|
-
currentSession = JSON.parse(
|
|
2420
|
+
currentSession = JSON.parse(readFileSync7(sessionFile, "utf-8"));
|
|
1930
2421
|
} catch {
|
|
1931
2422
|
currentSession = null;
|
|
1932
2423
|
}
|
|
1933
2424
|
}
|
|
1934
2425
|
const cacheFile = getTrackedProjectsCacheFile();
|
|
1935
2426
|
let matchedProject = null;
|
|
1936
|
-
if (
|
|
2427
|
+
if (existsSync10(cacheFile)) {
|
|
1937
2428
|
try {
|
|
1938
|
-
const cache = JSON.parse(
|
|
2429
|
+
const cache = JSON.parse(readFileSync7(cacheFile, "utf-8"));
|
|
1939
2430
|
const normalizedCwd = cwd.toLowerCase().replace(/\/+$/, "");
|
|
1940
2431
|
for (const project of cache.projects || []) {
|
|
1941
2432
|
const normalizedPath = project.path.toLowerCase().replace(/\/+$/, "");
|
|
@@ -1960,7 +2451,7 @@ var sessionAutoCommand = new Command8("session-auto").description("Auto-detect p
|
|
|
1960
2451
|
} catch {
|
|
1961
2452
|
}
|
|
1962
2453
|
try {
|
|
1963
|
-
|
|
2454
|
+
unlinkSync3(sessionFile);
|
|
1964
2455
|
} catch {
|
|
1965
2456
|
}
|
|
1966
2457
|
}
|
|
@@ -1969,7 +2460,7 @@ var sessionAutoCommand = new Command8("session-auto").description("Auto-detect p
|
|
|
1969
2460
|
const result = await client.startSession(shell, matchedProject.path);
|
|
1970
2461
|
if (result.success && result.data?.sessionId) {
|
|
1971
2462
|
ensureDirectory(sessionsDir);
|
|
1972
|
-
|
|
2463
|
+
writeFileSync5(sessionFile, JSON.stringify({
|
|
1973
2464
|
sessionId: result.data.sessionId,
|
|
1974
2465
|
projectPath: matchedProject.path,
|
|
1975
2466
|
projectName: matchedProject.name,
|
|
@@ -2267,11 +2758,11 @@ Synced to platform: ${response.data.sessions.length} recent sessions`);
|
|
|
2267
2758
|
|
|
2268
2759
|
// src/commands/project.ts
|
|
2269
2760
|
import { Command as Command10 } from "commander";
|
|
2270
|
-
import { existsSync as
|
|
2761
|
+
import { existsSync as existsSync12, writeFileSync as writeFileSync6 } from "fs";
|
|
2271
2762
|
|
|
2272
2763
|
// src/utils/git.ts
|
|
2273
|
-
import { execSync } from "child_process";
|
|
2274
|
-
import { join as
|
|
2764
|
+
import { execSync as execSync2 } from "child_process";
|
|
2765
|
+
import { join as join5, dirname as dirname3, basename as basename2 } from "path";
|
|
2275
2766
|
function getGitInfo(projectPath) {
|
|
2276
2767
|
const result = {
|
|
2277
2768
|
isGitRepo: false,
|
|
@@ -2282,7 +2773,7 @@ function getGitInfo(projectPath) {
|
|
|
2282
2773
|
projectName: null
|
|
2283
2774
|
};
|
|
2284
2775
|
try {
|
|
2285
|
-
const rootPath =
|
|
2776
|
+
const rootPath = execSync2("git rev-parse --show-toplevel", {
|
|
2286
2777
|
cwd: projectPath,
|
|
2287
2778
|
encoding: "utf-8",
|
|
2288
2779
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -2292,7 +2783,7 @@ function getGitInfo(projectPath) {
|
|
|
2292
2783
|
result.rootPath = rootPath;
|
|
2293
2784
|
result.projectName = basename2(rootPath);
|
|
2294
2785
|
try {
|
|
2295
|
-
result.branch =
|
|
2786
|
+
result.branch = execSync2("git rev-parse --abbrev-ref HEAD", {
|
|
2296
2787
|
cwd: projectPath,
|
|
2297
2788
|
encoding: "utf-8",
|
|
2298
2789
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -2300,20 +2791,20 @@ function getGitInfo(projectPath) {
|
|
|
2300
2791
|
} catch {
|
|
2301
2792
|
}
|
|
2302
2793
|
try {
|
|
2303
|
-
result.remote =
|
|
2794
|
+
result.remote = execSync2("git remote get-url origin", {
|
|
2304
2795
|
cwd: projectPath,
|
|
2305
2796
|
encoding: "utf-8",
|
|
2306
2797
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2307
2798
|
}).trim();
|
|
2308
2799
|
} catch {
|
|
2309
2800
|
try {
|
|
2310
|
-
const remotes =
|
|
2801
|
+
const remotes = execSync2("git remote", {
|
|
2311
2802
|
cwd: projectPath,
|
|
2312
2803
|
encoding: "utf-8",
|
|
2313
2804
|
stdio: ["pipe", "pipe", "pipe"]
|
|
2314
2805
|
}).trim().split("\n");
|
|
2315
2806
|
if (remotes.length > 0 && remotes[0]) {
|
|
2316
|
-
result.remote =
|
|
2807
|
+
result.remote = execSync2(`git remote get-url ${remotes[0]}`, {
|
|
2317
2808
|
cwd: projectPath,
|
|
2318
2809
|
encoding: "utf-8",
|
|
2319
2810
|
stdio: ["pipe", "pipe", "pipe"]
|
|
@@ -2397,7 +2888,7 @@ var trackCommand = new Command10("track").description("Enable tracking for the c
|
|
|
2397
2888
|
projects: listResult.data.projects.filter((p) => p.trackingEnabled).map((p) => ({ path: p.path, name: p.name }))
|
|
2398
2889
|
};
|
|
2399
2890
|
ensureDirectory(getDataDir());
|
|
2400
|
-
|
|
2891
|
+
writeFileSync6(getTrackedProjectsCacheFile(), JSON.stringify(cacheData, null, 2));
|
|
2401
2892
|
}
|
|
2402
2893
|
} catch {
|
|
2403
2894
|
}
|
|
@@ -2412,7 +2903,7 @@ var trackCommand = new Command10("track").description("Enable tracking for the c
|
|
|
2412
2903
|
}
|
|
2413
2904
|
}
|
|
2414
2905
|
const shellIntegrationDir = getShellIntegrationDir();
|
|
2415
|
-
if (!
|
|
2906
|
+
if (!existsSync12(shellIntegrationDir) || !existsSync12(resolve(shellIntegrationDir, "skillo.zsh"))) {
|
|
2416
2907
|
logger_default.blank();
|
|
2417
2908
|
logger_default.dim("Tip: Run 'skillo setup-shell' to enable terminal auto-detection.");
|
|
2418
2909
|
}
|
|
@@ -2538,11 +3029,11 @@ async function openBrowser(url) {
|
|
|
2538
3029
|
const { exec } = await import("child_process");
|
|
2539
3030
|
const { promisify } = await import("util");
|
|
2540
3031
|
const execAsync = promisify(exec);
|
|
2541
|
-
const
|
|
3032
|
+
const platform2 = process.platform;
|
|
2542
3033
|
let command;
|
|
2543
|
-
if (
|
|
3034
|
+
if (platform2 === "darwin") {
|
|
2544
3035
|
command = `open "${url}"`;
|
|
2545
|
-
} else if (
|
|
3036
|
+
} else if (platform2 === "win32") {
|
|
2546
3037
|
command = `start "" "${url}"`;
|
|
2547
3038
|
} else {
|
|
2548
3039
|
command = `xdg-open "${url}"`;
|
|
@@ -2556,6 +3047,22 @@ async function openBrowser(url) {
|
|
|
2556
3047
|
function sleep(ms) {
|
|
2557
3048
|
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
2558
3049
|
}
|
|
3050
|
+
async function autoStartDaemon() {
|
|
3051
|
+
try {
|
|
3052
|
+
const { running } = isDaemonRunning2();
|
|
3053
|
+
if (!running) {
|
|
3054
|
+
const pid = startDaemonProcess();
|
|
3055
|
+
if (pid) {
|
|
3056
|
+
logger_default.dim(`Background daemon started (PID: ${pid}).`);
|
|
3057
|
+
}
|
|
3058
|
+
}
|
|
3059
|
+
const result = await installService();
|
|
3060
|
+
if (result.success) {
|
|
3061
|
+
logger_default.dim("Auto-start installed. Daemon will survive reboots.");
|
|
3062
|
+
}
|
|
3063
|
+
} catch {
|
|
3064
|
+
}
|
|
3065
|
+
}
|
|
2559
3066
|
var loginCommand = new Command11("login").description("Login to Skillo platform").argument("[api-key]", "Your Skillo API key (optional, will use browser auth if not provided)").option("-u, --url <url>", "Platform URL (default: https://www.skillo.one)").option("-n, --no-browser", "Don't open browser automatically").action(async (apiKey, options) => {
|
|
2560
3067
|
const client = getApiClient();
|
|
2561
3068
|
logger_default.blank();
|
|
@@ -2578,6 +3085,7 @@ var loginCommand = new Command11("login").description("Login to Skillo platform"
|
|
|
2578
3085
|
logger_default.info(`Welcome, ${result.data.user.name}!`);
|
|
2579
3086
|
logger_default.dim(`Email: ${result.data.user.email}`);
|
|
2580
3087
|
logger_default.blank();
|
|
3088
|
+
await autoStartDaemon();
|
|
2581
3089
|
logger_default.dim("Your patterns and skills will now sync with the Skillo platform.");
|
|
2582
3090
|
} else {
|
|
2583
3091
|
client.clearApiKey();
|
|
@@ -2650,6 +3158,7 @@ var loginCommand = new Command11("login").description("Login to Skillo platform"
|
|
|
2650
3158
|
logger_default.dim(`Email: ${authResult.data.user.email}`);
|
|
2651
3159
|
}
|
|
2652
3160
|
logger_default.blank();
|
|
3161
|
+
await autoStartDaemon();
|
|
2653
3162
|
logger_default.dim("Your patterns and skills will now sync with the Skillo platform.");
|
|
2654
3163
|
return;
|
|
2655
3164
|
} else {
|
|
@@ -2717,8 +3226,8 @@ var whoamiCommand = new Command11("whoami").description("Show current login stat
|
|
|
2717
3226
|
});
|
|
2718
3227
|
|
|
2719
3228
|
// src/commands/sync.ts
|
|
2720
|
-
import { existsSync as
|
|
2721
|
-
import { join as
|
|
3229
|
+
import { existsSync as existsSync13, writeFileSync as writeFileSync7, mkdirSync as mkdirSync2 } from "fs";
|
|
3230
|
+
import { join as join6 } from "path";
|
|
2722
3231
|
import { Command as Command12 } from "commander";
|
|
2723
3232
|
var syncCommand = new Command12("sync").description("Sync data with Skillo platform").option("--push", "Push local data to platform").option("--pull", "Pull data from platform").option("--patterns", "Sync patterns only").option("--skills", "Sync skills only").option("--commands", "Sync commands only").action(
|
|
2724
3233
|
async (options) => {
|
|
@@ -2734,7 +3243,7 @@ var syncCommand = new Command12("sync").description("Sync data with Skillo platf
|
|
|
2734
3243
|
const syncSkills = options.skills || !options.patterns && !options.skills && !options.commands;
|
|
2735
3244
|
const syncCommands = options.commands || !options.patterns && !options.skills && !options.commands;
|
|
2736
3245
|
const dbPath = getDbPath();
|
|
2737
|
-
if (!
|
|
3246
|
+
if (!existsSync13(dbPath)) {
|
|
2738
3247
|
logger_default.error("Database not found. Run 'skillo init' first.");
|
|
2739
3248
|
return;
|
|
2740
3249
|
}
|
|
@@ -2836,21 +3345,38 @@ async function pullSkills(skills) {
|
|
|
2836
3345
|
let downloaded = 0;
|
|
2837
3346
|
for (const skill of skills) {
|
|
2838
3347
|
if (!skill.content) continue;
|
|
2839
|
-
const skillDir =
|
|
2840
|
-
const skillFile =
|
|
2841
|
-
if (!
|
|
2842
|
-
|
|
3348
|
+
const skillDir = join6(skillsDir, skill.slug);
|
|
3349
|
+
const skillFile = join6(skillDir, "SKILL.md");
|
|
3350
|
+
if (!existsSync13(skillDir)) {
|
|
3351
|
+
mkdirSync2(skillDir, { recursive: true });
|
|
2843
3352
|
}
|
|
2844
|
-
|
|
3353
|
+
writeFileSync7(skillFile, skill.content, "utf-8");
|
|
2845
3354
|
downloaded++;
|
|
2846
3355
|
logger_default.dim(` Downloaded: ${skill.name}`);
|
|
2847
3356
|
}
|
|
2848
3357
|
logger_default.success(`Downloaded ${downloaded} skills to ~/.claude/skills/`);
|
|
2849
3358
|
}
|
|
2850
3359
|
|
|
3360
|
+
// src/commands/tray.ts
|
|
3361
|
+
import { Command as Command13 } from "commander";
|
|
3362
|
+
var trayCommand = new Command13("tray").description("Start the system tray icon").action(async () => {
|
|
3363
|
+
try {
|
|
3364
|
+
const { startTray } = await import("./tray-62I5KIFE.js");
|
|
3365
|
+
await startTray();
|
|
3366
|
+
} catch (error) {
|
|
3367
|
+
if (error instanceof Error && error.message.includes("Cannot find module")) {
|
|
3368
|
+
logger_default.error("System tray not available. The systray2 package may not be installed.");
|
|
3369
|
+
logger_default.dim("Try: npm install -g skillo");
|
|
3370
|
+
} else {
|
|
3371
|
+
logger_default.error(`Tray error: ${error instanceof Error ? error.message : error}`);
|
|
3372
|
+
}
|
|
3373
|
+
process.exit(1);
|
|
3374
|
+
}
|
|
3375
|
+
});
|
|
3376
|
+
|
|
2851
3377
|
// src/cli.ts
|
|
2852
|
-
var VERSION = "0.1
|
|
2853
|
-
var program = new
|
|
3378
|
+
var VERSION = "0.2.1";
|
|
3379
|
+
var program = new Command14();
|
|
2854
3380
|
program.name("skillo").description(
|
|
2855
3381
|
`Skillo - Learn workflows by observation, not explanation.
|
|
2856
3382
|
|
|
@@ -2884,6 +3410,8 @@ program.addCommand(claudeCommand);
|
|
|
2884
3410
|
program.addCommand(projectCommand);
|
|
2885
3411
|
program.addCommand(trackCommand);
|
|
2886
3412
|
program.addCommand(untrackCommand);
|
|
3413
|
+
program.addCommand(serviceCommand);
|
|
3414
|
+
program.addCommand(trayCommand);
|
|
2887
3415
|
program.command("version").description("Show version information").action(() => {
|
|
2888
3416
|
console.log(`Skillo v${VERSION}`);
|
|
2889
3417
|
console.log("Autonomous workflow learning & skill generation system");
|