@jannael/glinter 1.2.8 → 1.3.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 (3) hide show
  1. package/README.md +104 -104
  2. package/dist/index.js +251 -124
  3. package/package.json +14 -18
package/README.md CHANGED
@@ -1,104 +1,104 @@
1
- <p align="center">
2
- <br>
3
- <br>
4
- <a href="https://glinter.jannael.com" target="_blank" rel="noopener noreferrer">
5
- <picture>
6
- <img alt="Glinter" src="https://github.com/Jannael/glinter/raw/main/apps/web/public/og.png">
7
- </picture>
8
- </a>
9
- </p>
10
-
11
- Glinter is a high-performance, transparent Git wrapper built with **Bun**.
12
-
13
- [Tutorial](https://youtu.be/LQWp6flYeyI)
14
-
15
- [Commands](https://glinter.jannael.com/#commands)
16
-
17
- [Aliases](https://glinter.jannael.com/alias)
18
-
19
- [Quick start](https://glinter.jannael.com/#quick-start)
20
-
21
- ## Preview
22
-
23
- ### Badge
24
-
25
- | Preview | Copy |
26
- | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
27
- | [![Glinter](https://glinter.jannael.com/badge.svg)](https://glinter.jannael.com) | `[![Glinter](https://glinter.jannael.com/badge.svg)](https://glinter.jannael.com)` |
28
-
29
- <video src="https://github.com/user-attachments/assets/63b401a0-e1e1-453c-9e38-c36cd14e200f" controls="false" autoplay="true" loop="true" muted="true" style="max-width: 100%;">
30
- Your browser does not support the video tag.
31
- </video>
32
-
33
- ### Screenshots
34
-
35
- | `g add` | `g commit` |
36
- | ------------------------------------ | --------------------------------------- |
37
- | ![glinter add](./screenshots/ga.png) | ![glinter commit](./screenshots/gc.png) |
38
-
39
- ## Security
40
-
41
- Security is a top priority for this project.
42
-
43
- This project uses Bun as its runtime and package manager. You don't need to worry about package vulnerabilities — the `bunfig.toml` configuration includes `minimumReleaseAge = 259200` (3 days), which prevents newly published packages from being installed automatically. This protects against supply chain attacks that have become increasingly common in the npm ecosystem.
44
-
45
- ## Features
46
-
47
- - **Abbreviation**: You can use `g` instead of `git`.
48
-
49
- - **Safe by Default**: Automatically filters and prevents accidental staging of sensitive files: `.env` and `node_modules`.
50
-
51
- - **Transparent Wrapper**: For every other command (like `commit`, `push`, `log`, or `status`), Glinter acts as a direct tunnel to Git. It preserves all original colors, formatting, and interactive features of the native Git CLI.
52
-
53
- ## Test environment
54
-
55
- - for windows i used my own laptop (windows 11) so if you have a problem please create an issue
56
-
57
- - for linux i used WSL (windows subsystem linux) with a kali-linux vm so if you have a problem please create an issue
58
-
59
- ## How it works
60
-
61
- Glinter is designed to be as "natural" as possible, meaning it shouldn't feel like a wrapper at all.
62
-
63
- ### 1. The Transparent Proxy
64
-
65
- In `src/index.ts`, Glinter uses `Bun.spawn` with `stdio: 'inherit'`. This is a low-level operation that connects the standard input, output, and error streams of the Git process directly to your terminal.
66
-
67
- - **Result**: Git "knows" it's in a real terminal, so it correctly detects colors and allows for interactive prompts (like credential entry).
68
-
69
- ### 2. Reliable Status Parsing
70
-
71
- For the interactive `add` feature, Glinter runs `git status --porcelain`.
72
-
73
- - **Why?**: Standard `git status` output is designed for humans and can change based on your Git version or system language. `--porcelain` is a machine-readable format that is consistent across all environments, making the file detection 100% reliable.
74
-
75
- ### 3. Interactive Selection
76
-
77
- Using the `@clack/prompts` library, Glinter transforms the raw status data into a selectable list.
78
-
79
- - The selection logic uses Bun's high-speed shell to execute the final `git add` command, correctly escaping filenames to handle spaces and special characters.
80
-
81
- ## Installation
82
-
83
- To use Glinter as your primary Git interface (e.g., using the command `g`):
84
-
85
- ```bash
86
- npm install -g @jannael/glinter
87
- ```
88
-
89
- ### For Development
90
-
91
- 1. **Clone the repo**
92
- 2. **Install dependencies**: `bun install`
93
- 3. **Link the binary**: `bun link`
94
-
95
- now you can simply run:
96
-
97
- ```bash
98
- g add # Opens the interactive selector
99
- g commit # Opens commit type + message prompt
100
- g add <file> # Runs standard git add <file>
101
- g commit -m "" # Runs standard git commit -m ""
102
- g status # Runs standard git status
103
- g push # Runs standard git push
104
- ```
1
+ <p align="center">
2
+ <br>
3
+ <br>
4
+ <a href="https://glinter.jannael.com" target="_blank" rel="noopener noreferrer">
5
+ <picture>
6
+ <img alt="Glinter" src="https://github.com/Jannael/glinter/raw/main/apps/web/public/og.png">
7
+ </picture>
8
+ </a>
9
+ </p>
10
+
11
+ Glinter is a high-performance, transparent Git wrapper built with **Bun**.
12
+
13
+ [Tutorial](https://youtu.be/LQWp6flYeyI)
14
+
15
+ [Commands](https://glinter.jannael.com/#commands)
16
+
17
+ [Aliases](https://glinter.jannael.com/alias)
18
+
19
+ [Quick start](https://glinter.jannael.com/#quick-start)
20
+
21
+ ## Preview
22
+
23
+ ### Badge
24
+
25
+ | Preview | Copy |
26
+ | -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------- |
27
+ | [![Glinter](https://glinter.jannael.com/badge.svg)](https://glinter.jannael.com) | `[![Glinter](https://glinter.jannael.com/badge.svg)](https://glinter.jannael.com)` |
28
+
29
+ <video src="https://github.com/user-attachments/assets/63b401a0-e1e1-453c-9e38-c36cd14e200f" controls="false" autoplay="true" loop="true" muted="true" style="max-width: 100%;">
30
+ Your browser does not support the video tag.
31
+ </video>
32
+
33
+ ### Screenshots
34
+
35
+ | `g add` | `g commit` |
36
+ | ------------------------------------ | --------------------------------------- |
37
+ | ![glinter add](./screenshots/ga.png) | ![glinter commit](./screenshots/gc.png) |
38
+
39
+ ## Security
40
+
41
+ Security is a top priority for this project.
42
+
43
+ This project uses Bun as its runtime and package manager. You don't need to worry about package vulnerabilities — the `bunfig.toml` configuration includes `minimumReleaseAge = 259200` (3 days), which prevents newly published packages from being installed automatically. This protects against supply chain attacks that have become increasingly common in the npm ecosystem.
44
+
45
+ ## Features
46
+
47
+ - **Abbreviation**: You can use `g` instead of `git`.
48
+
49
+ - **Safe by Default**: Automatically filters and prevents accidental staging of sensitive files: `.env` and `node_modules`.
50
+
51
+ - **Transparent Wrapper**: For every other command (like `commit`, `push`, `log`, or `status`), Glinter acts as a direct tunnel to Git. It preserves all original colors, formatting, and interactive features of the native Git CLI.
52
+
53
+ ## Test environment
54
+
55
+ - for windows i used my own laptop (windows 11) so if you have a problem please create an issue
56
+
57
+ - for linux i used WSL (windows subsystem linux) with a kali-linux vm so if you have a problem please create an issue
58
+
59
+ ## How it works
60
+
61
+ Glinter is designed to be as "natural" as possible, meaning it shouldn't feel like a wrapper at all.
62
+
63
+ ### 1. The Transparent Proxy
64
+
65
+ In `src/index.ts`, Glinter uses `Bun.spawn` with `stdio: 'inherit'`. This is a low-level operation that connects the standard input, output, and error streams of the Git process directly to your terminal.
66
+
67
+ - **Result**: Git "knows" it's in a real terminal, so it correctly detects colors and allows for interactive prompts (like credential entry).
68
+
69
+ ### 2. Reliable Status Parsing
70
+
71
+ For the interactive `add` feature, Glinter runs `git status --porcelain`.
72
+
73
+ - **Why?**: Standard `git status` output is designed for humans and can change based on your Git version or system language. `--porcelain` is a machine-readable format that is consistent across all environments, making the file detection 100% reliable.
74
+
75
+ ### 3. Interactive Selection
76
+
77
+ Using the `@clack/prompts` library, Glinter transforms the raw status data into a selectable list.
78
+
79
+ - The selection logic uses Bun's high-speed shell to execute the final `git add` command, correctly escaping filenames to handle spaces and special characters.
80
+
81
+ ## Installation
82
+
83
+ To use Glinter as your primary Git interface (e.g., using the command `g`):
84
+
85
+ ```bash
86
+ npm install -g @jannael/glinter
87
+ ```
88
+
89
+ ### For Development
90
+
91
+ 1. **Clone the repo**
92
+ 2. **Install dependencies**: `bun install`
93
+ 3. **Link the binary**: `bun link`
94
+
95
+ now you can simply run:
96
+
97
+ ```bash
98
+ g add # Opens the interactive selector
99
+ g commit # Opens commit type + message prompt
100
+ g add <file> # Runs standard git add <file>
101
+ g commit -m "" # Runs standard git commit -m ""
102
+ g status # Runs standard git status
103
+ g push # Runs standard git push
104
+ ```
package/dist/index.js CHANGED
@@ -116,6 +116,11 @@ var AVAILABLE_COMMANDS = [
116
116
  name: "setup",
117
117
  command: "g setup",
118
118
  description: "Setup alias for git and glinter"
119
+ },
120
+ {
121
+ name: "reset-hard",
122
+ command: "g reset-hard",
123
+ description: "Interactive reset hard"
119
124
  }
120
125
  ];
121
126
 
@@ -1188,8 +1193,8 @@ var u$1 = class u extends V {
1188
1193
  var o$1 = /* @__PURE__ */ new Set(["up", "down", "left", "right"]);
1189
1194
 
1190
1195
  class h extends V {
1191
- #s = false;
1192
- #t;
1196
+ #t = false;
1197
+ #s;
1193
1198
  focused = "editor";
1194
1199
  get userInputWithCursor() {
1195
1200
  if (this.state === "submit")
@@ -1197,10 +1202,10 @@ class h extends V {
1197
1202
  const t2 = this.userInput;
1198
1203
  if (this.cursor >= t2.length)
1199
1204
  return `${t2}\u2588`;
1200
- const s = t2.slice(0, this.cursor), r2 = t2[this.cursor], e = t2.slice(this.cursor + 1);
1205
+ const s = t2.slice(0, this.cursor), r2 = t2[this.cursor], i = t2.slice(this.cursor + 1);
1201
1206
  return r2 === `
1202
1207
  ` ? `${s}\u2588
1203
- ${e}` : `${s}${styleText("inverse", r2)}${e}`;
1208
+ ${i}` : `${s}${styleText("inverse", r2)}${i}`;
1204
1209
  }
1205
1210
  get cursor() {
1206
1211
  return this._cursor;
@@ -1230,37 +1235,41 @@ ${e}` : `${s}${styleText("inverse", r2)}${e}`;
1230
1235
  }
1231
1236
  }
1232
1237
  _shouldSubmit(t2, s) {
1233
- if (this.#t)
1238
+ if (this.#s)
1234
1239
  return this.focused === "submit" ? true : (this.#r(`
1235
1240
  `), this._cursor++, false);
1236
- const r2 = this.#s;
1237
- return this.#s = true, r2 ? (this.userInput[this.cursor - 1] === `
1241
+ const r2 = this.#t;
1242
+ return this.#t = true, r2 && this.cursor === this.userInput.length ? (this.userInput[this.cursor - 1] === `
1238
1243
  ` && (this._setUserInput(this.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)), this._cursor--), true) : (this.#r(`
1239
1244
  `), this._cursor++, false);
1240
1245
  }
1241
1246
  constructor(t2) {
1242
- super(t2, false), this.#t = t2.showSubmit ?? false, this.on("key", (s, r2) => {
1243
- if (r2?.name && o$1.has(r2.name)) {
1244
- this.#i(r2.name);
1247
+ const s = t2.initialUserInput ?? t2.initialValue;
1248
+ super({
1249
+ ...t2,
1250
+ initialUserInput: s
1251
+ }, false), s !== undefined && (this._cursor = s.length), this.#s = t2.showSubmit ?? false, this.on("key", (r2, i) => {
1252
+ if (i?.name && o$1.has(i.name)) {
1253
+ this.#t = false, this.#i(i.name);
1245
1254
  return;
1246
1255
  }
1247
- if (s === "\t" && this.#t) {
1256
+ if (r2 === "\t" && this.#s) {
1248
1257
  this.focused = this.focused === "editor" ? "submit" : "editor";
1249
1258
  return;
1250
1259
  }
1251
- if (r2?.name !== "return") {
1252
- if (this.#s = false, r2?.name === "backspace" && this.cursor > 0) {
1260
+ if (i?.name !== "return") {
1261
+ if (this.#t = false, i?.name === "backspace" && this.cursor > 0) {
1253
1262
  this._setUserInput(this.userInput.slice(0, this.cursor - 1) + this.userInput.slice(this.cursor)), this._cursor--;
1254
1263
  return;
1255
1264
  }
1256
- if (r2?.name === "delete" && this.cursor < this.userInput.length) {
1265
+ if (i?.name === "delete" && this.cursor < this.userInput.length) {
1257
1266
  this._setUserInput(this.userInput.slice(0, this.cursor) + this.userInput.slice(this.cursor + 1));
1258
1267
  return;
1259
1268
  }
1260
- s && (this.#t && this.focused === "submit" && (this.focused = "editor"), this.#r(s ?? ""), this._cursor++);
1269
+ r2 && (this.#s && this.focused === "submit" && (this.focused = "editor"), this.#r(r2 ?? ""), this._cursor++);
1261
1270
  }
1262
- }), this.on("userInput", (s) => {
1263
- this._setValue(s);
1271
+ }), this.on("userInput", (r2) => {
1272
+ this._setValue(r2);
1264
1273
  }), this.on("finalize", () => {
1265
1274
  this.value || (this.value = t2.defaultValue), this.value === undefined && (this.value = "");
1266
1275
  });
@@ -1377,7 +1386,7 @@ function isUnicodeSupported() {
1377
1386
  }
1378
1387
  var unicode = isUnicodeSupported();
1379
1388
  var isCI = () => process.env.CI === "true";
1380
- var unicodeOr = (e, o2) => unicode ? e : o2;
1389
+ var unicodeOr = (o2, e) => unicode ? o2 : e;
1381
1390
  var S_STEP_ACTIVE = unicodeOr("\u25C6", "*");
1382
1391
  var S_STEP_CANCEL = unicodeOr("\u25A0", "x");
1383
1392
  var S_STEP_ERROR = unicodeOr("\u25B2", "x");
@@ -1403,8 +1412,8 @@ var S_INFO = unicodeOr("\u25CF", "\u2022");
1403
1412
  var S_SUCCESS = unicodeOr("\u25C6", "*");
1404
1413
  var S_WARN = unicodeOr("\u25B2", "!");
1405
1414
  var S_ERROR = unicodeOr("\u25A0", "x");
1406
- var symbol = (e) => {
1407
- switch (e) {
1415
+ var symbol = (o2) => {
1416
+ switch (o2) {
1408
1417
  case "initial":
1409
1418
  case "active":
1410
1419
  return styleText2("cyan", S_STEP_ACTIVE);
@@ -1416,8 +1425,8 @@ var symbol = (e) => {
1416
1425
  return styleText2("green", S_STEP_SUBMIT);
1417
1426
  }
1418
1427
  };
1419
- var symbolBar = (e) => {
1420
- switch (e) {
1428
+ var symbolBar = (o2) => {
1429
+ switch (o2) {
1421
1430
  case "initial":
1422
1431
  case "active":
1423
1432
  return styleText2("cyan", S_BAR);
@@ -1429,6 +1438,10 @@ var symbolBar = (e) => {
1429
1438
  return styleText2("green", S_BAR);
1430
1439
  }
1431
1440
  };
1441
+ function formatInstructionFooter(o2, e) {
1442
+ const r2 = [`${e ? `${styleText2("cyan", S_BAR)} ` : ""}${o2.join(" \u2022 ")}`];
1443
+ return e && r2.push(styleText2("cyan", S_BAR_END)), r2;
1444
+ }
1432
1445
  var E$1 = (l2, o2, g2, c2, h2, O = false) => {
1433
1446
  let r2 = o2, w = 0;
1434
1447
  if (O)
@@ -1513,61 +1526,48 @@ ${g2}
1513
1526
  }
1514
1527
  }).prompt();
1515
1528
  };
1516
- var cancel = (o2 = "", t2) => {
1517
- const i = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR_END)} ` : "";
1518
- i.write(`${e}${styleText2("red", o2)}
1519
-
1520
- `);
1521
- };
1522
- var intro = (o2 = "", t2) => {
1523
- const i = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR_START)} ` : "";
1524
- i.write(`${e}${o2}
1525
- `);
1526
- };
1527
- var outro = (o2 = "", t2) => {
1528
- const i = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR)}
1529
- ${styleText2("gray", S_BAR_END)} ` : "";
1530
- i.write(`${e}${o2}
1531
-
1532
- `);
1533
- };
1534
- var d = (n2, a3) => n2.split(`
1535
- `).map((m2) => a3(m2)).join(`
1529
+ var MULTISELECT_INSTRUCTIONS = [
1530
+ `${styleText2("dim", "\u2191/\u2193")} to navigate`,
1531
+ `${styleText2("dim", "Space:")} select`,
1532
+ `${styleText2("dim", "Enter:")} confirm`
1533
+ ];
1534
+ var m2 = (n2, o2) => n2.split(`
1535
+ `).map((d) => o2(d)).join(`
1536
1536
  `);
1537
1537
  var multiselect = (n2) => {
1538
- const a3 = (t2, o2) => {
1538
+ const o2 = (t2, a3) => {
1539
1539
  const r2 = t2.label ?? String(t2.value);
1540
- return o2 === "disabled" ? `${styleText2("gray", S_CHECKBOX_INACTIVE)} ${d(r2, (l2) => styleText2(["strikethrough", "gray"], l2))}${t2.hint ? ` ${styleText2("dim", `(${t2.hint ?? "disabled"})`)}` : ""}` : o2 === "active" ? `${styleText2("cyan", S_CHECKBOX_ACTIVE)} ${r2}${t2.hint ? ` ${styleText2("dim", `(${t2.hint})`)}` : ""}` : o2 === "selected" ? `${styleText2("green", S_CHECKBOX_SELECTED)} ${d(r2, (l2) => styleText2("dim", l2))}${t2.hint ? ` ${styleText2("dim", `(${t2.hint})`)}` : ""}` : o2 === "cancelled" ? `${d(r2, (l2) => styleText2(["strikethrough", "dim"], l2))}` : o2 === "active-selected" ? `${styleText2("green", S_CHECKBOX_SELECTED)} ${r2}${t2.hint ? ` ${styleText2("dim", `(${t2.hint})`)}` : ""}` : o2 === "submitted" ? `${d(r2, (l2) => styleText2("dim", l2))}` : `${styleText2("dim", S_CHECKBOX_INACTIVE)} ${d(r2, (l2) => styleText2("dim", l2))}`;
1541
- }, m2 = n2.required ?? true;
1540
+ return a3 === "disabled" ? `${styleText2("gray", S_CHECKBOX_INACTIVE)} ${m2(r2, (l2) => styleText2(["strikethrough", "gray"], l2))}${t2.hint ? ` ${styleText2("dim", `(${t2.hint ?? "disabled"})`)}` : ""}` : a3 === "active" ? `${styleText2("cyan", S_CHECKBOX_ACTIVE)} ${r2}${t2.hint ? ` ${styleText2("dim", `(${t2.hint})`)}` : ""}` : a3 === "selected" ? `${styleText2("green", S_CHECKBOX_SELECTED)} ${m2(r2, (l2) => styleText2("dim", l2))}${t2.hint ? ` ${styleText2("dim", `(${t2.hint})`)}` : ""}` : a3 === "cancelled" ? `${m2(r2, (l2) => styleText2(["strikethrough", "dim"], l2))}` : a3 === "active-selected" ? `${styleText2("green", S_CHECKBOX_SELECTED)} ${r2}${t2.hint ? ` ${styleText2("dim", `(${t2.hint})`)}` : ""}` : a3 === "submitted" ? `${m2(r2, (l2) => styleText2("dim", l2))}` : `${styleText2("dim", S_CHECKBOX_INACTIVE)} ${m2(r2, (l2) => styleText2("dim", l2))}`;
1541
+ }, d = n2.required ?? true;
1542
1542
  return new a$1({
1543
1543
  options: n2.options,
1544
1544
  signal: n2.signal,
1545
1545
  input: n2.input,
1546
1546
  output: n2.output,
1547
1547
  initialValues: n2.initialValues,
1548
- required: m2,
1548
+ required: d,
1549
1549
  cursorAt: n2.cursorAt,
1550
1550
  validate(t2) {
1551
- if (m2 && (t2 === undefined || t2.length === 0))
1551
+ if (d && (t2 === undefined || t2.length === 0))
1552
1552
  return `Please select at least one option.
1553
1553
  ${styleText2("reset", styleText2("dim", `Press ${styleText2(["gray", "bgWhite", "inverse"], " space ")} to select, ${styleText2("gray", styleText2("bgWhite", styleText2("inverse", " enter ")))} to submit`))}`;
1554
1554
  },
1555
1555
  render() {
1556
- const t2 = n2.withGuide ?? settings.withGuide, o2 = wrapTextWithPrefix(n2.output, n2.message, t2 ? `${symbolBar(this.state)} ` : "", `${symbol(this.state)} `), r2 = `${t2 ? `${styleText2("gray", S_BAR)}
1557
- ` : ""}${o2}
1558
- `, l2 = this.value ?? [], g2 = (i, u3) => {
1556
+ const t2 = n2.withGuide ?? settings.withGuide, a3 = wrapTextWithPrefix(n2.output, n2.message, t2 ? `${symbolBar(this.state)} ` : "", `${symbol(this.state)} `), r2 = `${t2 ? `${styleText2("gray", S_BAR)}
1557
+ ` : ""}${a3}
1558
+ `, l2 = this.value ?? [], p2 = (i, u3) => {
1559
1559
  if (i.disabled)
1560
- return a3(i, "disabled");
1560
+ return o2(i, "disabled");
1561
1561
  const s = l2.includes(i.value);
1562
- return u3 && s ? a3(i, "active-selected") : s ? a3(i, "selected") : a3(i, u3 ? "active" : "inactive");
1562
+ return u3 && s ? o2(i, "active-selected") : s ? o2(i, "selected") : o2(i, u3 ? "active" : "inactive");
1563
1563
  };
1564
1564
  switch (this.state) {
1565
1565
  case "submit": {
1566
- const i = this.options.filter(({ value: s }) => l2.includes(s)).map((s) => a3(s, "submitted")).join(styleText2("dim", ", ")) || styleText2("dim", "none"), u3 = wrapTextWithPrefix(n2.output, i, t2 ? `${styleText2("gray", S_BAR)} ` : "");
1566
+ const i = this.options.filter(({ value: s }) => l2.includes(s)).map((s) => o2(s, "submitted")).join(styleText2("dim", ", ")) || styleText2("dim", "none"), u3 = wrapTextWithPrefix(n2.output, i, t2 ? `${styleText2("gray", S_BAR)} ` : "");
1567
1567
  return `${r2}${u3}`;
1568
1568
  }
1569
1569
  case "cancel": {
1570
- const i = this.options.filter(({ value: s }) => l2.includes(s)).map((s) => a3(s, "cancelled")).join(styleText2("dim", ", "));
1570
+ const i = this.options.filter(({ value: s }) => l2.includes(s)).map((s) => o2(s, "cancelled")).join(styleText2("dim", ", "));
1571
1571
  if (i.trim() === "")
1572
1572
  return `${r2}${styleText2("gray", S_BAR)}`;
1573
1573
  const u3 = wrapTextWithPrefix(n2.output, i, t2 ? `${styleText2("gray", S_BAR)} ` : "");
@@ -1576,9 +1576,9 @@ ${styleText2("gray", S_BAR)}` : ""}`;
1576
1576
  }
1577
1577
  case "error": {
1578
1578
  const i = t2 ? `${styleText2("yellow", S_BAR)} ` : "", u3 = this.error.split(`
1579
- `).map((h2, x) => x === 0 ? `${t2 ? `${styleText2("yellow", S_BAR_END)} ` : ""}${styleText2("yellow", h2)}` : ` ${h2}`).join(`
1579
+ `).map(($, x) => x === 0 ? `${t2 ? `${styleText2("yellow", S_BAR_END)} ` : ""}${styleText2("yellow", $)}` : ` ${$}`).join(`
1580
1580
  `), s = r2.split(`
1581
- `).length, v = u3.split(`
1581
+ `).length, g2 = u3.split(`
1582
1582
  `).length + 1;
1583
1583
  return `${r2}${i}${limitOptions({
1584
1584
  output: n2.output,
@@ -1586,8 +1586,8 @@ ${styleText2("gray", S_BAR)}` : ""}`;
1586
1586
  cursor: this.cursor,
1587
1587
  maxItems: n2.maxItems,
1588
1588
  columnPadding: i.length,
1589
- rowPadding: s + v,
1590
- style: g2
1589
+ rowPadding: s + g2,
1590
+ style: p2
1591
1591
  }).join(`
1592
1592
  ${i}`)}
1593
1593
  ${u3}
@@ -1595,24 +1595,43 @@ ${u3}
1595
1595
  }
1596
1596
  default: {
1597
1597
  const i = t2 ? `${styleText2("cyan", S_BAR)} ` : "", u3 = r2.split(`
1598
- `).length, s = t2 ? 2 : 1;
1598
+ `).length, s = formatInstructionFooter(MULTISELECT_INSTRUCTIONS, t2), g2 = s.join(`
1599
+ `), $ = s.length + 1;
1599
1600
  return `${r2}${i}${limitOptions({
1600
1601
  output: n2.output,
1601
1602
  options: this.options,
1602
1603
  cursor: this.cursor,
1603
1604
  maxItems: n2.maxItems,
1604
1605
  columnPadding: i.length,
1605
- rowPadding: u3 + s,
1606
- style: g2
1606
+ rowPadding: u3 + $,
1607
+ style: p2
1607
1608
  }).join(`
1608
1609
  ${i}`)}
1609
- ${t2 ? styleText2("cyan", S_BAR_END) : ""}
1610
+ ${g2}
1610
1611
  `;
1611
1612
  }
1612
1613
  }
1613
1614
  }
1614
1615
  }).prompt();
1615
1616
  };
1617
+ var cancel = (o2 = "", t2) => {
1618
+ const i = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR_END)} ` : "";
1619
+ i.write(`${e}${styleText2("red", o2)}
1620
+
1621
+ `);
1622
+ };
1623
+ var intro = (o2 = "", t2) => {
1624
+ const i = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR_START)} ` : "";
1625
+ i.write(`${e}${o2}
1626
+ `);
1627
+ };
1628
+ var outro = (o2 = "", t2) => {
1629
+ const i = t2?.output ?? process.stdout, e = t2?.withGuide ?? settings.withGuide ? `${styleText2("gray", S_BAR)}
1630
+ ${styleText2("gray", S_BAR_END)} ` : "";
1631
+ i.write(`${e}${o2}
1632
+
1633
+ `);
1634
+ };
1616
1635
  var W = (l2) => styleText2("magenta", l2);
1617
1636
  var spinner = ({
1618
1637
  indicator: l2 = "dots",
@@ -1622,18 +1641,18 @@ var spinner = ({
1622
1641
  errorMessage: O,
1623
1642
  frames: E = unicode ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"],
1624
1643
  delay: F = unicode ? 80 : 120,
1625
- signal: m2,
1644
+ signal: m3,
1626
1645
  ...I
1627
1646
  } = {}) => {
1628
1647
  const u3 = isCI();
1629
- let M2, T3, d2 = false, S = false, s = "", p2, w = performance.now();
1648
+ let M2, T3, d = false, S = false, s = "", p2, w = performance.now();
1630
1649
  const x = getColumns(n2), k = I?.styleFrame ?? W, g2 = (e) => {
1631
1650
  const r2 = e > 1 ? O ?? settings.messages.error : G ?? settings.messages.cancel;
1632
- S = e === 1, d2 && (a3(r2, e), S && typeof h2 == "function" && h2());
1651
+ S = e === 1, d && (a3(r2, e), S && typeof h2 == "function" && h2());
1633
1652
  }, f2 = () => g2(2), i = () => g2(1), A = () => {
1634
- process.on("uncaughtExceptionMonitor", f2), process.on("unhandledRejection", f2), process.on("SIGINT", i), process.on("SIGTERM", i), process.on("exit", g2), m2 && m2.addEventListener("abort", i);
1653
+ process.on("uncaughtExceptionMonitor", f2), process.on("unhandledRejection", f2), process.on("SIGINT", i), process.on("SIGTERM", i), process.on("exit", g2), m3 && m3.addEventListener("abort", i);
1635
1654
  }, H = () => {
1636
- process.removeListener("uncaughtExceptionMonitor", f2), process.removeListener("unhandledRejection", f2), process.removeListener("SIGINT", i), process.removeListener("SIGTERM", i), process.removeListener("exit", g2), m2 && m2.removeEventListener("abort", i);
1655
+ process.removeListener("uncaughtExceptionMonitor", f2), process.removeListener("unhandledRejection", f2), process.removeListener("SIGINT", i), process.removeListener("SIGTERM", i), process.removeListener("exit", g2), m3 && m3.removeEventListener("abort", i);
1637
1656
  }, y = () => {
1638
1657
  if (p2 === undefined)
1639
1658
  return;
@@ -1649,7 +1668,7 @@ var spinner = ({
1649
1668
  const r2 = (performance.now() - e) / 1000, t2 = Math.floor(r2 / 60), o2 = Math.floor(r2 % 60);
1650
1669
  return t2 > 0 ? `[${t2}m ${o2}s]` : `[${o2}s]`;
1651
1670
  }, N = I.withGuide ?? settings.withGuide, P2 = (e = "") => {
1652
- d2 = true, M2 = block({ output: n2 }), s = C2(e), w = performance.now(), N && n2.write(`${styleText2("gray", S_BAR)}
1671
+ d = true, M2 = block({ output: n2 }), s = C2(e), w = performance.now(), N && n2.write(`${styleText2("gray", S_BAR)}
1653
1672
  `);
1654
1673
  let r2 = 0, t2 = 0;
1655
1674
  A(), T3 = setInterval(() => {
@@ -1673,9 +1692,9 @@ var spinner = ({
1673
1692
  n2.write(j), r2 = r2 + 1 < E.length ? r2 + 1 : 0, t2 = t2 < 4 ? t2 + 0.125 : 0;
1674
1693
  }, F);
1675
1694
  }, a3 = (e = "", r2 = 0, t2 = false) => {
1676
- if (!d2)
1695
+ if (!d)
1677
1696
  return;
1678
- d2 = false, clearInterval(T3), y();
1697
+ d = false, clearInterval(T3), y();
1679
1698
  const o2 = r2 === 0 ? styleText2("green", S_STEP_SUBMIT) : r2 === 1 ? styleText2("red", S_STEP_CANCEL) : styleText2("red", S_STEP_ERROR);
1680
1699
  s = e ?? s, t2 || (l2 === "timer" ? n2.write(`${o2} ${s} ${_2(w)}
1681
1700
  `) : n2.write(`${o2} ${s}
@@ -1700,20 +1719,24 @@ var u3 = {
1700
1719
  heavy: unicodeOr("\u2501", "="),
1701
1720
  block: unicodeOr("\u2588", "#")
1702
1721
  };
1703
- var c2 = (e, a3) => e.includes(`
1704
- `) ? e.split(`
1705
- `).map((t2) => a3(t2)).join(`
1706
- `) : a3(e);
1707
- var select = (e) => {
1708
- const a3 = (t2, d2) => {
1709
- const s = t2.label ?? String(t2.value);
1710
- switch (d2) {
1722
+ var SELECT_INSTRUCTIONS = [
1723
+ `${styleText2("dim", "\u2191/\u2193")} to navigate`,
1724
+ `${styleText2("dim", "Enter:")} confirm`
1725
+ ];
1726
+ var c2 = (t2, a3) => t2.includes(`
1727
+ `) ? t2.split(`
1728
+ `).map((i) => a3(i)).join(`
1729
+ `) : a3(t2);
1730
+ var select = (t2) => {
1731
+ const a3 = (i, m3) => {
1732
+ const s = i.label ?? String(i.value);
1733
+ switch (m3) {
1711
1734
  case "disabled":
1712
- return `${styleText2("gray", S_RADIO_INACTIVE)} ${c2(s, (n2) => styleText2("gray", n2))}${t2.hint ? ` ${styleText2("dim", `(${t2.hint ?? "disabled"})`)}` : ""}`;
1735
+ return `${styleText2("gray", S_RADIO_INACTIVE)} ${c2(s, (n2) => styleText2("gray", n2))}${i.hint ? ` ${styleText2("dim", `(${i.hint ?? "disabled"})`)}` : ""}`;
1713
1736
  case "selected":
1714
1737
  return `${c2(s, (n2) => styleText2("dim", n2))}`;
1715
1738
  case "active":
1716
- return `${styleText2("green", S_RADIO_ACTIVE)} ${s}${t2.hint ? ` ${styleText2("dim", `(${t2.hint})`)}` : ""}`;
1739
+ return `${styleText2("green", S_RADIO_ACTIVE)} ${s}${i.hint ? ` ${styleText2("dim", `(${i.hint})`)}` : ""}`;
1717
1740
  case "cancelled":
1718
1741
  return `${c2(s, (n2) => styleText2(["strikethrough", "dim"], n2))}`;
1719
1742
  default:
@@ -1721,39 +1744,40 @@ var select = (e) => {
1721
1744
  }
1722
1745
  };
1723
1746
  return new a2({
1724
- options: e.options,
1725
- signal: e.signal,
1726
- input: e.input,
1727
- output: e.output,
1728
- initialValue: e.initialValue,
1747
+ options: t2.options,
1748
+ signal: t2.signal,
1749
+ input: t2.input,
1750
+ output: t2.output,
1751
+ initialValue: t2.initialValue,
1729
1752
  render() {
1730
- const t2 = e.withGuide ?? settings.withGuide, d2 = `${symbol(this.state)} `, s = `${symbolBar(this.state)} `, n2 = wrapTextWithPrefix(e.output, e.message, s, d2), u4 = `${t2 ? `${styleText2("gray", S_BAR)}
1753
+ const i = t2.withGuide ?? settings.withGuide, m3 = `${symbol(this.state)} `, s = `${symbolBar(this.state)} `, n2 = wrapTextWithPrefix(t2.output, t2.message, s, m3), u4 = `${i ? `${styleText2("gray", S_BAR)}
1731
1754
  ` : ""}${n2}
1732
1755
  `;
1733
1756
  switch (this.state) {
1734
1757
  case "submit": {
1735
- const r2 = t2 ? `${styleText2("gray", S_BAR)} ` : "", l2 = wrapTextWithPrefix(e.output, a3(this.options[this.cursor], "selected"), r2);
1736
- return `${u4}${l2}`;
1758
+ const r2 = i ? `${styleText2("gray", S_BAR)} ` : "", o2 = wrapTextWithPrefix(t2.output, a3(this.options[this.cursor], "selected"), r2);
1759
+ return `${u4}${o2}`;
1737
1760
  }
1738
1761
  case "cancel": {
1739
- const r2 = t2 ? `${styleText2("gray", S_BAR)} ` : "", l2 = wrapTextWithPrefix(e.output, a3(this.options[this.cursor], "cancelled"), r2);
1740
- return `${u4}${l2}${t2 ? `
1762
+ const r2 = i ? `${styleText2("gray", S_BAR)} ` : "", o2 = wrapTextWithPrefix(t2.output, a3(this.options[this.cursor], "cancelled"), r2);
1763
+ return `${u4}${o2}${i ? `
1741
1764
  ${styleText2("gray", S_BAR)}` : ""}`;
1742
1765
  }
1743
1766
  default: {
1744
- const r2 = t2 ? `${styleText2("cyan", S_BAR)} ` : "", l2 = t2 ? styleText2("cyan", S_BAR_END) : "", g2 = u4.split(`
1745
- `).length, h2 = t2 ? 2 : 1;
1767
+ const r2 = i ? `${styleText2("cyan", S_BAR)} ` : "", o2 = u4.split(`
1768
+ `).length, $ = formatInstructionFooter(SELECT_INSTRUCTIONS, i), h2 = $.join(`
1769
+ `), b2 = $.length + 1;
1746
1770
  return `${u4}${r2}${limitOptions({
1747
- output: e.output,
1771
+ output: t2.output,
1748
1772
  cursor: this.cursor,
1749
1773
  options: this.options,
1750
- maxItems: e.maxItems,
1774
+ maxItems: t2.maxItems,
1751
1775
  columnPadding: r2.length,
1752
- rowPadding: g2 + h2,
1753
- style: (p2, b2) => a3(p2, p2.disabled ? "disabled" : b2 ? "active" : "inactive")
1776
+ rowPadding: o2 + b2,
1777
+ style: (p2, x) => a3(p2, p2.disabled ? "disabled" : x ? "active" : "inactive")
1754
1778
  }).join(`
1755
1779
  ${r2}`)}
1756
- ${l2}
1780
+ ${h2}
1757
1781
  `;
1758
1782
  }
1759
1783
  }
@@ -1775,10 +1799,10 @@ var text = (t2) => new n({
1775
1799
  `, c3 = t2.placeholder ? styleText2("inverse", t2.placeholder[0]) + styleText2("dim", t2.placeholder.slice(1)) : styleText2(["inverse", "hidden"], "_"), o2 = this.userInput ? this.userInputWithCursor : c3, a3 = this.value ?? "";
1776
1800
  switch (this.state) {
1777
1801
  case "error": {
1778
- const n2 = this.error ? ` ${styleText2("yellow", this.error)}` : "", r2 = i2 ? `${styleText2("yellow", S_BAR)} ` : "", d2 = i2 ? styleText2("yellow", S_BAR_END) : "";
1802
+ const n2 = this.error ? ` ${styleText2("yellow", this.error)}` : "", r2 = i2 ? `${styleText2("yellow", S_BAR)} ` : "", d = i2 ? styleText2("yellow", S_BAR_END) : "";
1779
1803
  return `${s.trim()}
1780
1804
  ${r2}${o2}
1781
- ${d2}${n2}
1805
+ ${d}${n2}
1782
1806
  `;
1783
1807
  }
1784
1808
  case "submit": {
@@ -1835,8 +1859,6 @@ class AddCommand {
1835
1859
  const options = changes.map((c3) => ({ value: c3.value, label: c3.label }));
1836
1860
  const selectedChanges = await MultiSelect({
1837
1861
  message: `Select the changes you want to commit.
1838
- ` + BLUE({ text: "[space] to select" }) + `
1839
- ` + GREEN({ text: "[enter] to confirm" }) + `
1840
1862
  ` + MAGENTA({ text: "[a] to select all" }) + `
1841
1863
  ` + RED({ text: "[esc] to cancel" }) + `
1842
1864
  `,
@@ -2036,7 +2058,9 @@ var ALIASES = [
2036
2058
  { name: "gstp", command: "stash pop", kind: "git" },
2037
2059
  { name: "gstl", command: "stash list", kind: "git" },
2038
2060
  { name: "gcl", command: "clean -fd", kind: "git" },
2039
- { name: "grh", command: "reset --hard", kind: "git" }
2061
+ { name: "grh", command: "reset --hard", kind: "git" },
2062
+ { name: "ggrh", command: "reset-hard", kind: "glinter" },
2063
+ { name: "ggclean", command: "reset --hard && git clean -fd", kind: "git" }
2040
2064
  ];
2041
2065
 
2042
2066
  // apps/cli/modules/alias/main.ts
@@ -2108,10 +2132,7 @@ var commitTypeOptions = [
2108
2132
  ];
2109
2133
 
2110
2134
  // apps/cli/utils/input.ts
2111
- async function Input({
2112
- message,
2113
- placeholder
2114
- }) {
2135
+ async function Input({ message, placeholder }) {
2115
2136
  const value = await text({
2116
2137
  message,
2117
2138
  placeholder,
@@ -2129,10 +2150,7 @@ async function Input({
2129
2150
  }
2130
2151
 
2131
2152
  // apps/cli/utils/select.ts
2132
- async function Select({
2133
- message,
2134
- options
2135
- }) {
2153
+ async function Select({ message, options }) {
2136
2154
  const selected = await select({
2137
2155
  message,
2138
2156
  options
@@ -2190,6 +2208,123 @@ async function commitCommand() {
2190
2208
  await commitCmd.execute();
2191
2209
  }
2192
2210
 
2211
+ // apps/cli/modules/reset-hard/app/reset-hard-command.ts
2212
+ var PREV_PAGE = "< Previous page";
2213
+ var NEXT_PAGE = "Next page >";
2214
+
2215
+ class ResetHardCommand {
2216
+ resetHardRepository;
2217
+ constructor(resetHardRepository) {
2218
+ this.resetHardRepository = resetHardRepository;
2219
+ }
2220
+ async execute() {
2221
+ try {
2222
+ let page = 0;
2223
+ const pageSize = 10;
2224
+ while (true) {
2225
+ const { commits, hasMore } = await this.resetHardRepository.getCommits(page, pageSize);
2226
+ if (commits.length === 0) {
2227
+ console.log(`${WARNING({ text: "No commits found." })}`);
2228
+ return;
2229
+ }
2230
+ const options = [];
2231
+ if (page > 0) {
2232
+ options.push({ value: PREV_PAGE, label: PREV_PAGE });
2233
+ }
2234
+ for (const commit of commits) {
2235
+ options.push({
2236
+ value: commit.hash,
2237
+ label: `${YELLOW({ text: commit.shortHash })} ${commit.subject}`
2238
+ });
2239
+ }
2240
+ if (hasMore) {
2241
+ options.push({ value: NEXT_PAGE, label: NEXT_PAGE });
2242
+ }
2243
+ const selected = await Select({
2244
+ message: "Select the commit you want to reset to.",
2245
+ options
2246
+ });
2247
+ if (selected === PREV_PAGE) {
2248
+ page -= 1;
2249
+ continue;
2250
+ }
2251
+ if (selected === NEXT_PAGE) {
2252
+ page += 1;
2253
+ continue;
2254
+ }
2255
+ await this.resetHardRepository.resetHard(selected);
2256
+ console.log(`${CHECK({ text: "Successfully reset to " + selected })}`);
2257
+ break;
2258
+ }
2259
+ } catch (error) {
2260
+ errorHandler(error);
2261
+ }
2262
+ }
2263
+ }
2264
+
2265
+ // apps/cli/modules/reset-hard/infra/bun-reset-hard.repository.ts
2266
+ var {$: $2 } = globalThis.Bun;
2267
+
2268
+ // apps/cli/modules/reset-hard/domain/commit.ts
2269
+ class Commit {
2270
+ props;
2271
+ constructor(props) {
2272
+ this.props = props;
2273
+ }
2274
+ get hash() {
2275
+ return this.props.hash;
2276
+ }
2277
+ get shortHash() {
2278
+ return this.props.hash.slice(0, 7);
2279
+ }
2280
+ get subject() {
2281
+ return this.props.subject;
2282
+ }
2283
+ static fromGitLog(output) {
2284
+ if (!output.trim())
2285
+ return [];
2286
+ const lines = output.split(`
2287
+ `).filter((line) => line.trim() !== "");
2288
+ return lines.map((line) => {
2289
+ const hash = line.substring(0, 40);
2290
+ const subject = line.substring(41).trim();
2291
+ return new Commit({ hash, subject });
2292
+ });
2293
+ }
2294
+ }
2295
+
2296
+ // apps/cli/modules/reset-hard/infra/bun-reset-hard.repository.ts
2297
+ class BunResetHardRepository {
2298
+ async getCommits(page, pageSize) {
2299
+ try {
2300
+ const skip = page * pageSize;
2301
+ const output = await $2`git --no-pager log -n ${pageSize + 1} --skip=${skip} --format="%H %s"`.quiet().text();
2302
+ const allCommits = Commit.fromGitLog(output);
2303
+ const hasMore = allCommits.length > pageSize;
2304
+ const commits = hasMore ? allCommits.slice(0, pageSize) : allCommits;
2305
+ return { commits, hasMore };
2306
+ } catch {
2307
+ throw new ServerError("Git log failed", "Could not retrieve commits");
2308
+ }
2309
+ }
2310
+ async resetHard(hash) {
2311
+ const proc = Bun.spawn(["git", "reset", "--hard", hash], {
2312
+ stdio: ["inherit", "inherit", "inherit"]
2313
+ });
2314
+ const exitCode = await proc.exited;
2315
+ if (exitCode !== 0) {
2316
+ throw new ServerError("Git reset failed", `Failed to reset to commit ${hash}`);
2317
+ }
2318
+ }
2319
+ }
2320
+
2321
+ // apps/cli/modules/reset-hard/main.ts
2322
+ async function resetHardCommand() {
2323
+ const resetHardRepository = new BunResetHardRepository;
2324
+ const resetHardCmd = new ResetHardCommand(resetHardRepository);
2325
+ await resetHardCmd.execute();
2326
+ }
2327
+
2193
2328
  // apps/cli/modules/setup/app/setup-aliases.use-case.ts
2194
2329
  class SetupAliasesUseCase {
2195
2330
  aliasRepository;
@@ -2210,11 +2345,7 @@ class SetupAliasesUseCase {
2210
2345
  }
2211
2346
 
2212
2347
  // apps/cli/utils/confirm.ts
2213
- async function Confirm({
2214
- message,
2215
- cancelMessage,
2216
- exitOnCancel = true
2217
- }) {
2348
+ async function Confirm({ message, cancelMessage, exitOnCancel = true }) {
2218
2349
  const confirmed = await confirm({
2219
2350
  message
2220
2351
  });
@@ -2360,12 +2491,7 @@ ${functionLine}
2360
2491
  return path2.join(this.getWindowsDocumentsPath(), "PowerShell", "Microsoft.PowerShell_profile.ps1");
2361
2492
  }
2362
2493
  getWindowsDocumentsPath() {
2363
- const proc = Bun.spawnSync([
2364
- "powershell",
2365
- "-NoProfile",
2366
- "-Command",
2367
- "[Environment]::GetFolderPath('MyDocuments')"
2368
- ], {
2494
+ const proc = Bun.spawnSync(["powershell", "-NoProfile", "-Command", "[Environment]::GetFolderPath('MyDocuments')"], {
2369
2495
  stdout: "pipe",
2370
2496
  stderr: "pipe"
2371
2497
  });
@@ -2516,11 +2642,11 @@ class SwitchCommand {
2516
2642
  }
2517
2643
 
2518
2644
  // apps/cli/modules/switch/infra/bun-switch-repository.ts
2519
- var {$: $2 } = globalThis.Bun;
2645
+ var {$: $3 } = globalThis.Bun;
2520
2646
  class BunSwitchRepository {
2521
2647
  async getBranches() {
2522
2648
  try {
2523
- const output = await $2`git branch -a`.quiet().text();
2649
+ const output = await $3`git branch -a`.quiet().text();
2524
2650
  if (!output.trim()) {
2525
2651
  return [];
2526
2652
  }
@@ -2556,7 +2682,8 @@ var COMMANDS_FN = {
2556
2682
  commit: commitCommand,
2557
2683
  switch: switchCommand,
2558
2684
  alias: aliasCommand,
2559
- setup: setupCommand
2685
+ setup: setupCommand,
2686
+ "reset-hard": resetHardCommand
2560
2687
  };
2561
2688
 
2562
2689
  // apps/cli/index.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jannael/glinter",
3
- "version": "1.2.8",
3
+ "version": "1.3.0",
4
4
  "description": "A high-performance, transparent Git wrapper with interactive staging",
5
5
  "type": "module",
6
6
  "private": false,
@@ -36,12 +36,12 @@
36
36
  "scripts": {
37
37
  "build": "bun build ./apps/cli/index.ts --outfile=./dist/index.js --target=bun",
38
38
  "prepublishOnly": "bun run build",
39
- "test": "vitest",
40
- "lint": "oxlint",
41
- "lint:fix": "oxlint --fix",
42
- "format": "prettier --write .",
43
- "format:check": "prettier --check .",
39
+ "test": "bun test",
44
40
  "type-check": "bunx tsc --noEmit",
41
+ "lint": "eslint .",
42
+ "lint:fix": "eslint --fix .",
43
+ "fmt": "prettier --write .",
44
+ "fmt:check": "prettier --check .",
45
45
  "prepare": "husky"
46
46
  },
47
47
  "bin": {
@@ -51,27 +51,23 @@
51
51
  "bun": ">=1.0.0"
52
52
  },
53
53
  "devDependencies": {
54
+ "@eslint/js": "10.0.1",
54
55
  "@types/bun": "1.3.14",
56
+ "eslint": "10.5.0",
57
+ "eslint-plugin-astro": "2.0.0",
55
58
  "husky": "9.1.7",
56
- "oxlint": "1.68.0",
57
- "prettier": "3.8.3",
59
+ "lint-staged": "17.0.8",
60
+ "prettier": "3.8.4",
58
61
  "prettier-plugin-astro": "0.14.1",
59
62
  "prettier-plugin-tailwindcss": "0.8.0",
60
- "vitest": "4.1.8",
61
- "lint-staged": "17.0.7",
62
- "wrangler": "4.98.0"
63
+ "typescript-eslint": "8.62.0",
64
+ "wrangler": "4.104.0"
63
65
  },
64
66
  "peerDependencies": {
65
67
  "bun": "1.3.14",
66
68
  "typescript": "6.0.3"
67
69
  },
68
70
  "dependencies": {
69
- "@clack/prompts": "1.5.1"
70
- },
71
- "lint-staged": {
72
- "*.{ts,tsx,js,jsx,astro}": [
73
- "bun run lint:fix",
74
- "bun run format"
75
- ]
71
+ "@clack/prompts": "1.6.0"
76
72
  }
77
73
  }