@hallaxius/forge 0.1.2 → 0.1.4

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 (50) hide show
  1. package/README.md +160 -158
  2. package/bin/forge.js +2 -2
  3. package/dist/cli.js +31602 -9895
  4. package/package.json +75 -72
  5. package/src/cli.ts +80 -78
  6. package/src/commands/account.ts +80 -0
  7. package/src/commands/alias.ts +66 -66
  8. package/src/commands/branch.ts +46 -46
  9. package/src/commands/ci.ts +28 -0
  10. package/src/commands/clone.ts +100 -100
  11. package/src/commands/commit.ts +88 -93
  12. package/src/commands/config.ts +47 -48
  13. package/src/commands/diff.ts +26 -26
  14. package/src/commands/fetch.ts +20 -20
  15. package/src/commands/help.ts +58 -58
  16. package/src/commands/init.ts +32 -37
  17. package/src/commands/issue.ts +63 -0
  18. package/src/commands/log.ts +29 -29
  19. package/src/commands/merge.ts +37 -37
  20. package/src/commands/pr.ts +65 -0
  21. package/src/commands/push.ts +35 -35
  22. package/src/commands/release.ts +26 -0
  23. package/src/commands/remote.ts +107 -107
  24. package/src/commands/reset.ts +30 -30
  25. package/src/commands/setup.ts +93 -95
  26. package/src/commands/stash.ts +44 -44
  27. package/src/commands/status.ts +74 -74
  28. package/src/commands/sync.ts +20 -20
  29. package/src/commands/tag.ts +41 -41
  30. package/src/commands/undo.ts +27 -27
  31. package/src/commands/version.ts +12 -12
  32. package/src/constants/colors.ts +7 -7
  33. package/src/constants/commit-types.ts +24 -24
  34. package/src/constants/messages.ts +13 -23
  35. package/src/lib/auth.ts +172 -95
  36. package/src/lib/config.ts +108 -99
  37. package/src/lib/git.ts +543 -382
  38. package/src/lib/github.ts +202 -0
  39. package/src/lib/logger.ts +18 -31
  40. package/src/lib/ui.ts +122 -156
  41. package/src/lib/validators.ts +16 -27
  42. package/src/templates/commit-types.json +9 -9
  43. package/src/utils/files.ts +21 -21
  44. package/src/utils/strings.ts +19 -19
  45. package/src/version.const.ts +1 -1
  46. package/src/commands/archive.ts +0 -35
  47. package/src/commands/bisect.ts +0 -102
  48. package/src/commands/cherry-pick.ts +0 -57
  49. package/src/commands/clean.ts +0 -76
  50. package/src/commands/worktree.ts +0 -92
@@ -1,107 +1,107 @@
1
- import type { Command } from "commander";
2
- import * as git from "../lib/git.js";
3
- import { error, info, success } from "../lib/logger.js";
4
- import { confirm, createTable } from "../lib/ui.js";
5
-
6
- export default function register(program: Command): void {
7
- const remote = program.command("remote").description("Manage remotes");
8
-
9
- remote
10
- .command("add <name> <url>")
11
- .description("Add a new remote")
12
- .action(async (name, url) => {
13
- try {
14
- await git.remoteAdd(name, url);
15
- success(`Remote '${name}' added (${url}).`);
16
- } catch (err) {
17
- error(
18
- `Failed to add remote: ${err instanceof Error ? err.message : String(err)}`,
19
- );
20
- }
21
- });
22
-
23
- remote
24
- .command("remove <name>")
25
- .description("Remove a remote")
26
- .action(async (name) => {
27
- try {
28
- const confirmed = await confirm(
29
- `Are you sure you want to remove remote '${name}'?`,
30
- false,
31
- );
32
-
33
- if (!confirmed) {
34
- info("Canceled.");
35
- return;
36
- }
37
-
38
- await git.remoteRemove(name);
39
- success(`Remote '${name}' removed.`);
40
- } catch (err) {
41
- error(
42
- `Failed to remove remote: ${err instanceof Error ? err.message : String(err)}`,
43
- );
44
- }
45
- });
46
-
47
- remote
48
- .command("set-url <name> <new-url>")
49
- .description("Change a remote URL")
50
- .action(async (name, newUrl) => {
51
- try {
52
- await git.remoteSetUrl(name, newUrl);
53
- success(`Remote '${name}' URL updated to ${newUrl}.`);
54
- } catch (err) {
55
- error(
56
- `Failed to update remote URL: ${err instanceof Error ? err.message : String(err)}`,
57
- );
58
- }
59
- });
60
-
61
- remote
62
- .command("rename <old-name> <new-name>")
63
- .description("Rename a remote")
64
- .action(async (oldName, newName) => {
65
- try {
66
- await git.remoteRename(oldName, newName);
67
- success(`Remote '${oldName}' renamed to '${newName}'.`);
68
- } catch (err) {
69
- error(
70
- `Failed to rename remote: ${err instanceof Error ? err.message : String(err)}`,
71
- );
72
- }
73
- });
74
-
75
- remote
76
- .command("get-url <name>")
77
- .description("Get the URL of a remote")
78
- .action(async (name) => {
79
- try {
80
- const url = await git.remoteGetUrl(name);
81
- info(`Remote '${name}': ${url}`);
82
- } catch (err) {
83
- error(
84
- `Failed to get remote URL: ${err instanceof Error ? err.message : String(err)}`,
85
- );
86
- }
87
- });
88
-
89
- remote.action(async () => {
90
- try {
91
- const remotes = await git.remoteList();
92
-
93
- if (remotes.length === 0) {
94
- info("No remotes configured.");
95
- return;
96
- }
97
-
98
- const rows = remotes.map((r) => [r.name, r.url]);
99
- info("Remotes:");
100
- console.log(createTable(["Name", "URL"], rows));
101
- } catch (err) {
102
- error(
103
- `Failed to list remotes: ${err instanceof Error ? err.message : String(err)}`,
104
- );
105
- }
106
- });
107
- }
1
+ import type { Command } from "commander";
2
+ import * as git from "../lib/git.js";
3
+ import { error, text } from "../lib/logger.js";
4
+ import { confirm, createTable } from "../lib/ui.js";
5
+
6
+ export default function register(program: Command): void {
7
+ const remote = program.command("remote").description("Manage remotes");
8
+
9
+ remote
10
+ .command("add <name> <url>")
11
+ .description("Add a new remote")
12
+ .action(async (name, url) => {
13
+ try {
14
+ await git.remoteAdd(name, url);
15
+ text(`Remote '${name}' added (${url}).`);
16
+ } catch (err) {
17
+ error(
18
+ `Failed to add remote: ${err instanceof Error ? err.message : String(err)}`,
19
+ );
20
+ }
21
+ });
22
+
23
+ remote
24
+ .command("remove <name>")
25
+ .description("Remove a remote")
26
+ .action(async (name) => {
27
+ try {
28
+ const confirmed = await confirm(
29
+ `Are you sure you want to remove remote '${name}'?`,
30
+ false,
31
+ );
32
+
33
+ if (!confirmed) {
34
+ text("Canceled.");
35
+ return;
36
+ }
37
+
38
+ await git.remoteRemove(name);
39
+ text(`Remote '${name}' removed.`);
40
+ } catch (err) {
41
+ error(
42
+ `Failed to remove remote: ${err instanceof Error ? err.message : String(err)}`,
43
+ );
44
+ }
45
+ });
46
+
47
+ remote
48
+ .command("set-url <name> <new-url>")
49
+ .description("Change a remote URL")
50
+ .action(async (name, newUrl) => {
51
+ try {
52
+ await git.remoteSetUrl(name, newUrl);
53
+ text(`Remote '${name}' URL updated to ${newUrl}.`);
54
+ } catch (err) {
55
+ error(
56
+ `Failed to update remote URL: ${err instanceof Error ? err.message : String(err)}`,
57
+ );
58
+ }
59
+ });
60
+
61
+ remote
62
+ .command("rename <old-name> <new-name>")
63
+ .description("Rename a remote")
64
+ .action(async (oldName, newName) => {
65
+ try {
66
+ await git.remoteRename(oldName, newName);
67
+ text(`Remote '${oldName}' renamed to '${newName}'.`);
68
+ } catch (err) {
69
+ error(
70
+ `Failed to rename remote: ${err instanceof Error ? err.message : String(err)}`,
71
+ );
72
+ }
73
+ });
74
+
75
+ remote
76
+ .command("get-url <name>")
77
+ .description("Get the URL of a remote")
78
+ .action(async (name) => {
79
+ try {
80
+ const url = await git.remoteGetUrl(name);
81
+ text(`Remote '${name}': ${url}`);
82
+ } catch (err) {
83
+ error(
84
+ `Failed to get remote URL: ${err instanceof Error ? err.message : String(err)}`,
85
+ );
86
+ }
87
+ });
88
+
89
+ remote.action(async () => {
90
+ try {
91
+ const remotes = await git.remoteList();
92
+
93
+ if (remotes.length === 0) {
94
+ text("No remotes configured.");
95
+ return;
96
+ }
97
+
98
+ const rows = remotes.map((r) => [r.name, r.url]);
99
+ text("Remotes:");
100
+ text(createTable(["Name", "URL"], rows));
101
+ } catch (err) {
102
+ error(
103
+ `Failed to list remotes: ${err instanceof Error ? err.message : String(err)}`,
104
+ );
105
+ }
106
+ });
107
+ }
@@ -1,30 +1,30 @@
1
- import type { Command } from "commander";
2
- import { ConfigManager } from "../lib/config.js";
3
- import { error, info, success, warning } from "../lib/logger.js";
4
- import { confirm } from "../lib/ui.js";
5
-
6
- export default function register(program: Command): void {
7
- program
8
- .command("reset")
9
- .description("Reset all configuration")
10
- .action(async () => {
11
- try {
12
- warning("This will delete all Forge configuration.");
13
- const proceed = await confirm(
14
- "Are you sure? This will delete all configuration",
15
- );
16
- if (!proceed) {
17
- info("Reset cancelled.");
18
- return;
19
- }
20
-
21
- const config = new ConfigManager();
22
- config.clear();
23
- success("Configuration reset successfully.");
24
- } catch (err) {
25
- error(
26
- `Reset failed: ${err instanceof Error ? err.message : String(err)}`,
27
- );
28
- }
29
- });
30
- }
1
+ import type { Command } from "commander";
2
+ import { ConfigManager } from "../lib/config.js";
3
+ import { error, text, warning } from "../lib/logger.js";
4
+ import { confirm } from "../lib/ui.js";
5
+
6
+ export default function register(program: Command): void {
7
+ program
8
+ .command("reset")
9
+ .description("Reset all configuration")
10
+ .action(async () => {
11
+ try {
12
+ warning("This will delete all Forge configuration.");
13
+ const proceed = await confirm(
14
+ "Are you sure? This will delete all configuration",
15
+ );
16
+ if (!proceed) {
17
+ text("Reset cancelled.");
18
+ return;
19
+ }
20
+
21
+ const config = new ConfigManager();
22
+ config.clear();
23
+ text("Configuration reset successfully.");
24
+ } catch (err) {
25
+ error(
26
+ `Reset failed: ${err instanceof Error ? err.message : String(err)}`,
27
+ );
28
+ }
29
+ });
30
+ }
@@ -1,95 +1,93 @@
1
- import type { Command } from "commander";
2
- import { encryptToken } from "../lib/auth.js";
3
- import { ConfigManager } from "../lib/config.js";
4
- import { error, info, newline, success, warning } from "../lib/logger.js";
5
- import { confirm, input, password, showBox } from "../lib/ui.js";
6
- import {
7
- validateEmail,
8
- validateGitHubToken,
9
- validateGitInstalled,
10
- } from "../lib/validators.js";
11
-
12
- export default function register(program: Command): void {
13
- program
14
- .command("setup")
15
- .description("Interactive setup wizard")
16
- .action(async () => {
17
- try {
18
- const config = new ConfigManager();
19
-
20
- if (config.isConfigured()) {
21
- warning("Forge is already configured. You are about to reconfigure.");
22
- const proceed = await confirm("Continue with reconfiguration?");
23
- if (!proceed) {
24
- info("Setup cancelled.");
25
- return;
26
- }
27
- }
28
-
29
- const gitInstalled = await validateGitInstalled();
30
- if (!gitInstalled) {
31
- error("Git is not installed. Please install Git first.");
32
- return;
33
- }
34
-
35
- newline();
36
- info("Starting setup...");
37
- newline();
38
-
39
- const name = await input("Your name");
40
-
41
- let email = await input("Your email");
42
- while (!validateEmail(email)) {
43
- error("Invalid email format.");
44
- email = await input("Your email");
45
- }
46
-
47
- let token = await input("GitHub token (optional)");
48
- if (token && !validateGitHubToken(token)) {
49
- warning("Token format looks unusual. It will be saved as-is.");
50
- }
51
-
52
- if (token) {
53
- const encrypt = await confirm(
54
- "Encrypt token with a master password?",
55
- false,
56
- );
57
- if (encrypt) {
58
- const masterPassword = await password("Master password:");
59
- token = await encryptToken(token, masterPassword);
60
- success("Token encrypted successfully.");
61
- }
62
- }
63
-
64
- config.set("user", { name, email });
65
- config.set("github", { token });
66
- config.set("preferences", {
67
- autoPush: false,
68
- commitTemplate: "",
69
- editor: process.env.EDITOR || "vim",
70
- });
71
-
72
- newline();
73
- const tokenStatus = token
74
- ? token.includes(":")
75
- ? "Set (encrypted)"
76
- : "Set"
77
- : "Not set";
78
- showBox(
79
- "Configuration Complete",
80
- [
81
- `User: ${name} <${email}>`,
82
- `Token: ${tokenStatus}`,
83
- `Git: Installed`,
84
- `Config: ${config.getPath()}`,
85
- ].join("\n"),
86
- );
87
-
88
- success("Forge is ready to use!");
89
- } catch (err) {
90
- error(
91
- `Setup failed: ${err instanceof Error ? err.message : String(err)}`,
92
- );
93
- }
94
- });
95
- }
1
+ import type { Command } from "commander";
2
+ import { encryptToken, generateMachineKey } from "../lib/auth.js";
3
+ import { ConfigManager } from "../lib/config.js";
4
+ import { error, newline, text, warning } from "../lib/logger.js";
5
+ import { confirm, input, password } from "../lib/ui.js";
6
+ import { validateEmail } from "../lib/validators.js";
7
+
8
+ export default function register(program: Command): void {
9
+ program
10
+ .command("setup")
11
+ .description("Interactive setup wizard")
12
+ .action(async () => {
13
+ try {
14
+ const config = new ConfigManager();
15
+
16
+ if (config.isConfigured()) {
17
+ warning("Forge is already configured. You are about to reconfigure.");
18
+ const proceed = await confirm("Continue with reconfiguration?");
19
+ if (!proceed) {
20
+ text("Setup cancelled.");
21
+ return;
22
+ }
23
+ }
24
+
25
+ newline();
26
+ text("Starting setup...");
27
+ newline();
28
+
29
+ const name = await input("Your name");
30
+
31
+ let email = await input("Your email");
32
+ while (!validateEmail(email)) {
33
+ error("Invalid email format.");
34
+ email = await input("Your email");
35
+ }
36
+
37
+ const rawToken = await input(
38
+ "GitHub token (required for remote operations)",
39
+ );
40
+ if (!rawToken) {
41
+ error(
42
+ "GitHub token is required. Get one at https://github.com/settings/tokens",
43
+ );
44
+ return;
45
+ }
46
+
47
+ const useMasterPassword = await confirm(
48
+ "Protect token with a master password? (recommended)",
49
+ true,
50
+ );
51
+
52
+ const machineKey = generateMachineKey();
53
+ let hasMasterPassword = false;
54
+
55
+ if (useMasterPassword) {
56
+ const mp = await password("Master password:");
57
+ const encryptedKey = await encryptToken(machineKey, mp);
58
+ config.set("auth.machineKey", encryptedKey);
59
+ config.set("auth.hasMasterPassword", true);
60
+ hasMasterPassword = true;
61
+ } else {
62
+ config.set("auth.machineKey", machineKey);
63
+ config.set("auth.hasMasterPassword", false);
64
+ }
65
+
66
+ const encryptedToken = await encryptToken(rawToken, machineKey);
67
+ config.set("github.encryptedToken", encryptedToken);
68
+
69
+ config.set("user", { name, email });
70
+ config.set("preferences", {
71
+ autoPush: false,
72
+ commitTemplate: "",
73
+ editor: process.env.EDITOR || "vim",
74
+ });
75
+
76
+ newline();
77
+ text(
78
+ [
79
+ `User: ${name} <${email}>`,
80
+ `Token: Set (encrypted)`,
81
+ `Security: ${hasMasterPassword ? "Master password protected" : "Machine key"}`,
82
+ `Config: ${config.getPath()}`,
83
+ ].join("\n"),
84
+ );
85
+
86
+ text("Forge is ready to use!");
87
+ } catch (err) {
88
+ error(
89
+ `Setup failed: ${err instanceof Error ? err.message : String(err)}`,
90
+ );
91
+ }
92
+ });
93
+ }
@@ -1,44 +1,44 @@
1
- import type { Command } from "commander";
2
- import * as git from "../lib/git.js";
3
- import { error, info, success } from "../lib/logger.js";
4
- import { createTable, input, withSpinner } from "../lib/ui.js";
5
-
6
- export default function register(program: Command): void {
7
- program
8
- .command("stash")
9
- .description("Manage stashes")
10
- .option("--pop", "Apply and remove latest stash")
11
- .option("--list", "List all stashes")
12
- .action(async (options) => {
13
- try {
14
- if (options.pop) {
15
- const result = await withSpinner("Applying stash...", () =>
16
- git.stashPop(),
17
- );
18
- success(`Stash popped: ${result}`);
19
- return;
20
- }
21
-
22
- if (options.list) {
23
- const stashes = await git.stashList();
24
- if (stashes.length === 0) {
25
- info("No stashes found.");
26
- return;
27
- }
28
-
29
- const rows = stashes.map((s) => [String(s.index), s.description]);
30
- info("Stashes:");
31
- console.log(createTable(["#", "Description"], rows));
32
- return;
33
- }
34
-
35
- const _message = await input("Stash message (optional)");
36
- const result = await withSpinner("Stashing...", () => git.stash());
37
- success(`Stash saved: ${result}`);
38
- } catch (err) {
39
- error(
40
- `Stash operation failed: ${err instanceof Error ? err.message : String(err)}`,
41
- );
42
- }
43
- });
44
- }
1
+ import type { Command } from "commander";
2
+ import * as git from "../lib/git.js";
3
+ import { error, text } from "../lib/logger.js";
4
+ import { createTable, input, withSpinner } from "../lib/ui.js";
5
+
6
+ export default function register(program: Command): void {
7
+ program
8
+ .command("stash")
9
+ .description("Manage stashes")
10
+ .option("--pop", "Apply and remove latest stash")
11
+ .option("--list", "List all stashes")
12
+ .action(async (options) => {
13
+ try {
14
+ if (options.pop) {
15
+ const result = await withSpinner("Applying stash...", () =>
16
+ git.stashPop(),
17
+ );
18
+ text(`Stash popped: ${result}`);
19
+ return;
20
+ }
21
+
22
+ if (options.list) {
23
+ const stashes = await git.stashList();
24
+ if (stashes.length === 0) {
25
+ text("No stashes found.");
26
+ return;
27
+ }
28
+
29
+ const rows = stashes.map((s) => [String(s.index), s.description]);
30
+ text("Stashes:");
31
+ text(createTable(["#", "Description"], rows));
32
+ return;
33
+ }
34
+
35
+ const _message = await input("Stash message (optional)");
36
+ const result = await withSpinner("Stashing...", () => git.stash());
37
+ text(`Stash saved: ${result}`);
38
+ } catch (err) {
39
+ error(
40
+ `Stash operation failed: ${err instanceof Error ? err.message : String(err)}`,
41
+ );
42
+ }
43
+ });
44
+ }