@jannael/glinter 1.1.1 → 1.2.1

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 +29 -7
  2. package/dist/index.js +672 -54
  3. package/package.json +10 -8
package/README.md CHANGED
@@ -1,23 +1,43 @@
1
- # Glinter
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
+ <br>
10
+ <br>
11
+ <br>
12
+ </p>
2
13
 
3
- Glinter is a high-performance, transparent Git wrapper built with **Bun**. It enhances the standard `git add` workflow with a beautiful, interactive CLI interface while acting as a seamless pass-through for all other Git commands.
14
+ Glinter is a high-performance, transparent Git wrapper built with **Bun**.
15
+
16
+ [Commands](https://glinter.jannael.com/#commands)
17
+
18
+ [Aliases](https://glinter.jannael.com/alias)
19
+
20
+ [Quick start](https://glinter.jannael.com/#quick-start)
4
21
 
5
22
  ## Preview
6
23
 
7
- <video src="https://github.com/user-attachments/assets/7c7cddef-d656-45e7-82e3-452cf669bbfc" controls="false" autoplay="true" loop="true" muted="true" style="max-width: 100%;">
24
+ <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%;">
8
25
  Your browser does not support the video tag.
9
26
  </video>
10
27
 
28
+ ### Screenshots
11
29
 
12
- ## Features
30
+ | `g add` | `g commit` |
31
+ |---|---|
32
+ | ![glinter add](./screenshots/ga.png) | ![glinter commit](./screenshots/gc.png) |
13
33
 
14
- - **Interactive `add`**: When you run `g add`, it presents a color-coded list of your modified, new, and deleted files. You can multi-select exactly what you want to stage using a GUI-like interface in your terminal.
34
+ ## Features
15
35
 
16
- - **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.
36
+ - **Abbreviation**: You can use `g` instead of `git`.
17
37
 
18
38
  - **Safe by Default**: Automatically filters and prevents accidental staging of sensitive files: `.env` and `node_modules`.
19
39
 
20
- - **Abbreviation**: You can use `g` instead of `git`.
40
+ - **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.
21
41
 
22
42
  ## How it works
23
43
 
@@ -52,7 +72,9 @@ npm install -g @jannael/glinter
52
72
  now you can simply run:
53
73
  ```bash
54
74
  g add # Opens the interactive selector
75
+ g commit # Opens commit type + message prompt
55
76
  g add <file> # Runs standard git add <file>
77
+ g commit -m "" # Runs standard git commit -m ""
56
78
  g status # Runs standard git status
57
79
  g push # Runs standard git push
58
80
  ```
package/dist/index.js CHANGED
@@ -90,22 +90,41 @@ var require_src = __commonJS((exports, module) => {
90
90
  module.exports = { cursor, scroll, erase, beep };
91
91
  });
92
92
 
93
- // src/utils/colors.ts
94
- var RESET = "\x1B[0m";
95
- var GREEN = ({ text }) => `\x1B[32m${text}${RESET}`;
96
- var YELLOW = ({ text }) => `\x1B[33m${text}${RESET}`;
97
- var RED = ({ text }) => `\x1B[31m${text}${RESET}`;
98
- var MAGENTA = ({ text }) => `\x1B[35m${text}${RESET}`;
99
- var BLUE = ({ text }) => `\x1B[34m${text}${RESET}`;
100
- var BLACK = ({ text }) => `\x1B[30m${text}${RESET}`;
101
- var BG_YELLOW = ({ text }) => `\x1B[43m${text}${RESET}`;
93
+ // apps/cli/commands.ts
94
+ var AVAILABLE_COMMANDS = [
95
+ {
96
+ name: "add",
97
+ command: "g add",
98
+ description: "Interactive add",
99
+ allowGitArgs: true
100
+ },
101
+ {
102
+ name: "commit",
103
+ command: "g commit",
104
+ description: "Interactive commit",
105
+ allowGitArgs: true
106
+ },
107
+ {
108
+ name: "switch",
109
+ command: "g switch",
110
+ description: "Interactive switch",
111
+ allowGitArgs: true
112
+ },
113
+ {
114
+ name: "alias",
115
+ command: "g alias",
116
+ description: "Show all the aliases",
117
+ allowGitArgs: false
118
+ },
119
+ {
120
+ name: "setup",
121
+ command: "g setup",
122
+ description: "Setup alias for git and glinter",
123
+ allowGitArgs: true
124
+ }
125
+ ];
102
126
 
103
- // src/utils/icons-terminal.ts
104
- var X = ({ text }) => RED({ text: `\u2716 ${text}` });
105
- var CHECK = ({ text }) => `${GREEN({ text: "\u2714" })} ${text}`;
106
- var WARNING = ({ text }) => BG_YELLOW({ text: BLACK({ text: ` \u26A0 ${text}` }) });
107
-
108
- // src/error/error-constructor.ts
127
+ // apps/cli/error/error-constructor.ts
109
128
  function CreateError(name) {
110
129
  const capitalize = (text) => text.charAt(0).toUpperCase() + text.slice(1);
111
130
  return class extends Error {
@@ -118,14 +137,29 @@ function CreateError(name) {
118
137
  };
119
138
  }
120
139
 
121
- // src/error/error-instance.ts
140
+ // apps/cli/error/error-instance.ts
122
141
  var NotFound = CreateError("NotFound");
123
142
  var Forbidden = CreateError("Forbidden");
124
143
  var Conflict = CreateError("Conflict");
125
144
  var ServerError = CreateError("ServerError");
126
145
  var BadRequest = CreateError("BadRequest");
127
146
 
128
- // src/error/error-handler.ts
147
+ // apps/cli/utils/colors.ts
148
+ var RESET = "\x1B[0m";
149
+ var GREEN = ({ text }) => `\x1B[32m${text}${RESET}`;
150
+ var YELLOW = ({ text }) => `\x1B[33m${text}${RESET}`;
151
+ var RED = ({ text }) => `\x1B[31m${text}${RESET}`;
152
+ var MAGENTA = ({ text }) => `\x1B[35m${text}${RESET}`;
153
+ var BLUE = ({ text }) => `\x1B[34m${text}${RESET}`;
154
+ var BLACK = ({ text }) => `\x1B[30m${text}${RESET}`;
155
+ var BG_YELLOW = ({ text }) => `\x1B[43m${text}${RESET}`;
156
+
157
+ // apps/cli/utils/icons-terminal.ts
158
+ var X = ({ text }) => RED({ text: `\u2716 ${text}` });
159
+ var CHECK = ({ text }) => `${GREEN({ text: "\u2714" })} ${text}`;
160
+ var WARNING = ({ text }) => BG_YELLOW({ text: BLACK({ text: ` \u26A0 ${text}` }) });
161
+
162
+ // apps/cli/error/error-handler.ts
129
163
  function errorHandler(error) {
130
164
  if (error instanceof ServerError) {
131
165
  console.error(X({ text: error.message }));
@@ -147,6 +181,7 @@ function errorHandler(error) {
147
181
  // node_modules/@clack/core/dist/index.mjs
148
182
  import { styleText as y } from "util";
149
183
  import { stdout as S, stdin as $ } from "process";
184
+ import * as _ from "readline";
150
185
  import P from "readline";
151
186
 
152
187
  // node_modules/fast-string-truncated-width/dist/utils.js
@@ -535,6 +570,7 @@ function wrapAnsi(string, columns, options) {
535
570
 
536
571
  // node_modules/@clack/core/dist/index.mjs
537
572
  var import_sisteransi = __toESM(require_src(), 1);
573
+ import { ReadStream as D } from "tty";
538
574
  function d(r, t, e) {
539
575
  if (!e.some((o) => !o.disabled))
540
576
  return r;
@@ -571,6 +607,28 @@ function w(r, t) {
571
607
  const e = r;
572
608
  e.isTTY && e.setRawMode(t);
573
609
  }
610
+ function z({ input: r = $, output: t = S, overwrite: e = true, hideCursor: s = true } = {}) {
611
+ const i = _.createInterface({ input: r, output: t, prompt: "", tabSize: 1 });
612
+ _.emitKeypressEvents(r, i), r instanceof D && r.isTTY && r.setRawMode(true);
613
+ const n = (o, { name: a, sequence: h }) => {
614
+ const l = String(o);
615
+ if (V([l, a, h], "cancel")) {
616
+ s && t.write(import_sisteransi.cursor.show), process.exit(0);
617
+ return;
618
+ }
619
+ if (!e)
620
+ return;
621
+ const f = a === "return" ? 0 : -1, v = a === "return" ? -1 : 0;
622
+ _.moveCursor(t, f, v, () => {
623
+ _.clearLine(t, 1, () => {
624
+ r.once("keypress", n);
625
+ });
626
+ });
627
+ };
628
+ return s && t.write(import_sisteransi.cursor.hide), r.once("keypress", n), () => {
629
+ r.off("keypress", n), s && t.write(import_sisteransi.cursor.show), r instanceof D && r.isTTY && !Y && r.setRawMode(false), i.terminal = false, i.close();
630
+ };
631
+ }
574
632
  var O = (r) => ("columns" in r) && typeof r.columns == "number" ? r.columns : 80;
575
633
  var A = (r) => ("rows" in r) && typeof r.rows == "number" ? r.rows : 20;
576
634
  function R(r, t, e, s = e) {
@@ -785,6 +843,24 @@ var H = class extends p {
785
843
  }
786
844
  }
787
845
  };
846
+
847
+ class Q extends p {
848
+ get cursor() {
849
+ return this.value ? 0 : 1;
850
+ }
851
+ get _value() {
852
+ return this.cursor === 0;
853
+ }
854
+ constructor(t) {
855
+ super(t, false), this.value = !!t.initialValue, this.on("userInput", () => {
856
+ this.value = this._value;
857
+ }), this.on("confirm", (e) => {
858
+ this.output.write(import_sisteransi.cursor.move(0, -1)), this.value = e, this.state = "submit", this.close();
859
+ }), this.on("cursor", () => {
860
+ this.value = !this.value;
861
+ });
862
+ }
863
+ }
788
864
  var X2 = { Y: { type: "year", len: 4 }, M: { type: "month", len: 2 }, D: { type: "day", len: 2 } };
789
865
  function L(r) {
790
866
  return [...r].map((t) => X2[t]);
@@ -1089,6 +1165,27 @@ class nt extends p {
1089
1165
  });
1090
1166
  }
1091
1167
  }
1168
+ class at extends p {
1169
+ get userInputWithCursor() {
1170
+ if (this.state === "submit")
1171
+ return this.userInput;
1172
+ const t = this.userInput;
1173
+ if (this.cursor >= t.length)
1174
+ return `${this.userInput}\u2588`;
1175
+ const e = t.slice(0, this.cursor), [s, ...i] = t.slice(this.cursor);
1176
+ return `${e}${y("inverse", s)}${i.join("")}`;
1177
+ }
1178
+ get cursor() {
1179
+ return this._cursor;
1180
+ }
1181
+ constructor(t) {
1182
+ super({ ...t, initialUserInput: t.initialUserInput ?? t.initialValue }), this.on("userInput", (e) => {
1183
+ this._setValue(e);
1184
+ }), this.on("finalize", () => {
1185
+ this.value || (this.value = t.defaultValue), this.value === undefined && (this.value = "");
1186
+ });
1187
+ }
1188
+ }
1092
1189
 
1093
1190
  // node_modules/@clack/prompts/dist/index.mjs
1094
1191
  import { styleText as t, stripVTControlCharacters as ne } from "util";
@@ -1098,6 +1195,7 @@ function Ze() {
1098
1195
  return P2.platform !== "win32" ? P2.env.TERM !== "linux" : !!P2.env.CI || !!P2.env.WT_SESSION || !!P2.env.TERMINUS_SUBLIME || P2.env.ConEmuTask === "{cmd::Cmder}" || P2.env.TERM_PROGRAM === "Terminus-Sublime" || P2.env.TERM_PROGRAM === "vscode" || P2.env.TERM === "xterm-256color" || P2.env.TERM === "alacritty" || P2.env.TERMINAL_EMULATOR === "JetBrains-JediTerm";
1099
1196
  }
1100
1197
  var ee = Ze();
1198
+ var ae = () => process.env.CI === "true";
1101
1199
  var w2 = (e, i) => ee ? e : i;
1102
1200
  var _e = w2("\u25C6", "*");
1103
1201
  var oe = w2("\u25A0", "x");
@@ -1175,7 +1273,7 @@ var Y2 = ({ cursor: e, options: i, style: s, output: r = process.stdout, maxItem
1175
1273
  }
1176
1274
  if (f > $2) {
1177
1275
  let b = 0, x = 0, G2 = f;
1178
- const M2 = e - v, R2 = (j2, D) => et2(h, G2, j2, D, $2);
1276
+ const M2 = e - v, R2 = (j2, D2) => et2(h, G2, j2, D2, $2);
1179
1277
  m ? ({ lineCount: G2, removals: b } = R2(0, M2), G2 > $2 && ({ lineCount: G2, removals: x } = R2(M2 + 1, h.length))) : ({ lineCount: G2, removals: x } = R2(M2 + 1, h.length), G2 > $2 && ({ lineCount: G2, removals: b } = R2(0, M2))), b > 0 && (m = true, h.splice(0, b)), x > 0 && (g = true, h.splice(h.length - x, x));
1180
1278
  }
1181
1279
  const C2 = [];
@@ -1185,10 +1283,49 @@ var Y2 = ({ cursor: e, options: i, style: s, output: r = process.stdout, maxItem
1185
1283
  C2.push(x);
1186
1284
  return g && C2.push(l), C2;
1187
1285
  };
1286
+ var ot2 = (e) => {
1287
+ const i = e.active ?? "Yes", s = e.inactive ?? "No";
1288
+ return new Q({ active: i, inactive: s, signal: e.signal, input: e.input, output: e.output, initialValue: e.initialValue ?? true, render() {
1289
+ const r = e.withGuide ?? u.withGuide, u2 = `${V2(this.state)} `, n = r ? `${t("gray", d2)} ` : "", o = R(e.output, e.message, n, u2), c2 = `${r ? `${t("gray", d2)}
1290
+ ` : ""}${o}
1291
+ `, a = this.value ? i : s;
1292
+ switch (this.state) {
1293
+ case "submit": {
1294
+ const l = r ? `${t("gray", d2)} ` : "";
1295
+ return `${c2}${l}${t("dim", a)}`;
1296
+ }
1297
+ case "cancel": {
1298
+ const l = r ? `${t("gray", d2)} ` : "";
1299
+ return `${c2}${l}${t(["strikethrough", "dim"], a)}${r ? `
1300
+ ${t("gray", d2)}` : ""}`;
1301
+ }
1302
+ default: {
1303
+ const l = r ? `${t("cyan", d2)} ` : "", $2 = r ? t("cyan", E2) : "";
1304
+ return `${c2}${l}${this.value ? `${t("green", z2)} ${i}` : `${t("dim", H2)} ${t("dim", i)}`}${e.vertical ? r ? `
1305
+ ${t("cyan", d2)} ` : `
1306
+ ` : ` ${t("dim", "/")} `}${this.value ? `${t("dim", H2)} ${t("dim", s)}` : `${t("green", z2)} ${s}`}
1307
+ ${$2}
1308
+ `;
1309
+ }
1310
+ }
1311
+ } }).prompt();
1312
+ };
1188
1313
  var pt = (e = "", i) => {
1189
1314
  const s = i?.output ?? process.stdout, r = i?.withGuide ?? u.withGuide ? `${t("gray", E2)} ` : "";
1190
1315
  s.write(`${r}${t("red", e)}
1191
1316
 
1317
+ `);
1318
+ };
1319
+ var mt = (e = "", i) => {
1320
+ const s = i?.output ?? process.stdout, r = i?.withGuide ?? u.withGuide ? `${t("gray", le)} ` : "";
1321
+ s.write(`${r}${e}
1322
+ `);
1323
+ };
1324
+ var gt = (e = "", i) => {
1325
+ const s = i?.output ?? process.stdout, r = i?.withGuide ?? u.withGuide ? `${t("gray", d2)}
1326
+ ${t("gray", E2)} ` : "";
1327
+ s.write(`${r}${e}
1328
+
1192
1329
  `);
1193
1330
  };
1194
1331
  var Q2 = (e, i) => e.split(`
@@ -1247,6 +1384,64 @@ ${r ? t("cyan", E2) : ""}
1247
1384
  }
1248
1385
  } }).prompt();
1249
1386
  };
1387
+ var Ct = (e) => t("magenta", e);
1388
+ var fe = ({ indicator: e = "dots", onCancel: i, output: s = process.stdout, cancelMessage: r, errorMessage: u2, frames: n = ee ? ["\u25D2", "\u25D0", "\u25D3", "\u25D1"] : ["\u2022", "o", "O", "0"], delay: o = ee ? 80 : 120, signal: c2, ...a } = {}) => {
1389
+ const l = ae();
1390
+ let $2, y2, p2 = false, m = false, g = "", S2, h = performance.now();
1391
+ const f = O(s), v = a?.styleFrame ?? Ct, T2 = (_2) => {
1392
+ const A2 = _2 > 1 ? u2 ?? u.messages.error : r ?? u.messages.cancel;
1393
+ m = _2 === 1, p2 && (W2(A2, _2), m && typeof i == "function" && i());
1394
+ }, C2 = () => T2(2), b = () => T2(1), x = () => {
1395
+ process.on("uncaughtExceptionMonitor", C2), process.on("unhandledRejection", C2), process.on("SIGINT", b), process.on("SIGTERM", b), process.on("exit", T2), c2 && c2.addEventListener("abort", b);
1396
+ }, G2 = () => {
1397
+ process.removeListener("uncaughtExceptionMonitor", C2), process.removeListener("unhandledRejection", C2), process.removeListener("SIGINT", b), process.removeListener("SIGTERM", b), process.removeListener("exit", T2), c2 && c2.removeEventListener("abort", b);
1398
+ }, M2 = () => {
1399
+ if (S2 === undefined)
1400
+ return;
1401
+ l && s.write(`
1402
+ `);
1403
+ const _2 = wrapAnsi(S2, f, { hard: true, trim: false }).split(`
1404
+ `);
1405
+ _2.length > 1 && s.write(import_sisteransi2.cursor.up(_2.length - 1)), s.write(import_sisteransi2.cursor.to(0)), s.write(import_sisteransi2.erase.down());
1406
+ }, R2 = (_2) => _2.replace(/\.+$/, ""), j2 = (_2) => {
1407
+ const A2 = (performance.now() - _2) / 1000, k2 = Math.floor(A2 / 60), L2 = Math.floor(A2 % 60);
1408
+ return k2 > 0 ? `[${k2}m ${L2}s]` : `[${L2}s]`;
1409
+ }, D2 = a.withGuide ?? u.withGuide, ie = (_2 = "") => {
1410
+ p2 = true, $2 = z({ output: s }), g = R2(_2), h = performance.now(), D2 && s.write(`${t("gray", d2)}
1411
+ `);
1412
+ let A2 = 0, k2 = 0;
1413
+ x(), y2 = setInterval(() => {
1414
+ if (l && g === S2)
1415
+ return;
1416
+ M2(), S2 = g;
1417
+ const L2 = v(n[A2]);
1418
+ let Z2;
1419
+ if (l)
1420
+ Z2 = `${L2} ${g}...`;
1421
+ else if (e === "timer")
1422
+ Z2 = `${L2} ${g} ${j2(h)}`;
1423
+ else {
1424
+ const Be = ".".repeat(Math.floor(k2)).slice(0, 3);
1425
+ Z2 = `${L2} ${g}${Be}`;
1426
+ }
1427
+ const Ne = wrapAnsi(Z2, f, { hard: true, trim: false });
1428
+ s.write(Ne), A2 = A2 + 1 < n.length ? A2 + 1 : 0, k2 = k2 < 4 ? k2 + 0.125 : 0;
1429
+ }, o);
1430
+ }, W2 = (_2 = "", A2 = 0, k2 = false) => {
1431
+ if (!p2)
1432
+ return;
1433
+ p2 = false, clearInterval(y2), M2();
1434
+ const L2 = A2 === 0 ? t("green", F2) : A2 === 1 ? t("red", oe) : t("red", ue);
1435
+ g = _2 ?? g, k2 || (e === "timer" ? s.write(`${L2} ${g} ${j2(h)}
1436
+ `) : s.write(`${L2} ${g}
1437
+ `)), G2(), $2();
1438
+ };
1439
+ return { start: ie, stop: (_2 = "") => W2(_2, 0), message: (_2 = "") => {
1440
+ g = R2(_2 ?? g);
1441
+ }, cancel: (_2 = "") => W2(_2, 1), error: (_2 = "") => W2(_2, 2), clear: () => W2("", 0, true), get isCancelled() {
1442
+ return m;
1443
+ } };
1444
+ };
1250
1445
  var Ve = { light: w2("\u2500", "-"), heavy: w2("\u2501", "="), block: w2("\u2588", "#") };
1251
1446
  var re = (e, i) => e.includes(`
1252
1447
  `) ? e.split(`
@@ -1294,8 +1489,37 @@ ${a}
1294
1489
  } }).prompt();
1295
1490
  };
1296
1491
  var je = `${t("gray", d2)} `;
1492
+ var Ot = (e) => new at({ validate: e.validate, placeholder: e.placeholder, defaultValue: e.defaultValue, initialValue: e.initialValue, output: e.output, signal: e.signal, input: e.input, render() {
1493
+ const i = e?.withGuide ?? u.withGuide, s = `${`${i ? `${t("gray", d2)}
1494
+ ` : ""}${V2(this.state)} `}${e.message}
1495
+ `, r = e.placeholder ? t("inverse", e.placeholder[0]) + t("dim", e.placeholder.slice(1)) : t(["inverse", "hidden"], "_"), u2 = this.userInput ? this.userInputWithCursor : r, n = this.value ?? "";
1496
+ switch (this.state) {
1497
+ case "error": {
1498
+ const o = this.error ? ` ${t("yellow", this.error)}` : "", c2 = i ? `${t("yellow", d2)} ` : "", a = i ? t("yellow", E2) : "";
1499
+ return `${s.trim()}
1500
+ ${c2}${u2}
1501
+ ${a}${o}
1502
+ `;
1503
+ }
1504
+ case "submit": {
1505
+ const o = n ? ` ${t("dim", n)}` : "", c2 = i ? t("gray", d2) : "";
1506
+ return `${s}${c2}${o}`;
1507
+ }
1508
+ case "cancel": {
1509
+ const o = n ? ` ${t(["strikethrough", "dim"], n)}` : "", c2 = i ? t("gray", d2) : "";
1510
+ return `${s}${c2}${o}${n.trim() ? `
1511
+ ${c2}` : ""}`;
1512
+ }
1513
+ default: {
1514
+ const o = i ? `${t("cyan", d2)} ` : "", c2 = i ? t("cyan", E2) : "";
1515
+ return `${s}${o}${u2}
1516
+ ${c2}
1517
+ `;
1518
+ }
1519
+ }
1520
+ } }).prompt();
1297
1521
 
1298
- // src/utils/multiselect.ts
1522
+ // apps/cli/utils/multiselect.ts
1299
1523
  async function MultiSelect({
1300
1524
  message,
1301
1525
  options
@@ -1312,7 +1536,7 @@ async function MultiSelect({
1312
1536
  return selected;
1313
1537
  }
1314
1538
 
1315
- // src/modules/add/app/add-command.ts
1539
+ // apps/cli/modules/add/app/add-command.ts
1316
1540
  class AddCommand {
1317
1541
  getChangesUseCase;
1318
1542
  stageChangesUseCase;
@@ -1332,7 +1556,7 @@ class AddCommand {
1332
1556
  ];
1333
1557
  const selectedChanges = await MultiSelect({
1334
1558
  message: `Select the changes you want to commit.
1335
- ` + BLUE({ text: "[space] to select and" }) + `
1559
+ ` + BLUE({ text: "[space] to select" }) + `
1336
1560
  ` + GREEN({ text: "[enter] to confirm" }) + `
1337
1561
  ` + MAGENTA({ text: "[a] to select all" }) + `
1338
1562
  ` + RED({ text: "[esc] to cancel" }) + `
@@ -1360,7 +1584,7 @@ ${WARNING({ text: " WARNING " })}`);
1360
1584
  }
1361
1585
  }
1362
1586
 
1363
- // src/modules/add/domain/change.ts
1587
+ // apps/cli/modules/add/domain/change.ts
1364
1588
  class Change {
1365
1589
  props;
1366
1590
  constructor(props) {
@@ -1427,7 +1651,7 @@ class Change {
1427
1651
  }
1428
1652
  }
1429
1653
 
1430
- // src/modules/add/app/get-changes.use-case.ts
1654
+ // apps/cli/modules/add/app/get-changes.use-case.ts
1431
1655
  class GetChangesUseCase {
1432
1656
  gitRepository;
1433
1657
  constructor(gitRepository) {
@@ -1454,7 +1678,7 @@ class GetChangesUseCase {
1454
1678
  }
1455
1679
  }
1456
1680
 
1457
- // src/modules/add/app/stage-changes.use-case.ts
1681
+ // apps/cli/modules/add/app/stage-changes.use-case.ts
1458
1682
  class StageChangesUseCase {
1459
1683
  gitRepository;
1460
1684
  constructor(gitRepository) {
@@ -1467,7 +1691,7 @@ class StageChangesUseCase {
1467
1691
  }
1468
1692
  }
1469
1693
 
1470
- // src/modules/add/infra/bun-git.repository.ts
1694
+ // apps/cli/modules/add/infra/bun-git.repository.ts
1471
1695
  var {$: $2 } = globalThis.Bun;
1472
1696
  class BunGitRepository {
1473
1697
  async getEntries() {
@@ -1494,7 +1718,7 @@ class BunGitRepository {
1494
1718
  }
1495
1719
  }
1496
1720
 
1497
- // src/modules/add/main.ts
1721
+ // apps/cli/modules/add/main.ts
1498
1722
  async function addCommand() {
1499
1723
  const gitRepository = new BunGitRepository;
1500
1724
  const getChangesUseCase = new GetChangesUseCase(gitRepository);
@@ -1503,7 +1727,405 @@ async function addCommand() {
1503
1727
  await addCommand2.execute();
1504
1728
  }
1505
1729
 
1506
- // src/modules/switch/domain/branch.ts
1730
+ // apps/cli/alias.ts
1731
+ var ALIASES = [
1732
+ { name: "gs", command: "status -sb", kind: "git" },
1733
+ {
1734
+ name: "gl",
1735
+ command: "log --oneline --decorate --graph --all -n 20",
1736
+ kind: "git"
1737
+ },
1738
+ { name: "gll", command: "log --stat", kind: "git" },
1739
+ { name: "gd", command: "diff --word-diff=color", kind: "git" },
1740
+ { name: "gds", command: "diff --staged --word-diff=color", kind: "git" },
1741
+ { name: "ga", command: "add", kind: "glinter" },
1742
+ { name: "gaa", command: "add -A", kind: "git" },
1743
+ { name: "gc", command: "commit", kind: "glinter" },
1744
+ { name: "gcm", command: "commit -m", kind: "git" },
1745
+ { name: "gca", command: "commit --amend", kind: "git" },
1746
+ { name: "gcan", command: "commit --amend --no-edit", kind: "git" },
1747
+ { name: "gb", command: "branch", kind: "git" },
1748
+ { name: "gba", command: "branch -a", kind: "git" },
1749
+ { name: "gco", command: "switch", kind: "glinter" },
1750
+ { name: "gcb", command: "checkout -b", kind: "git" },
1751
+ { name: "gpl", command: "pull", kind: "git" },
1752
+ { name: "gplr", command: "pull --rebase", kind: "git" },
1753
+ { name: "gp", command: "push", kind: "git" },
1754
+ { name: "ggpush", command: "push origin HEAD", kind: "git" },
1755
+ { name: "gpf", command: "push --force-with-lease", kind: "git" },
1756
+ { name: "gst", command: "stash", kind: "git" },
1757
+ { name: "gstp", command: "stash pop", kind: "git" },
1758
+ { name: "gstl", command: "stash list", kind: "git" },
1759
+ { name: "gcl", command: "clean -fd", kind: "git" },
1760
+ { name: "grh", command: "reset --hard", kind: "git" }
1761
+ ];
1762
+
1763
+ // apps/cli/modules/alias/main.ts
1764
+ function resolveAlias(name, command) {
1765
+ return { name, value: command };
1766
+ }
1767
+ function printAliases({
1768
+ title = "The following aliases are configured by setup:"
1769
+ } = {}) {
1770
+ console.log(YELLOW({ text: `
1771
+ ${title}
1772
+ ` }));
1773
+ for (const alias of ALIASES) {
1774
+ const tag = alias.kind === "glinter" ? MAGENTA({ text: "[glinter]" }) : BLUE({ text: "[git] " });
1775
+ const { name, value } = resolveAlias(alias.name, alias.command);
1776
+ console.log(` ${tag} ${GREEN({ text: name.padEnd(8) })} \u2192 ${value}`);
1777
+ }
1778
+ console.log("");
1779
+ }
1780
+ async function aliasCommand() {
1781
+ printAliases();
1782
+ }
1783
+
1784
+ // apps/cli/modules/commit/app/commit.use-case.ts
1785
+ class CommitUseCase {
1786
+ commitRepository;
1787
+ constructor(commitRepository) {
1788
+ this.commitRepository = commitRepository;
1789
+ }
1790
+ async execute({ message }) {
1791
+ await this.commitRepository.commit(message);
1792
+ }
1793
+ }
1794
+
1795
+ // apps/cli/commit-options.ts
1796
+ var commitTypeOptions = [
1797
+ {
1798
+ value: "feat",
1799
+ label: `${GREEN({ text: "feat" })}: A new feature`
1800
+ },
1801
+ {
1802
+ value: "fix",
1803
+ label: `${GREEN({ text: "fix" })}: A bug fix`
1804
+ },
1805
+ {
1806
+ value: "chore",
1807
+ label: `${GREEN({ text: "chore" })}: Routine maintenance`
1808
+ },
1809
+ {
1810
+ value: "docs",
1811
+ label: `${GREEN({ text: "docs" })}: Documentation updates`
1812
+ },
1813
+ {
1814
+ value: "refactor",
1815
+ label: `${GREEN({ text: "refactor" })}: Code changes without behavior change`
1816
+ },
1817
+ {
1818
+ value: "test",
1819
+ label: `${GREEN({ text: "test" })}: Add or update tests`
1820
+ },
1821
+ {
1822
+ value: "perf",
1823
+ label: `${GREEN({ text: "perf" })}: Performance improvements`
1824
+ },
1825
+ {
1826
+ value: "style",
1827
+ label: `${GREEN({ text: "style" })}: Formatting or style-only changes`
1828
+ }
1829
+ ];
1830
+
1831
+ // apps/cli/utils/input.ts
1832
+ async function Input({
1833
+ message,
1834
+ placeholder
1835
+ }) {
1836
+ const value = await Ot({
1837
+ message,
1838
+ placeholder,
1839
+ validate(input) {
1840
+ if (!input?.trim()) {
1841
+ return "Commit message is required.";
1842
+ }
1843
+ }
1844
+ });
1845
+ if (q(value)) {
1846
+ pt("Operation cancelled.");
1847
+ process.exit(0);
1848
+ }
1849
+ return value.trim();
1850
+ }
1851
+
1852
+ // apps/cli/utils/select.ts
1853
+ async function Select({
1854
+ message,
1855
+ options
1856
+ }) {
1857
+ const selected = await _t({
1858
+ message,
1859
+ options
1860
+ });
1861
+ if (q(selected)) {
1862
+ pt("Operation cancelled.");
1863
+ process.exit(0);
1864
+ }
1865
+ return selected;
1866
+ }
1867
+
1868
+ // apps/cli/modules/commit/app/commit-command.ts
1869
+ class CommitCommand {
1870
+ commitUseCase;
1871
+ constructor(commitUseCase) {
1872
+ this.commitUseCase = commitUseCase;
1873
+ }
1874
+ async execute() {
1875
+ try {
1876
+ const commitType = await Select({
1877
+ message: "Select the commit type.",
1878
+ options: commitTypeOptions
1879
+ });
1880
+ const commitDescription = await Input({
1881
+ message: "Write your commit message.",
1882
+ placeholder: "e.g. add interactive commit flow"
1883
+ });
1884
+ await this.commitUseCase.execute({
1885
+ message: `${commitType}: ${commitDescription}`
1886
+ });
1887
+ } catch (error) {
1888
+ errorHandler(error);
1889
+ }
1890
+ }
1891
+ }
1892
+
1893
+ // apps/cli/modules/commit/infra/bun-commit.repository.ts
1894
+ class BunCommitRepository {
1895
+ async commit(message) {
1896
+ const proc = Bun.spawn(["git", "commit", "-m", message], {
1897
+ stdio: ["inherit", "inherit", "inherit"]
1898
+ });
1899
+ const exitCode = await proc.exited;
1900
+ if (exitCode !== 0) {
1901
+ throw new ServerError("Git commit failed", "Could not create commit from interactive prompt");
1902
+ }
1903
+ }
1904
+ }
1905
+
1906
+ // apps/cli/modules/commit/main.ts
1907
+ async function commitCommand() {
1908
+ const commitRepository = new BunCommitRepository;
1909
+ const commitUseCase = new CommitUseCase(commitRepository);
1910
+ const commitCommand2 = new CommitCommand(commitUseCase);
1911
+ await commitCommand2.execute();
1912
+ }
1913
+
1914
+ // apps/cli/modules/setup/app/setup-aliases.use-case.ts
1915
+ class SetupAliasesUseCase {
1916
+ aliasRepository;
1917
+ constructor(aliasRepository) {
1918
+ this.aliasRepository = aliasRepository;
1919
+ }
1920
+ async execute() {
1921
+ for (const alias of ALIASES) {
1922
+ const { name, value } = this.resolveAlias(alias.name, alias.command, alias.kind);
1923
+ await this.aliasRepository.setAlias(name, value);
1924
+ }
1925
+ return { total: ALIASES.length };
1926
+ }
1927
+ resolveAlias(name, command, kind) {
1928
+ const aliasCommand2 = kind === "git" ? `git ${command}` : `g ${command}`;
1929
+ return { name, value: aliasCommand2 };
1930
+ }
1931
+ }
1932
+
1933
+ // apps/cli/utils/confirm.ts
1934
+ async function Confirm({
1935
+ message,
1936
+ cancelMessage,
1937
+ exitOnCancel = true
1938
+ }) {
1939
+ const confirmed = await ot2({
1940
+ message
1941
+ });
1942
+ if (q(confirmed)) {
1943
+ if (exitOnCancel) {
1944
+ pt(cancelMessage ?? "Operation cancelled.");
1945
+ process.exit(0);
1946
+ }
1947
+ return null;
1948
+ }
1949
+ return confirmed;
1950
+ }
1951
+
1952
+ // apps/cli/utils/intro.ts
1953
+ function Intro(message) {
1954
+ mt(message);
1955
+ }
1956
+
1957
+ // apps/cli/utils/outro.ts
1958
+ function Outro(message) {
1959
+ gt(message);
1960
+ }
1961
+
1962
+ // apps/cli/utils/spinner.ts
1963
+ function Spinner() {
1964
+ return fe();
1965
+ }
1966
+
1967
+ // apps/cli/modules/setup/app/setup-command.ts
1968
+ class SetupCommand {
1969
+ setupAliasesUseCase;
1970
+ constructor(setupAliasesUseCase) {
1971
+ this.setupAliasesUseCase = setupAliasesUseCase;
1972
+ }
1973
+ async execute() {
1974
+ try {
1975
+ Intro(MAGENTA({ text: " Glinter Setup " }));
1976
+ printAliases({ title: "The following aliases will be set globally:" });
1977
+ const confirmed = await Confirm({
1978
+ message: "Apply these aliases to your global git config?",
1979
+ exitOnCancel: false
1980
+ });
1981
+ if (!confirmed) {
1982
+ Outro("Setup cancelled.");
1983
+ return;
1984
+ }
1985
+ const spinner = Spinner();
1986
+ spinner.start("Writing aliases...");
1987
+ const { total } = await this.setupAliasesUseCase.execute();
1988
+ console.log("");
1989
+ spinner.stop(`${CHECK({ text: `${total} aliases configured successfully.` })}`);
1990
+ Outro(`${GREEN({ text: "GLINTER" })}, if you like the project, give a star on github: https://github.com/jannael/glinter`);
1991
+ console.log("");
1992
+ console.log(MAGENTA({ text: "To use the aliases, please restart your terminal." }));
1993
+ } catch (error) {
1994
+ errorHandler(error);
1995
+ }
1996
+ }
1997
+ }
1998
+
1999
+ // apps/cli/modules/setup/infra/bun-alias-unix.ts
2000
+ import fs from "fs";
2001
+ import os from "os";
2002
+ import path from "path";
2003
+
2004
+ class BunAliasUnix {
2005
+ async setAlias(name, value) {
2006
+ const profilePath = this.getUnixProfilePath();
2007
+ fs.mkdirSync(path.dirname(profilePath), { recursive: true });
2008
+ const current = fs.existsSync(profilePath) ? fs.readFileSync(profilePath, "utf8") : "";
2009
+ const updated = this.upsertUnixProfileAlias(current, name, value);
2010
+ fs.writeFileSync(profilePath, updated);
2011
+ }
2012
+ upsertUnixProfileAlias(content, name, value) {
2013
+ const escapedName = this.escapeRegex(name);
2014
+ const escapedValue = this.escapeSingleQuotes(value);
2015
+ const aliasLine = `alias ${name}='${escapedValue}'`;
2016
+ const aliasRegex = new RegExp(`^alias ${escapedName}=.*\\r?\\n?`, "gm");
2017
+ const cleaned = content.replace(aliasRegex, "").trimEnd();
2018
+ const prefix = cleaned.length > 0 ? `${cleaned}
2019
+ ` : "";
2020
+ return `${prefix}${aliasLine}
2021
+ `;
2022
+ }
2023
+ getUnixProfilePath() {
2024
+ const home = os.homedir();
2025
+ const shellName = path.basename(process.env.SHELL ?? "");
2026
+ const preferredProfile = shellName === "zsh" ? ".zshrc" : shellName === "bash" ? ".bashrc" : ".profile";
2027
+ const candidates = [preferredProfile, ".bashrc", ".zshrc", ".profile"];
2028
+ const uniqueCandidates = [...new Set(candidates)];
2029
+ for (const candidate of uniqueCandidates) {
2030
+ const candidatePath = path.join(home, candidate);
2031
+ if (fs.existsSync(candidatePath)) {
2032
+ return candidatePath;
2033
+ }
2034
+ }
2035
+ return path.join(home, preferredProfile);
2036
+ }
2037
+ escapeRegex(value) {
2038
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2039
+ }
2040
+ escapeSingleQuotes(value) {
2041
+ return value.replace(/'/g, `'"'"'`);
2042
+ }
2043
+ }
2044
+
2045
+ // apps/cli/modules/setup/infra/bun-alias-windows.ts
2046
+ import fs2 from "fs";
2047
+ import os2 from "os";
2048
+ import path2 from "path";
2049
+
2050
+ class BunAliasWindows {
2051
+ async setAlias(name, value) {
2052
+ const psProfilePath = this.getWindowsProfilePath();
2053
+ fs2.mkdirSync(path2.dirname(psProfilePath), { recursive: true });
2054
+ const current = fs2.existsSync(psProfilePath) ? fs2.readFileSync(psProfilePath, "utf8") : "";
2055
+ const updated = this.upsertWindowsProfileFunction(current, name, value);
2056
+ fs2.writeFileSync(psProfilePath, updated);
2057
+ }
2058
+ upsertWindowsProfileFunction(content, name, value) {
2059
+ const escapedName = this.escapeRegex(name);
2060
+ const removeAliasLine = `if (Get-Alias -Name ${name} -ErrorAction SilentlyContinue) { Remove-Item Alias:${name} -Force }`;
2061
+ const functionLine = `function ${name} { ${value} @args }`;
2062
+ const removeAliasRegex = new RegExp(`^if \\(Get-Alias -Name ${escapedName} -ErrorAction SilentlyContinue\\) \\{ Remove-Item Alias:${escapedName} -Force \\}\\r?\\n?`, "gm");
2063
+ const functionRegex = new RegExp(`^function ${escapedName} \\{[^\\r\\n]*\\}\\r?\\n?`, "gm");
2064
+ const cleaned = content.replace(removeAliasRegex, "").replace(functionRegex, "").trimEnd();
2065
+ const prefix = cleaned.length > 0 ? `${cleaned}
2066
+ ` : "";
2067
+ return `${prefix}${removeAliasLine}
2068
+ ${functionLine}
2069
+ `;
2070
+ }
2071
+ escapeRegex(value) {
2072
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
2073
+ }
2074
+ getWindowsProfilePath() {
2075
+ return path2.join(this.getWindowsDocumentsPath(), "PowerShell", "Microsoft.PowerShell_profile.ps1");
2076
+ }
2077
+ getWindowsDocumentsPath() {
2078
+ const proc = Bun.spawnSync([
2079
+ "powershell",
2080
+ "-NoProfile",
2081
+ "-Command",
2082
+ "[Environment]::GetFolderPath('MyDocuments')"
2083
+ ], {
2084
+ stdout: "pipe",
2085
+ stderr: "pipe"
2086
+ });
2087
+ if (proc.exitCode === 0) {
2088
+ const docsPath = new TextDecoder().decode(proc.stdout).trim();
2089
+ if (docsPath.length > 0) {
2090
+ return docsPath;
2091
+ }
2092
+ }
2093
+ return path2.join(os2.homedir(), "Documents");
2094
+ }
2095
+ }
2096
+
2097
+ // apps/cli/modules/setup/infra/bun-alias.repository.ts
2098
+ class BunAliasRepository {
2099
+ getOS() {
2100
+ return process.platform === "win32" ? "windows" : "unix";
2101
+ }
2102
+ async setAlias(name, value) {
2103
+ try {
2104
+ const system = this.getOS();
2105
+ if (system === "windows") {
2106
+ const bunAliasWindows = new BunAliasWindows;
2107
+ await bunAliasWindows.setAlias(name, value);
2108
+ }
2109
+ if (system === "unix") {
2110
+ const bunAliasUnix = new BunAliasUnix;
2111
+ await bunAliasUnix.setAlias(name, value);
2112
+ }
2113
+ console.log(` ${MAGENTA({ text: name.padEnd(8) })} set ${GREEN({ text: "successfully".padStart(10) })}`);
2114
+ } catch {
2115
+ throw new ServerError("Unexpected execution error", `Failed to set alias: ${name}`);
2116
+ }
2117
+ }
2118
+ }
2119
+
2120
+ // apps/cli/modules/setup/main.ts
2121
+ async function setupCommand() {
2122
+ const aliasRepository = new BunAliasRepository;
2123
+ const setupAliasesUseCase = new SetupAliasesUseCase(aliasRepository);
2124
+ const setupCommand2 = new SetupCommand(setupAliasesUseCase);
2125
+ await setupCommand2.execute();
2126
+ }
2127
+
2128
+ // apps/cli/modules/switch/domain/branch.ts
1507
2129
  class Branch {
1508
2130
  props;
1509
2131
  constructor(props) {
@@ -1539,7 +2161,7 @@ class Branch {
1539
2161
  }
1540
2162
  }
1541
2163
 
1542
- // src/modules/switch/app/get-branches.use-case.ts
2164
+ // apps/cli/modules/switch/app/get-branches.use-case.ts
1543
2165
  class GetBranchesUseCase {
1544
2166
  branchRepository;
1545
2167
  constructor(branchRepository) {
@@ -1552,7 +2174,7 @@ class GetBranchesUseCase {
1552
2174
  }
1553
2175
  }
1554
2176
 
1555
- // src/modules/switch/app/switch-branch.use-case.ts
2177
+ // apps/cli/modules/switch/app/switch-branch.use-case.ts
1556
2178
  class SwitchBranch {
1557
2179
  branchRepository;
1558
2180
  constructor(branchRepository) {
@@ -1563,23 +2185,7 @@ class SwitchBranch {
1563
2185
  }
1564
2186
  }
1565
2187
 
1566
- // src/utils/select.ts
1567
- async function Select({
1568
- message,
1569
- options
1570
- }) {
1571
- const selected = await _t({
1572
- message,
1573
- options
1574
- });
1575
- if (q(selected)) {
1576
- pt("Operation cancelled.");
1577
- process.exit(0);
1578
- }
1579
- return selected;
1580
- }
1581
-
1582
- // src/modules/switch/app/switch-command.ts
2188
+ // apps/cli/modules/switch/app/switch-command.ts
1583
2189
  class SwitchCommand {
1584
2190
  getBranchesUseCase;
1585
2191
  switchBranch;
@@ -1624,7 +2230,7 @@ class SwitchCommand {
1624
2230
  }
1625
2231
  }
1626
2232
 
1627
- // src/modules/switch/infra/bun-switch-repository.ts
2233
+ // apps/cli/modules/switch/infra/bun-switch-repository.ts
1628
2234
  var {$: $3 } = globalThis.Bun;
1629
2235
  class BunSwitchRepository {
1630
2236
  async getBranches() {
@@ -1650,7 +2256,7 @@ class BunSwitchRepository {
1650
2256
  }
1651
2257
  }
1652
2258
 
1653
- // src/modules/switch/main.ts
2259
+ // apps/cli/modules/switch/main.ts
1654
2260
  async function switchCommand() {
1655
2261
  const switchRepo = new BunSwitchRepository;
1656
2262
  const switchBranchUseCase = new SwitchBranch(switchRepo);
@@ -1659,13 +2265,25 @@ async function switchCommand() {
1659
2265
  await switchCommand2.execute();
1660
2266
  }
1661
2267
 
1662
- // src/index.ts
2268
+ // apps/cli/commands-fn.ts
2269
+ var COMMANDS_FN = {
2270
+ add: addCommand,
2271
+ commit: commitCommand,
2272
+ switch: switchCommand,
2273
+ alias: aliasCommand,
2274
+ setup: setupCommand
2275
+ };
2276
+
2277
+ // apps/cli/index.ts
1663
2278
  var args = Bun.argv.slice(2);
1664
- if (args[0] === "add" && !args[1])
1665
- await addCommand();
1666
- else if (args[0] === "switch" && !args[1])
1667
- await switchCommand();
1668
- else {
2279
+ var command = AVAILABLE_COMMANDS.find((command2) => command2.name === args[0]);
2280
+ if (command) {
2281
+ if (args.length > 1 && !command.allowGitArgs) {
2282
+ console.error("This command does not accept any arguments");
2283
+ process.exit(1);
2284
+ }
2285
+ await COMMANDS_FN[command.name]?.(args.slice(1));
2286
+ } else {
1669
2287
  const proc = Bun.spawn(["git", ...args], {
1670
2288
  stdio: ["inherit", "inherit", "inherit"]
1671
2289
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jannael/glinter",
3
- "version": "1.1.1",
3
+ "version": "1.2.1",
4
4
  "description": "A high-performance, transparent Git wrapper with interactive staging",
5
5
  "type": "module",
6
6
  "private": false,
@@ -13,7 +13,7 @@
13
13
  "name": "Jannael",
14
14
  "url": "https://github.com/jannael"
15
15
  },
16
- "homepage": "https://github.com/jannael/glinter#readme",
16
+ "homepage": "https://glinter.jannael.com",
17
17
  "bugs": {
18
18
  "url": "https://github.com/jannael/glinter/issues"
19
19
  },
@@ -34,12 +34,13 @@
34
34
  ".": "./dist/index.js"
35
35
  },
36
36
  "scripts": {
37
- "dev": "bun run src/index.ts",
38
- "build": "bun build ./src/index.ts --outfile=./dist/index.js --target=bun",
39
- "prepublishOnly": "bun run build",
37
+ "build:cli": "bun build ./apps/cli/index.ts --outfile=./dist/index.js --target=bun",
38
+ "prepublishOnly": "bun run build:cli",
39
+
40
40
  "test": "vitest",
41
41
  "lint": "biome check",
42
- "lint:fix": "biome format --write"
42
+ "lint:fix": "biome format --write",
43
+ "type-check": "bunx tsc --noEmit"
43
44
  },
44
45
  "bin": {
45
46
  "g": "./dist/index.js"
@@ -50,7 +51,8 @@
50
51
  "devDependencies": {
51
52
  "@biomejs/biome": "2.4.10",
52
53
  "@types/bun": "latest",
53
- "vitest": "4.1.4"
54
+ "vitest": "4.1.4",
55
+ "wrangler": "4.84.1"
54
56
  },
55
57
  "peerDependencies": {
56
58
  "bun": ">=1.0.0",
@@ -59,4 +61,4 @@
59
61
  "dependencies": {
60
62
  "@clack/prompts": "1.2.0"
61
63
  }
62
- }
64
+ }