@romenkova/cli 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.
@@ -0,0 +1,42 @@
1
+ import { slugify } from "../git.js";
2
+ import { prompt, ui } from "../ui.js";
3
+ import { execSync } from "node:child_process";
4
+ const branchCommand = {
5
+ command: "branch",
6
+ describe: "Create a branch from a ticket ID",
7
+ builder: {
8
+ name: {
9
+ alias: "n",
10
+ type: "string",
11
+ demandOption: true,
12
+ describe: "Your name (used as branch prefix)",
13
+ },
14
+ },
15
+ handler: async (argv) => {
16
+ const author = argv.name;
17
+ ui.header("branch");
18
+ ui.info("author", author);
19
+ ui.separator();
20
+ const ticket = (await prompt("Ticket (e.g. XXX-000): ")).toUpperCase();
21
+ if (!/^[A-Z]+-\d+$/.test(ticket)) {
22
+ ui.fail(`Invalid ticket format: ${ticket}`, "Expected: XXX-000");
23
+ process.exit(1);
24
+ }
25
+ const description = await prompt("Description: ");
26
+ if (!description) {
27
+ ui.fail("Description cannot be empty.");
28
+ process.exit(1);
29
+ }
30
+ const branchName = `${author}/${ticket}-${slugify(description)}`;
31
+ ui.result("branch", branchName);
32
+ try {
33
+ execSync(`git checkout -b ${branchName}`, { stdio: "inherit" });
34
+ }
35
+ catch {
36
+ ui.fail("Failed to create branch.");
37
+ process.exit(1);
38
+ }
39
+ ui.done();
40
+ },
41
+ };
42
+ export default branchCommand;
@@ -0,0 +1,35 @@
1
+ import { extractTicket, getBranchName } from "../git.js";
2
+ import { prompt, ui } from "../ui.js";
3
+ import { execSync } from "node:child_process";
4
+ const commitCommand = {
5
+ command: "commit",
6
+ describe: "Create a commit prefixed with the ticket from the branch name",
7
+ handler: async () => {
8
+ ui.header("commit");
9
+ const branch = getBranchName();
10
+ const ticket = extractTicket(branch);
11
+ if (!ticket) {
12
+ ui.fail(`Could not extract ticket from branch: ${branch}`, "Expected: name/XXX-000-some-slug");
13
+ process.exit(1);
14
+ }
15
+ ui.info("branch", branch);
16
+ ui.highlight("ticket", ticket);
17
+ ui.separator();
18
+ const message = await prompt("Message: ");
19
+ if (!message) {
20
+ ui.fail("Commit message cannot be empty.");
21
+ process.exit(1);
22
+ }
23
+ const commitMessage = `${ticket}: ${message}`;
24
+ ui.result("commit", `"${commitMessage}"`);
25
+ try {
26
+ execSync(`git commit -m "${commitMessage}"`, { stdio: "inherit" });
27
+ }
28
+ catch {
29
+ ui.fail("Git commit failed.");
30
+ process.exit(1);
31
+ }
32
+ ui.done();
33
+ },
34
+ };
35
+ export default commitCommand;
@@ -0,0 +1,12 @@
1
+ import branchCommand from "./branch.js";
2
+ import commitCommand from "./commit.js";
3
+ const gitCommand = {
4
+ command: "git",
5
+ describe: "Git workflow helpers",
6
+ builder: (yargs) => yargs
7
+ .command(commitCommand)
8
+ .command(branchCommand)
9
+ .demandCommand(1, ""),
10
+ handler: () => { },
11
+ };
12
+ export default gitCommand;
package/dist/git.js ADDED
@@ -0,0 +1,16 @@
1
+ import { execSync } from "node:child_process";
2
+ export function getBranchName() {
3
+ return execSync("git rev-parse --abbrev-ref HEAD", {
4
+ encoding: "utf-8",
5
+ }).trim();
6
+ }
7
+ export function extractTicket(branch) {
8
+ const match = branch.match(/\/([A-Z]+-\d+)/);
9
+ return match ? match[1] : null;
10
+ }
11
+ export function slugify(text) {
12
+ return text
13
+ .toLowerCase()
14
+ .replace(/[^a-z0-9]+/g, "-")
15
+ .replace(/^-+|-+$/g, "");
16
+ }
package/dist/index.js ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ import yargs from "yargs";
3
+ import { hideBin } from "yargs/helpers";
4
+ import gitCommand from "./commands/git.js";
5
+ yargs(hideBin(process.argv))
6
+ .scriptName("ri")
7
+ .command(gitCommand)
8
+ .demandCommand(1, "")
9
+ .strict()
10
+ .help()
11
+ .parse();
package/dist/ui.js ADDED
@@ -0,0 +1,56 @@
1
+ import chalk from "chalk";
2
+ import * as readline from "node:readline";
3
+ export const log = console.log;
4
+ const S = {
5
+ dot: "\u25CF",
6
+ play: "\u25B6",
7
+ ok: "\u2714",
8
+ fail: "\u2718",
9
+ arrow: "\u25B8",
10
+ line: "\u2500",
11
+ to: "\u2192",
12
+ };
13
+ export const ui = {
14
+ separator: () => log(chalk.dim(S.line.repeat(48))),
15
+ header: (title) => {
16
+ log("");
17
+ log(chalk.bold.cyan(` ${title}`));
18
+ ui.separator();
19
+ },
20
+ info: (label, value) => {
21
+ log(chalk.dim(` ${S.dot} ${label.padEnd(8)}`) + chalk.white(value));
22
+ },
23
+ highlight: (label, value) => {
24
+ log(chalk.dim(` ${S.play} ${label.padEnd(8)}`) + chalk.bold.yellow(value));
25
+ },
26
+ result: (label, value) => {
27
+ log("");
28
+ ui.separator();
29
+ log(chalk.dim(` ${label} ${S.to} `) + chalk.bold.white(value));
30
+ ui.separator();
31
+ },
32
+ done: () => {
33
+ log("");
34
+ log(chalk.green(` ${S.ok} Done`));
35
+ log("");
36
+ },
37
+ fail: (message, hint) => {
38
+ log("");
39
+ log(chalk.red(` ${S.fail} ${message}`));
40
+ if (hint)
41
+ log(chalk.yellow(` ${hint}`));
42
+ log("");
43
+ },
44
+ };
45
+ export function prompt(label) {
46
+ const rl = readline.createInterface({
47
+ input: process.stdin,
48
+ output: process.stdout,
49
+ });
50
+ return new Promise((resolve) => {
51
+ rl.question(chalk.bold.white(` ${S.arrow} ${label}`), (answer) => {
52
+ rl.close();
53
+ resolve(answer.trim());
54
+ });
55
+ });
56
+ }
package/package.json ADDED
@@ -0,0 +1,33 @@
1
+ {
2
+ "name": "@romenkova/cli",
3
+ "version": "1.0.0",
4
+ "description": "Personal dev CLI",
5
+ "type": "module",
6
+ "main": "dist/index.js",
7
+ "bin": {
8
+ "ri": "dist/index.js"
9
+ },
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "scripts": {
14
+ "build": "tsc",
15
+ "dev": "tsx ./src/index.ts",
16
+ "lint": "eslint ."
17
+ },
18
+ "keywords": [],
19
+ "author": "",
20
+ "license": "ISC",
21
+ "packageManager": "pnpm@10.12.3",
22
+ "devDependencies": {
23
+ "@ri/eslint-config": "workspace:*",
24
+ "@ri/typescript-config": "workspace:*",
25
+ "@types/yargs": "^17.0.35",
26
+ "eslint": "^9.39.1",
27
+ "tsx": "^4.21.0"
28
+ },
29
+ "dependencies": {
30
+ "chalk": "^5.6.2",
31
+ "yargs": "^18.0.0"
32
+ }
33
+ }