switchly 1.0.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 (54) hide show
  1. package/README.md +116 -0
  2. package/dist/commands/add.d.ts +11 -0
  3. package/dist/commands/add.d.ts.map +1 -0
  4. package/dist/commands/add.js +211 -0
  5. package/dist/commands/add.js.map +1 -0
  6. package/dist/commands/keys.d.ts +2 -0
  7. package/dist/commands/keys.d.ts.map +1 -0
  8. package/dist/commands/keys.js +40 -0
  9. package/dist/commands/keys.js.map +1 -0
  10. package/dist/commands/list.d.ts +2 -0
  11. package/dist/commands/list.d.ts.map +1 -0
  12. package/dist/commands/list.js +38 -0
  13. package/dist/commands/list.js.map +1 -0
  14. package/dist/commands/remove.d.ts +7 -0
  15. package/dist/commands/remove.d.ts.map +1 -0
  16. package/dist/commands/remove.js +85 -0
  17. package/dist/commands/remove.js.map +1 -0
  18. package/dist/commands/status.d.ts +6 -0
  19. package/dist/commands/status.d.ts.map +1 -0
  20. package/dist/commands/status.js +92 -0
  21. package/dist/commands/status.js.map +1 -0
  22. package/dist/commands/switch.d.ts +2 -0
  23. package/dist/commands/switch.d.ts.map +1 -0
  24. package/dist/commands/switch.js +108 -0
  25. package/dist/commands/switch.js.map +1 -0
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.d.ts.map +1 -0
  28. package/dist/index.js +55 -0
  29. package/dist/index.js.map +1 -0
  30. package/dist/services/gh.d.ts +15 -0
  31. package/dist/services/gh.d.ts.map +1 -0
  32. package/dist/services/gh.js +89 -0
  33. package/dist/services/gh.js.map +1 -0
  34. package/dist/services/git.d.ts +23 -0
  35. package/dist/services/git.d.ts.map +1 -0
  36. package/dist/services/git.js +130 -0
  37. package/dist/services/git.js.map +1 -0
  38. package/dist/services/ssh.d.ts +16 -0
  39. package/dist/services/ssh.d.ts.map +1 -0
  40. package/dist/services/ssh.js +180 -0
  41. package/dist/services/ssh.js.map +1 -0
  42. package/dist/services/store.d.ts +12 -0
  43. package/dist/services/store.d.ts.map +1 -0
  44. package/dist/services/store.js +58 -0
  45. package/dist/services/store.js.map +1 -0
  46. package/dist/types/index.d.ts +24 -0
  47. package/dist/types/index.d.ts.map +1 -0
  48. package/dist/types/index.js +2 -0
  49. package/dist/types/index.js.map +1 -0
  50. package/dist/utils/logger.d.ts +18 -0
  51. package/dist/utils/logger.d.ts.map +1 -0
  52. package/dist/utils/logger.js +23 -0
  53. package/dist/utils/logger.js.map +1 -0
  54. package/package.json +50 -0
@@ -0,0 +1,180 @@
1
+ import { exec } from "child_process";
2
+ import { promisify } from "util";
3
+ import { readFile, writeFile, access, mkdir } from "fs/promises";
4
+ import { homedir } from "os";
5
+ import { join } from "path";
6
+ const execAsync = promisify(exec);
7
+ const SSH_DIR = join(homedir(), ".ssh");
8
+ const SSH_CONFIG_PATH = join(SSH_DIR, "config");
9
+ export async function ensureSSHDir() {
10
+ try {
11
+ await access(SSH_DIR);
12
+ }
13
+ catch {
14
+ await mkdir(SSH_DIR, { mode: 0o700 });
15
+ }
16
+ }
17
+ export async function generateSSHKey(email, profileName) {
18
+ await ensureSSHDir();
19
+ const keyName = `switchly_${profileName}`;
20
+ const privateKeyPath = join(SSH_DIR, keyName);
21
+ const publicKeyPath = `${privateKeyPath}.pub`;
22
+ // Generate ED25519 key (more secure and shorter than RSA)
23
+ await execAsync(`ssh-keygen -t ed25519 -C "${email}" -f "${privateKeyPath}" -N ""`);
24
+ const publicKey = await readFile(publicKeyPath, "utf-8");
25
+ return {
26
+ privateKey: privateKeyPath,
27
+ publicKey: publicKey.trim(),
28
+ };
29
+ }
30
+ export async function readSSHConfig() {
31
+ try {
32
+ return await readFile(SSH_CONFIG_PATH, "utf-8");
33
+ }
34
+ catch {
35
+ return "";
36
+ }
37
+ }
38
+ export async function writeSSHConfig(content) {
39
+ await ensureSSHDir();
40
+ await writeFile(SSH_CONFIG_PATH, content, { mode: 0o600 });
41
+ }
42
+ export function parseSSHConfig(content) {
43
+ const configs = new Map();
44
+ const lines = content.split("\n");
45
+ let currentHost = null;
46
+ let currentConfig = {};
47
+ for (const line of lines) {
48
+ const trimmed = line.trim();
49
+ if (!trimmed || trimmed.startsWith("#"))
50
+ continue;
51
+ const match = trimmed.match(/^(\S+)\s+(.+)$/);
52
+ if (!match)
53
+ continue;
54
+ const [, key, value] = match;
55
+ const keyLower = key.toLowerCase();
56
+ if (keyLower === "host") {
57
+ // Save previous config
58
+ if (currentHost && currentConfig.hostName) {
59
+ configs.set(currentHost, currentConfig);
60
+ }
61
+ currentHost = value;
62
+ currentConfig = { host: value };
63
+ }
64
+ else if (currentHost) {
65
+ switch (keyLower) {
66
+ case "hostname":
67
+ currentConfig.hostName = value;
68
+ break;
69
+ case "user":
70
+ currentConfig.user = value;
71
+ break;
72
+ case "identityfile":
73
+ currentConfig.identityFile = value.replace("~", homedir());
74
+ break;
75
+ case "identitiesonly":
76
+ currentConfig.identitiesOnly = value.toLowerCase() === "yes";
77
+ break;
78
+ }
79
+ }
80
+ }
81
+ // Save last config
82
+ if (currentHost && currentConfig.hostName) {
83
+ configs.set(currentHost, currentConfig);
84
+ }
85
+ return configs;
86
+ }
87
+ export function formatSSHConfig(config) {
88
+ const lines = [
89
+ `Host ${config.host}`,
90
+ ` HostName ${config.hostName}`,
91
+ ` User ${config.user}`,
92
+ ` IdentityFile ${config.identityFile}`,
93
+ ];
94
+ if (config.identitiesOnly) {
95
+ lines.push(" IdentitiesOnly yes");
96
+ }
97
+ return lines.join("\n");
98
+ }
99
+ export async function addSSHHostConfig(profileName, privateKeyPath) {
100
+ const hostAlias = `github.com-${profileName}`;
101
+ const newConfig = {
102
+ host: hostAlias,
103
+ hostName: "github.com",
104
+ user: "git",
105
+ identityFile: privateKeyPath,
106
+ identitiesOnly: true,
107
+ };
108
+ let currentContent = await readSSHConfig();
109
+ const configs = parseSSHConfig(currentContent);
110
+ // Check if config already exists
111
+ if (configs.has(hostAlias)) {
112
+ // Update existing config
113
+ const lines = currentContent.split("\n");
114
+ const newLines = [];
115
+ let skipUntilNextHost = false;
116
+ for (const line of lines) {
117
+ if (line.trim().toLowerCase().startsWith("host ")) {
118
+ if (line.includes(hostAlias)) {
119
+ skipUntilNextHost = true;
120
+ newLines.push(formatSSHConfig(newConfig));
121
+ }
122
+ else {
123
+ skipUntilNextHost = false;
124
+ }
125
+ }
126
+ if (!skipUntilNextHost) {
127
+ newLines.push(line);
128
+ }
129
+ }
130
+ await writeSSHConfig(newLines.join("\n"));
131
+ }
132
+ else {
133
+ // Add new config
134
+ const newConfigStr = formatSSHConfig(newConfig);
135
+ const separator = currentContent.trim() ? "\n\n" : "";
136
+ await writeSSHConfig(currentContent.trim() + separator + newConfigStr + "\n");
137
+ }
138
+ return hostAlias;
139
+ }
140
+ export async function removeSSHHostConfig(profileName) {
141
+ const hostAlias = `github.com-${profileName}`;
142
+ const currentContent = await readSSHConfig();
143
+ const lines = currentContent.split("\n");
144
+ const newLines = [];
145
+ let skipUntilNextHost = false;
146
+ for (const line of lines) {
147
+ if (line.trim().toLowerCase().startsWith("host ")) {
148
+ skipUntilNextHost = line.includes(hostAlias);
149
+ }
150
+ if (!skipUntilNextHost) {
151
+ newLines.push(line);
152
+ }
153
+ }
154
+ await writeSSHConfig(newLines.join("\n"));
155
+ }
156
+ export async function testSSHConnection(host) {
157
+ try {
158
+ await execAsync(`ssh -T -o StrictHostKeyChecking=no git@${host} 2>&1`, {
159
+ timeout: 10000,
160
+ });
161
+ return true;
162
+ }
163
+ catch (error) {
164
+ // GitHub returns exit code 1 even on success with message "Hi username!"
165
+ return error.stdout?.includes("Hi ") || error.stderr?.includes("Hi ");
166
+ }
167
+ }
168
+ export function getSSHKeyPath(profileName) {
169
+ return join(SSH_DIR, `switchly_${profileName}`);
170
+ }
171
+ export async function sshKeyExists(profileName) {
172
+ try {
173
+ await access(getSSHKeyPath(profileName));
174
+ return true;
175
+ }
176
+ catch {
177
+ return false;
178
+ }
179
+ }
180
+ //# sourceMappingURL=ssh.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ssh.js","sourceRoot":"","sources":["../../src/services/ssh.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import type { Profile } from "../types/index.js";
2
+ export declare function getProfiles(): Profile[];
3
+ export declare function getActiveProfileName(): string | null;
4
+ export declare function getActiveProfile(): Profile | null;
5
+ export declare function getProfile(name: string): Profile | null;
6
+ export declare function profileExists(name: string): boolean;
7
+ export declare function addProfile(profile: Profile): void;
8
+ export declare function updateProfile(name: string, updates: Partial<Profile>): void;
9
+ export declare function removeProfile(name: string): boolean;
10
+ export declare function setActiveProfile(name: string | null): void;
11
+ export declare function getConfigPath(): string;
12
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../src/services/store.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAU,MAAM,mBAAmB,CAAC;AAUzD,wBAAgB,WAAW,IAAI,OAAO,EAAE,CAEvC;AAED,wBAAgB,oBAAoB,IAAI,MAAM,GAAG,IAAI,CAEpD;AAED,wBAAgB,gBAAgB,IAAI,OAAO,GAAG,IAAI,CAIjD;AAED,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,GAAG,IAAI,CAEvD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAEnD;AAED,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAIjD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,GAAG,IAAI,CAO3E;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAWnD;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAE1D;AAED,wBAAgB,aAAa,IAAI,MAAM,CAEtC"}
@@ -0,0 +1,58 @@
1
+ import Conf from "conf";
2
+ const config = new Conf({
3
+ projectName: "switchly",
4
+ defaults: {
5
+ profiles: [],
6
+ activeProfile: null,
7
+ },
8
+ });
9
+ export function getProfiles() {
10
+ return config.get("profiles");
11
+ }
12
+ export function getActiveProfileName() {
13
+ return config.get("activeProfile");
14
+ }
15
+ export function getActiveProfile() {
16
+ const activeName = getActiveProfileName();
17
+ if (!activeName)
18
+ return null;
19
+ return getProfiles().find((p) => p.name === activeName) || null;
20
+ }
21
+ export function getProfile(name) {
22
+ return getProfiles().find((p) => p.name === name) || null;
23
+ }
24
+ export function profileExists(name) {
25
+ return getProfiles().some((p) => p.name === name);
26
+ }
27
+ export function addProfile(profile) {
28
+ const profiles = getProfiles();
29
+ profiles.push(profile);
30
+ config.set("profiles", profiles);
31
+ }
32
+ export function updateProfile(name, updates) {
33
+ const profiles = getProfiles();
34
+ const index = profiles.findIndex((p) => p.name === name);
35
+ if (index !== -1) {
36
+ profiles[index] = { ...profiles[index], ...updates };
37
+ config.set("profiles", profiles);
38
+ }
39
+ }
40
+ export function removeProfile(name) {
41
+ const profiles = getProfiles();
42
+ const filtered = profiles.filter((p) => p.name !== name);
43
+ if (filtered.length === profiles.length)
44
+ return false;
45
+ config.set("profiles", filtered);
46
+ // Clear active profile if it was removed
47
+ if (getActiveProfileName() === name) {
48
+ config.set("activeProfile", null);
49
+ }
50
+ return true;
51
+ }
52
+ export function setActiveProfile(name) {
53
+ config.set("activeProfile", name);
54
+ }
55
+ export function getConfigPath() {
56
+ return config.path;
57
+ }
58
+ //# sourceMappingURL=store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/services/store.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,MAAM,MAAM,GAAG,IAAI,IAAI,CAAS;IAC9B,WAAW,EAAE,UAAU;IACvB,QAAQ,EAAE;QACR,QAAQ,EAAE,EAAE;QACZ,aAAa,EAAE,IAAI;KACpB;CACF,CAAC,CAAC;AAEH,MAAM,UAAU,WAAW;IACzB,OAAO,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;AAChC,CAAC;AAED,MAAM,UAAU,oBAAoB;IAClC,OAAO,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;AACrC,CAAC;AAED,MAAM,UAAU,gBAAgB;IAC9B,MAAM,UAAU,GAAG,oBAAoB,EAAE,CAAC;IAC1C,IAAI,CAAC,UAAU;QAAE,OAAO,IAAI,CAAC;IAC7B,OAAO,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,CAAC,IAAI,IAAI,CAAC;AAClE,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,IAAY;IACrC,OAAO,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,IAAI,IAAI,CAAC;AAC5D,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,OAAO,WAAW,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,OAAgB;IACzC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,QAAQ,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;IACvB,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY,EAAE,OAAyB;IACnE,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,KAAK,GAAG,QAAQ,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACzD,IAAI,KAAK,KAAK,CAAC,CAAC,EAAE,CAAC;QACjB,QAAQ,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,EAAE,GAAG,OAAO,EAAE,CAAC;QACrD,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IACnC,CAAC;AACH,CAAC;AAED,MAAM,UAAU,aAAa,CAAC,IAAY;IACxC,MAAM,QAAQ,GAAG,WAAW,EAAE,CAAC;IAC/B,MAAM,QAAQ,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACzD,IAAI,QAAQ,CAAC,MAAM,KAAK,QAAQ,CAAC,MAAM;QAAE,OAAO,KAAK,CAAC;IACtD,MAAM,CAAC,GAAG,CAAC,UAAU,EAAE,QAAQ,CAAC,CAAC;IAEjC,yCAAyC;IACzC,IAAI,oBAAoB,EAAE,KAAK,IAAI,EAAE,CAAC;QACpC,MAAM,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;IACpC,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,IAAmB;IAClD,MAAM,CAAC,GAAG,CAAC,eAAe,EAAE,IAAI,CAAC,CAAC;AACpC,CAAC;AAED,MAAM,UAAU,aAAa;IAC3B,OAAO,MAAM,CAAC,IAAI,CAAC;AACrB,CAAC"}
@@ -0,0 +1,24 @@
1
+ export interface Profile {
2
+ name: string;
3
+ gitName: string;
4
+ gitEmail: string;
5
+ sshKeyPath: string;
6
+ sshHost: string;
7
+ ghToken?: string;
8
+ signingKey?: string;
9
+ signingFormat?: "gpg" | "ssh";
10
+ signCommits?: boolean;
11
+ createdAt: string;
12
+ }
13
+ export interface Config {
14
+ profiles: Profile[];
15
+ activeProfile: string | null;
16
+ }
17
+ export interface SSHConfig {
18
+ host: string;
19
+ hostName: string;
20
+ user: string;
21
+ identityFile: string;
22
+ identitiesOnly: boolean;
23
+ }
24
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,OAAO;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,CAAC,EAAE,MAAM,CAAC;IAEjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IAC9B,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,MAAM;IACrB,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;CAC9B;AAED,MAAM,WAAW,SAAS;IACxB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,MAAM,CAAC;IACb,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,OAAO,CAAC;CACzB"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":""}
@@ -0,0 +1,18 @@
1
+ export declare const log: {
2
+ info: (msg: string) => void;
3
+ success: (msg: string) => void;
4
+ warning: (msg: string) => void;
5
+ error: (msg: string) => void;
6
+ dim: (msg: string) => void;
7
+ title: (msg: string) => void;
8
+ profile: (name: string, active: boolean) => string;
9
+ };
10
+ export declare const colors: {
11
+ primary: import("chalk").ChalkInstance;
12
+ success: import("chalk").ChalkInstance;
13
+ warning: import("chalk").ChalkInstance;
14
+ error: import("chalk").ChalkInstance;
15
+ dim: import("chalk").ChalkInstance;
16
+ bold: import("chalk").ChalkInstance;
17
+ };
18
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,GAAG;gBACF,MAAM;mBACH,MAAM;mBACN,MAAM;iBACR,MAAM;eACR,MAAM;iBACJ,MAAM;oBACH,MAAM,UAAU,OAAO;CAKxC,CAAC;AAEF,eAAO,MAAM,MAAM;;;;;;;CAOlB,CAAC"}
@@ -0,0 +1,23 @@
1
+ import chalk from "chalk";
2
+ export const log = {
3
+ info: (msg) => console.log(chalk.blue("ℹ"), msg),
4
+ success: (msg) => console.log(chalk.green("✔"), msg),
5
+ warning: (msg) => console.log(chalk.yellow("⚠"), msg),
6
+ error: (msg) => console.log(chalk.red("✖"), msg),
7
+ dim: (msg) => console.log(chalk.dim(msg)),
8
+ title: (msg) => console.log(chalk.bold.cyan(`\n${msg}\n`)),
9
+ profile: (name, active) => {
10
+ const indicator = active ? chalk.green("● ") : chalk.dim("○ ");
11
+ const nameStr = active ? chalk.bold.green(name) : chalk.white(name);
12
+ return `${indicator}${nameStr}`;
13
+ },
14
+ };
15
+ export const colors = {
16
+ primary: chalk.cyan,
17
+ success: chalk.green,
18
+ warning: chalk.yellow,
19
+ error: chalk.red,
20
+ dim: chalk.dim,
21
+ bold: chalk.bold,
22
+ };
23
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,MAAM,CAAC,MAAM,GAAG,GAAG;IACjB,IAAI,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;IACxD,OAAO,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;IAC5D,OAAO,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;IAC7D,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,GAAG,CAAC;IACxD,GAAG,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACjD,KAAK,EAAE,CAAC,GAAW,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC,CAAC;IAClE,OAAO,EAAE,CAAC,IAAY,EAAE,MAAe,EAAE,EAAE;QACzC,MAAM,SAAS,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/D,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;QACpE,OAAO,GAAG,SAAS,GAAG,OAAO,EAAE,CAAC;IAClC,CAAC;CACF,CAAC;AAEF,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,OAAO,EAAE,KAAK,CAAC,IAAI;IACnB,OAAO,EAAE,KAAK,CAAC,KAAK;IACpB,OAAO,EAAE,KAAK,CAAC,MAAM;IACrB,KAAK,EAAE,KAAK,CAAC,GAAG;IAChB,GAAG,EAAE,KAAK,CAAC,GAAG;IACd,IAAI,EAAE,KAAK,CAAC,IAAI;CACjB,CAAC"}
package/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "switchly",
3
+ "version": "1.0.0",
4
+ "description": "Switch between multiple GitHub accounts easily - manage SSH keys, Git config, and GitHub CLI auth",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "switchly": "./dist/index.js"
9
+ },
10
+ "scripts": {
11
+ "build": "tsc",
12
+ "dev": "tsc --watch",
13
+ "start": "node dist/index.js",
14
+ "prepublishOnly": "npm run build"
15
+ },
16
+ "keywords": [
17
+ "github",
18
+ "git",
19
+ "ssh",
20
+ "account",
21
+ "switch",
22
+ "profile",
23
+ "cli",
24
+ "multi-account"
25
+ ],
26
+ "author": "Yasser",
27
+ "license": "MIT",
28
+ "dependencies": {
29
+ "chalk": "^5.3.0",
30
+ "commander": "^12.1.0",
31
+ "conf": "^13.0.1",
32
+ "inquirer": "^9.3.7",
33
+ "ora": "^8.1.1"
34
+ },
35
+ "devDependencies": {
36
+ "@types/inquirer": "^9.0.7",
37
+ "@types/node": "^22.10.0",
38
+ "typescript": "^5.7.2"
39
+ },
40
+ "engines": {
41
+ "node": ">=18.0.0"
42
+ },
43
+ "files": [
44
+ "dist"
45
+ ],
46
+ "repository": {
47
+ "type": "git",
48
+ "url": "https://github.com/yasser/switchly.git"
49
+ }
50
+ }