@hallaxius/forge 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +159 -0
- package/bin/forge.js +2 -0
- package/dist/cli.js +35641 -0
- package/package.json +65 -0
- package/src/cli.ts +78 -0
- package/src/commands/alias.ts +66 -0
- package/src/commands/archive.ts +35 -0
- package/src/commands/bisect.ts +102 -0
- package/src/commands/branch.ts +46 -0
- package/src/commands/cherry-pick.ts +57 -0
- package/src/commands/clean.ts +76 -0
- package/src/commands/clone.ts +100 -0
- package/src/commands/commit.ts +93 -0
- package/src/commands/config.ts +48 -0
- package/src/commands/diff.ts +26 -0
- package/src/commands/fetch.ts +20 -0
- package/src/commands/help.ts +58 -0
- package/src/commands/init.ts +37 -0
- package/src/commands/log.ts +29 -0
- package/src/commands/merge.ts +37 -0
- package/src/commands/push.ts +35 -0
- package/src/commands/remote.ts +107 -0
- package/src/commands/reset.ts +30 -0
- package/src/commands/setup.ts +95 -0
- package/src/commands/stash.ts +44 -0
- package/src/commands/status.ts +74 -0
- package/src/commands/sync.ts +20 -0
- package/src/commands/tag.ts +41 -0
- package/src/commands/undo.ts +27 -0
- package/src/commands/version.ts +19 -0
- package/src/commands/worktree.ts +92 -0
- package/src/constants/colors.ts +7 -0
- package/src/constants/commit-types.ts +24 -0
- package/src/constants/messages.ts +23 -0
- package/src/lib/auth.ts +95 -0
- package/src/lib/config.ts +99 -0
- package/src/lib/git.ts +382 -0
- package/src/lib/logger.ts +31 -0
- package/src/lib/ui.ts +162 -0
- package/src/lib/validators.ts +27 -0
- package/src/templates/commit-types.json +9 -0
- package/src/utils/files.ts +21 -0
- package/src/utils/strings.ts +19 -0
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import * as git from "../lib/git.js";
|
|
3
|
+
import { info, newline, text } from "../lib/logger.js";
|
|
4
|
+
import { createTable, showHeader, showSeparator } from "../lib/ui.js";
|
|
5
|
+
|
|
6
|
+
function statusIcon(code: string): string {
|
|
7
|
+
switch (code) {
|
|
8
|
+
case "M":
|
|
9
|
+
return "M";
|
|
10
|
+
case "A":
|
|
11
|
+
return "A";
|
|
12
|
+
case "D":
|
|
13
|
+
return "D";
|
|
14
|
+
case "?":
|
|
15
|
+
return "?";
|
|
16
|
+
default:
|
|
17
|
+
return " ";
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export default function register(program: Command): void {
|
|
22
|
+
program
|
|
23
|
+
.command("status")
|
|
24
|
+
.description("Show detailed repository status")
|
|
25
|
+
.action(async () => {
|
|
26
|
+
try {
|
|
27
|
+
const status = await git.getStatus();
|
|
28
|
+
|
|
29
|
+
showHeader(`Branch: ${status.current}`);
|
|
30
|
+
if (status.tracking) {
|
|
31
|
+
info(`Tracking: ${status.tracking}`);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
if (status.ahead > 0 || status.behind > 0) {
|
|
35
|
+
const ahead = status.ahead > 0 ? `${status.ahead} ahead` : "";
|
|
36
|
+
const behind = status.behind > 0 ? `${status.behind} behind` : "";
|
|
37
|
+
const sep = ahead && behind ? " | " : "";
|
|
38
|
+
info(`${ahead}${sep}${behind}`);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
newline();
|
|
42
|
+
|
|
43
|
+
if (status.files.length > 0) {
|
|
44
|
+
info("Files:");
|
|
45
|
+
const fileRows = status.files.map((f) => [
|
|
46
|
+
f.path,
|
|
47
|
+
statusIcon(f.index),
|
|
48
|
+
statusIcon(f.working_dir),
|
|
49
|
+
]);
|
|
50
|
+
text(createTable(["File", "Index", "Working"], fileRows));
|
|
51
|
+
} else {
|
|
52
|
+
info("Working tree clean.");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
newline();
|
|
56
|
+
showSeparator();
|
|
57
|
+
newline();
|
|
58
|
+
|
|
59
|
+
if (status.recentCommits.length > 0) {
|
|
60
|
+
info("Recent commits:");
|
|
61
|
+
const commitRows = status.recentCommits
|
|
62
|
+
.slice(0, 3)
|
|
63
|
+
.map((c) => [
|
|
64
|
+
c.hash.substring(0, 7),
|
|
65
|
+
c.date.substring(0, 10),
|
|
66
|
+
c.message.substring(0, 50),
|
|
67
|
+
]);
|
|
68
|
+
text(createTable(["Hash", "Date", "Message"], commitRows));
|
|
69
|
+
}
|
|
70
|
+
} catch (err) {
|
|
71
|
+
info(`Status: ${err instanceof Error ? err.message : String(err)}`);
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import * as git from "../lib/git.js";
|
|
3
|
+
import { error, success } from "../lib/logger.js";
|
|
4
|
+
import { withSpinner } from "../lib/ui.js";
|
|
5
|
+
|
|
6
|
+
export default function register(program: Command): void {
|
|
7
|
+
program
|
|
8
|
+
.command("sync")
|
|
9
|
+
.description("Pull with rebase")
|
|
10
|
+
.action(async () => {
|
|
11
|
+
try {
|
|
12
|
+
const result = await withSpinner("Syncing...", () => git.pullRebase());
|
|
13
|
+
success(`Sync complete: ${result}`);
|
|
14
|
+
} catch (err) {
|
|
15
|
+
error(
|
|
16
|
+
`Sync failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
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 } from "../lib/ui.js";
|
|
5
|
+
|
|
6
|
+
export default function register(program: Command): void {
|
|
7
|
+
program
|
|
8
|
+
.command("tag")
|
|
9
|
+
.description("Manage tags")
|
|
10
|
+
.option("-n, --name <name>", "Create tag")
|
|
11
|
+
.option("-m, --message <message>", "Tag message")
|
|
12
|
+
.option("--list", "List tags")
|
|
13
|
+
.action(async (options) => {
|
|
14
|
+
try {
|
|
15
|
+
if (options.list) {
|
|
16
|
+
const tags = await git.tagList();
|
|
17
|
+
if (tags.length === 0) {
|
|
18
|
+
info("No tags found.");
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const rows = tags.map((t) => [t]);
|
|
23
|
+
info("Tags:");
|
|
24
|
+
console.log(createTable(["Name"], rows));
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
if (options.name) {
|
|
29
|
+
const tagName = await git.tag(options.name, options.message);
|
|
30
|
+
success(`Tag '${tagName}' created.`);
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
info("Use --name to create a tag or --list to list tags.");
|
|
35
|
+
} catch (err) {
|
|
36
|
+
error(
|
|
37
|
+
`Tag operation failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { Command } from "commander";
|
|
2
|
+
import * as git from "../lib/git.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("undo")
|
|
9
|
+
.description("Undo last commit (soft reset)")
|
|
10
|
+
.action(async () => {
|
|
11
|
+
try {
|
|
12
|
+
warning("This will undo the last commit using a soft reset.");
|
|
13
|
+
const proceed = await confirm("Are you sure?");
|
|
14
|
+
if (!proceed) {
|
|
15
|
+
info("Undo cancelled.");
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const result = await git.undo();
|
|
20
|
+
success(`Undo complete: ${result}`);
|
|
21
|
+
} catch (err) {
|
|
22
|
+
error(
|
|
23
|
+
`Undo failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
});
|
|
27
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { readFileSync } from "node:fs";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import type { Command } from "commander";
|
|
5
|
+
import { info } from "../lib/logger.js";
|
|
6
|
+
|
|
7
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const pkg = JSON.parse(
|
|
9
|
+
readFileSync(join(__dirname, "../../package.json"), "utf-8"),
|
|
10
|
+
);
|
|
11
|
+
|
|
12
|
+
export default function register(program: Command): void {
|
|
13
|
+
program
|
|
14
|
+
.command("version")
|
|
15
|
+
.description("Show version")
|
|
16
|
+
.action(async () => {
|
|
17
|
+
info(`Forge v${pkg.version}`);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
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 } from "../lib/ui.js";
|
|
5
|
+
|
|
6
|
+
export default function register(program: Command): void {
|
|
7
|
+
const cmd = program.command("worktree").description("Manage worktrees");
|
|
8
|
+
|
|
9
|
+
cmd
|
|
10
|
+
.command("add")
|
|
11
|
+
.description("Add a new worktree")
|
|
12
|
+
.argument("<path>", "Path for the new worktree")
|
|
13
|
+
.argument("[branch]", "Branch to checkout")
|
|
14
|
+
.option("--new", "Create and checkout a new branch")
|
|
15
|
+
.option("--detach", "Checkout detached HEAD")
|
|
16
|
+
.action(
|
|
17
|
+
async (
|
|
18
|
+
path: string,
|
|
19
|
+
branch: string | undefined,
|
|
20
|
+
options: { new?: boolean; detach?: boolean },
|
|
21
|
+
) => {
|
|
22
|
+
try {
|
|
23
|
+
await git.worktreeAdd(path, branch, {
|
|
24
|
+
new: options.new,
|
|
25
|
+
detach: options.detach,
|
|
26
|
+
});
|
|
27
|
+
success(`Worktree added at '${path}'.`);
|
|
28
|
+
} catch (err) {
|
|
29
|
+
error(
|
|
30
|
+
`Worktree add failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
);
|
|
35
|
+
|
|
36
|
+
cmd
|
|
37
|
+
.command("list")
|
|
38
|
+
.description("List all worktrees")
|
|
39
|
+
.action(async () => {
|
|
40
|
+
try {
|
|
41
|
+
const entries = await git.worktreeList();
|
|
42
|
+
if (entries.length === 0) {
|
|
43
|
+
info("No worktrees found.");
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
const rows = entries.map((e) => [e.path, e.branch, e.hash]);
|
|
47
|
+
console.log(createTable(["Path", "Branch", "Hash"], rows));
|
|
48
|
+
} catch (err) {
|
|
49
|
+
error(
|
|
50
|
+
`Worktree list failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
cmd
|
|
56
|
+
.command("remove")
|
|
57
|
+
.description("Remove a worktree")
|
|
58
|
+
.argument("<path>", "Path of the worktree to remove")
|
|
59
|
+
.option("--force", "Force removal")
|
|
60
|
+
.action(async (path: string, options: { force?: boolean }) => {
|
|
61
|
+
try {
|
|
62
|
+
await git.worktreeRemove(path, options.force);
|
|
63
|
+
success(`Worktree '${path}' removed.`);
|
|
64
|
+
} catch (err) {
|
|
65
|
+
error(
|
|
66
|
+
`Worktree remove failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
cmd
|
|
72
|
+
.command("prune")
|
|
73
|
+
.description("Prune stale worktree references")
|
|
74
|
+
.option("--dry-run", "Only show what would be pruned")
|
|
75
|
+
.action(async (options: { dryRun?: boolean }) => {
|
|
76
|
+
try {
|
|
77
|
+
const result = await git.worktreePrune(options.dryRun);
|
|
78
|
+
if (result.length === 0) {
|
|
79
|
+
info("Nothing to prune.");
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
for (const line of result) {
|
|
83
|
+
info(line);
|
|
84
|
+
}
|
|
85
|
+
success(`Pruned ${result.length} reference(s).`);
|
|
86
|
+
} catch (err) {
|
|
87
|
+
error(
|
|
88
|
+
`Worktree prune failed: ${err instanceof Error ? err.message : String(err)}`,
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export const CommitType = {
|
|
2
|
+
FEAT: "feat",
|
|
3
|
+
FIX: "fix",
|
|
4
|
+
DOCS: "docs",
|
|
5
|
+
STYLE: "style",
|
|
6
|
+
REFACTOR: "refactor",
|
|
7
|
+
TEST: "test",
|
|
8
|
+
CHORE: "chore",
|
|
9
|
+
} as const;
|
|
10
|
+
|
|
11
|
+
export type CommitType = (typeof CommitType)[keyof typeof CommitType];
|
|
12
|
+
|
|
13
|
+
export const commitTypes: { value: CommitType; description: string }[] = [
|
|
14
|
+
{ value: CommitType.FEAT, description: "A new feature" },
|
|
15
|
+
{ value: CommitType.FIX, description: "A bug fix" },
|
|
16
|
+
{ value: CommitType.DOCS, description: "Documentation only changes" },
|
|
17
|
+
{
|
|
18
|
+
value: CommitType.STYLE,
|
|
19
|
+
description: "Code style changes (formatting, etc)",
|
|
20
|
+
},
|
|
21
|
+
{ value: CommitType.REFACTOR, description: "Code refactoring" },
|
|
22
|
+
{ value: CommitType.TEST, description: "Adding or fixing tests" },
|
|
23
|
+
{ value: CommitType.CHORE, description: "Build process or tool changes" },
|
|
24
|
+
];
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export const icons = {
|
|
2
|
+
success: "\u2713",
|
|
3
|
+
error: "\u2717",
|
|
4
|
+
warning: "\u26A0",
|
|
5
|
+
info: "\u25CB",
|
|
6
|
+
highlight: "\u00BB",
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
export const formatting = {
|
|
10
|
+
header: "\u2550".repeat(30),
|
|
11
|
+
separator: "\u2500".repeat(25),
|
|
12
|
+
indent: " ",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export const commitTypes = [
|
|
16
|
+
{ value: "feat", description: "A new feature" },
|
|
17
|
+
{ value: "fix", description: "A bug fix" },
|
|
18
|
+
{ value: "docs", description: "Documentation only changes" },
|
|
19
|
+
{ value: "style", description: "Code style changes (formatting, etc)" },
|
|
20
|
+
{ value: "refactor", description: "Code refactoring" },
|
|
21
|
+
{ value: "test", description: "Adding or fixing tests" },
|
|
22
|
+
{ value: "chore", description: "Build process or tool changes" },
|
|
23
|
+
] as const;
|
package/src/lib/auth.ts
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
const ITERATIONS = 100000;
|
|
2
|
+
const KEY_LENGTH = 256;
|
|
3
|
+
const SALT_LENGTH = 32;
|
|
4
|
+
const IV_LENGTH = 12;
|
|
5
|
+
|
|
6
|
+
function base64Encode(buf: Uint8Array): string {
|
|
7
|
+
return btoa(String.fromCharCode(...buf));
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
function base64Decode(str: string): Uint8Array {
|
|
11
|
+
return Uint8Array.from(atob(str), (c) => c.charCodeAt(0));
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function generateSalt(length: number = SALT_LENGTH): Uint8Array {
|
|
15
|
+
return crypto.getRandomValues(new Uint8Array(length));
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function generateIV(): Uint8Array {
|
|
19
|
+
return crypto.getRandomValues(new Uint8Array(IV_LENGTH));
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
async function deriveKey(
|
|
23
|
+
password: string,
|
|
24
|
+
salt: Uint8Array,
|
|
25
|
+
): Promise<CryptoKey> {
|
|
26
|
+
const encoder = new TextEncoder();
|
|
27
|
+
const keyMaterial = await crypto.subtle.importKey(
|
|
28
|
+
"raw",
|
|
29
|
+
encoder.encode(password),
|
|
30
|
+
"PBKDF2",
|
|
31
|
+
false,
|
|
32
|
+
["deriveKey"],
|
|
33
|
+
);
|
|
34
|
+
|
|
35
|
+
return crypto.subtle.deriveKey(
|
|
36
|
+
{
|
|
37
|
+
name: "PBKDF2",
|
|
38
|
+
salt,
|
|
39
|
+
iterations: ITERATIONS,
|
|
40
|
+
hash: "SHA-256",
|
|
41
|
+
},
|
|
42
|
+
keyMaterial,
|
|
43
|
+
{ name: "AES-GCM", length: KEY_LENGTH },
|
|
44
|
+
false,
|
|
45
|
+
["encrypt", "decrypt"],
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export async function encryptToken(
|
|
50
|
+
token: string,
|
|
51
|
+
password: string,
|
|
52
|
+
): Promise<string> {
|
|
53
|
+
const salt = generateSalt();
|
|
54
|
+
const iv = generateIV();
|
|
55
|
+
const key = await deriveKey(password, salt);
|
|
56
|
+
const encoder = new TextEncoder();
|
|
57
|
+
const encrypted = await crypto.subtle.encrypt(
|
|
58
|
+
{ name: "AES-GCM", iv },
|
|
59
|
+
key,
|
|
60
|
+
encoder.encode(token),
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
const parts = [
|
|
64
|
+
base64Encode(salt),
|
|
65
|
+
base64Encode(iv),
|
|
66
|
+
base64Encode(new Uint8Array(encrypted)),
|
|
67
|
+
];
|
|
68
|
+
|
|
69
|
+
return parts.join(":");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export async function decryptToken(
|
|
73
|
+
encryptedData: string,
|
|
74
|
+
password: string,
|
|
75
|
+
): Promise<string> {
|
|
76
|
+
const parts = encryptedData.split(":");
|
|
77
|
+
if (parts.length !== 3) {
|
|
78
|
+
throw new Error("Invalid encrypted data format");
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
const [saltB64, ivB64, ciphertextB64] = parts;
|
|
82
|
+
const salt = base64Decode(saltB64);
|
|
83
|
+
const iv = base64Decode(ivB64);
|
|
84
|
+
const ciphertext = base64Decode(ciphertextB64);
|
|
85
|
+
const key = await deriveKey(password, salt);
|
|
86
|
+
|
|
87
|
+
const decrypted = await crypto.subtle.decrypt(
|
|
88
|
+
{ name: "AES-GCM", iv },
|
|
89
|
+
key,
|
|
90
|
+
ciphertext,
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
const decoder = new TextDecoder();
|
|
94
|
+
return decoder.decode(decrypted);
|
|
95
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { homedir } from "node:os";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import Conf from "conf";
|
|
4
|
+
|
|
5
|
+
export interface ForgeConfig {
|
|
6
|
+
user: {
|
|
7
|
+
name: string;
|
|
8
|
+
email: string;
|
|
9
|
+
};
|
|
10
|
+
github: {
|
|
11
|
+
token: string;
|
|
12
|
+
};
|
|
13
|
+
preferences: {
|
|
14
|
+
autoPush: boolean;
|
|
15
|
+
commitTemplate: string;
|
|
16
|
+
editor: string;
|
|
17
|
+
};
|
|
18
|
+
clones: string[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
const DEFAULTS: ForgeConfig = {
|
|
22
|
+
user: { name: "", email: "" },
|
|
23
|
+
github: { token: "" },
|
|
24
|
+
preferences: {
|
|
25
|
+
autoPush: false,
|
|
26
|
+
commitTemplate: "",
|
|
27
|
+
editor: process.env.EDITOR || "vim",
|
|
28
|
+
},
|
|
29
|
+
clones: [],
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const configPath = join(homedir(), ".forge");
|
|
33
|
+
|
|
34
|
+
export class ConfigManager {
|
|
35
|
+
private conf: Conf<ForgeConfig>;
|
|
36
|
+
|
|
37
|
+
constructor(options?: { configName?: string; cwd?: string }) {
|
|
38
|
+
this.conf = new Conf<ForgeConfig>({
|
|
39
|
+
configName: options?.configName || "forge",
|
|
40
|
+
cwd: options?.cwd || configPath,
|
|
41
|
+
defaults: DEFAULTS,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
get(key: string): any {
|
|
46
|
+
return this.conf.get(key);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
set(key: string, value: any): void {
|
|
50
|
+
this.conf.set(key, value);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
delete(key: string): void {
|
|
54
|
+
this.conf.delete(key);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
clear(): void {
|
|
58
|
+
this.conf.clear();
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
getPath(): string {
|
|
62
|
+
return this.conf.path;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
getAll(): ForgeConfig & { clones: string[] } {
|
|
66
|
+
return {
|
|
67
|
+
user: {
|
|
68
|
+
name: this.conf.get("user.name") as string,
|
|
69
|
+
email: this.conf.get("user.email") as string,
|
|
70
|
+
},
|
|
71
|
+
github: {
|
|
72
|
+
token: this.conf.get("github.token") as string,
|
|
73
|
+
},
|
|
74
|
+
preferences: {
|
|
75
|
+
autoPush: this.conf.get("preferences.autoPush") as boolean,
|
|
76
|
+
commitTemplate: this.conf.get("preferences.commitTemplate") as string,
|
|
77
|
+
editor: this.conf.get("preferences.editor") as string,
|
|
78
|
+
},
|
|
79
|
+
clones: this.getClones(),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
addClone(entry: string): void {
|
|
84
|
+
const clones = (this.conf.get("clones") as string[]) || [];
|
|
85
|
+
clones.unshift(entry);
|
|
86
|
+
if (clones.length > 10) clones.length = 10;
|
|
87
|
+
this.conf.set("clones", clones);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
getClones(): string[] {
|
|
91
|
+
return (this.conf.get("clones") as string[]) || [];
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
isConfigured(): boolean {
|
|
95
|
+
const name = this.conf.get("user.name") as string;
|
|
96
|
+
const email = this.conf.get("user.email") as string;
|
|
97
|
+
return !!(name && email);
|
|
98
|
+
}
|
|
99
|
+
}
|