@laabroms/alias-cli 0.1.0 → 0.1.3
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/README.md +2 -30
- package/dist/cli.js +51 -27
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
[](https://www.npmjs.com/package/@laabroms/alias-cli)
|
|
15
15
|
[](https://opensource.org/licenses/MIT)
|
|
16
16
|
|
|
17
|
-
|
|
17
|
+
## Demo
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+

|
|
20
20
|
|
|
21
21
|
## Features
|
|
22
22
|
|
|
@@ -128,26 +128,6 @@ After making changes, the CLI automatically copies the `source` command to your
|
|
|
128
128
|
- Node.js >= 18.0.0
|
|
129
129
|
- Terminal with ANSI color support
|
|
130
130
|
|
|
131
|
-
## Development
|
|
132
|
-
|
|
133
|
-
```bash
|
|
134
|
-
# Clone the repo
|
|
135
|
-
git clone https://github.com/laabroms/alias-cli.git
|
|
136
|
-
cd alias-cli
|
|
137
|
-
|
|
138
|
-
# Install dependencies
|
|
139
|
-
npm install
|
|
140
|
-
|
|
141
|
-
# Run in dev mode
|
|
142
|
-
npm run dev
|
|
143
|
-
|
|
144
|
-
# Build for production
|
|
145
|
-
npm run build
|
|
146
|
-
|
|
147
|
-
# Type check
|
|
148
|
-
npm run typecheck
|
|
149
|
-
```
|
|
150
|
-
|
|
151
131
|
## Tech Stack
|
|
152
132
|
|
|
153
133
|
- **Ink** — React renderer for CLIs
|
|
@@ -156,14 +136,6 @@ npm run typecheck
|
|
|
156
136
|
- **tsup** — Fast bundler
|
|
157
137
|
- **tsx** — TypeScript execution
|
|
158
138
|
|
|
159
|
-
## Future Ideas
|
|
160
|
-
|
|
161
|
-
- [ ] Import/export alias sets
|
|
162
|
-
- [ ] Syntax highlighting for commands
|
|
163
|
-
- [ ] Multi-select delete
|
|
164
|
-
- [ ] Alias categories/tags
|
|
165
|
-
- [ ] Support for `.bash_aliases` and other config files
|
|
166
|
-
|
|
167
139
|
## Contributing
|
|
168
140
|
|
|
169
141
|
PRs welcome! Please open an issue first to discuss what you'd like to change.
|
package/dist/cli.js
CHANGED
|
@@ -13,6 +13,7 @@ import { render } from "ink";
|
|
|
13
13
|
// src/App.tsx
|
|
14
14
|
import React8, { useState as useState4, useEffect, useCallback } from "react";
|
|
15
15
|
import { Box as Box8, Text as Text8, useInput as useInput5, useApp } from "ink";
|
|
16
|
+
import fs2 from "fs";
|
|
16
17
|
import os2 from "os";
|
|
17
18
|
|
|
18
19
|
// src/aliases.ts
|
|
@@ -53,7 +54,8 @@ function loadAliases() {
|
|
|
53
54
|
try {
|
|
54
55
|
const configPath = getShellConfigPath();
|
|
55
56
|
const content = fs.readFileSync(configPath, "utf-8");
|
|
56
|
-
|
|
57
|
+
const allAliases = parseAliases(content);
|
|
58
|
+
return allAliases.filter((a) => a.name !== "alias-cli");
|
|
57
59
|
} catch (error) {
|
|
58
60
|
console.error("Failed to load aliases:", error);
|
|
59
61
|
return [];
|
|
@@ -67,14 +69,15 @@ function saveAliases(aliases) {
|
|
|
67
69
|
content = fs.readFileSync(configPath, "utf-8");
|
|
68
70
|
}
|
|
69
71
|
const lines = content.split("\n");
|
|
70
|
-
const
|
|
72
|
+
const MARKER = "# Aliases managed by alias-cli";
|
|
73
|
+
const filteredLines = lines.filter((line) => {
|
|
71
74
|
const trimmed = line.trim();
|
|
72
|
-
return !trimmed.startsWith("alias ");
|
|
75
|
+
return !trimmed.startsWith("alias ") && trimmed !== MARKER;
|
|
73
76
|
});
|
|
74
77
|
const newContent = [
|
|
75
|
-
...
|
|
78
|
+
...filteredLines,
|
|
76
79
|
"",
|
|
77
|
-
|
|
80
|
+
MARKER,
|
|
78
81
|
serializeAliases(aliases)
|
|
79
82
|
].join("\n");
|
|
80
83
|
const backupPath = `${configPath}.backup`;
|
|
@@ -142,10 +145,11 @@ function AliasList({ aliases, selectedIndex, isSearchMode }) {
|
|
|
142
145
|
import React4, { useState } from "react";
|
|
143
146
|
import { Box as Box4, Text as Text4, useInput } from "ink";
|
|
144
147
|
import TextInput from "ink-text-input";
|
|
145
|
-
function AddAliasModal({ onSave, onCancel }) {
|
|
148
|
+
function AddAliasModal({ onSave, onCancel, existingAliases }) {
|
|
146
149
|
const [name, setName] = useState("");
|
|
147
150
|
const [command, setCommand] = useState("");
|
|
148
151
|
const [focusedField, setFocusedField] = useState("name");
|
|
152
|
+
const isDuplicate = name && existingAliases.some((a) => a.name === name);
|
|
149
153
|
useInput((input, key) => {
|
|
150
154
|
if (key.tab || key.downArrow) {
|
|
151
155
|
setFocusedField((prev) => prev === "name" ? "command" : "name");
|
|
@@ -153,7 +157,7 @@ function AddAliasModal({ onSave, onCancel }) {
|
|
|
153
157
|
setFocusedField((prev) => prev === "command" ? "name" : "command");
|
|
154
158
|
} else if (key.escape) {
|
|
155
159
|
onCancel();
|
|
156
|
-
} else if (key.return && name && command) {
|
|
160
|
+
} else if (key.return && name && command && !isDuplicate) {
|
|
157
161
|
onSave(name, command);
|
|
158
162
|
}
|
|
159
163
|
});
|
|
@@ -168,7 +172,7 @@ function AddAliasModal({ onSave, onCancel }) {
|
|
|
168
172
|
width: 50
|
|
169
173
|
},
|
|
170
174
|
isFocusedName ? /* @__PURE__ */ React4.createElement(TextInput, { value: name, onChange: setName, placeholder: "e.g., gc" }) : /* @__PURE__ */ React4.createElement(Text4, { color: name ? "white" : "gray" }, name || "e.g., gc")
|
|
171
|
-
)), /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Box4, { marginBottom: 0 }, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: isFocusedCommand ? "cyan" : "gray" }, isFocusedCommand && "\u25B6 ", "Command:")), /* @__PURE__ */ React4.createElement(
|
|
175
|
+
), isDuplicate && /* @__PURE__ */ React4.createElement(Box4, { marginTop: 0 }, /* @__PURE__ */ React4.createElement(Text4, { color: "red" }, "\u26A0 Alias '", name, "' already exists"))), /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "column" }, /* @__PURE__ */ React4.createElement(Box4, { marginBottom: 0 }, /* @__PURE__ */ React4.createElement(Text4, { bold: true, color: isFocusedCommand ? "cyan" : "gray" }, isFocusedCommand && "\u25B6 ", "Command:")), /* @__PURE__ */ React4.createElement(
|
|
172
176
|
Box4,
|
|
173
177
|
{
|
|
174
178
|
borderStyle: "round",
|
|
@@ -328,7 +332,7 @@ function App() {
|
|
|
328
332
|
);
|
|
329
333
|
} else if (input === "a") {
|
|
330
334
|
setMode("add");
|
|
331
|
-
} else if (input === "e" && filteredAliases.length > 0) {
|
|
335
|
+
} else if ((input === "e" || key.return) && filteredAliases.length > 0) {
|
|
332
336
|
setMode("edit");
|
|
333
337
|
} else if ((input === "d" || key.delete) && filteredAliases.length > 0) {
|
|
334
338
|
setMode("delete");
|
|
@@ -341,28 +345,39 @@ function App() {
|
|
|
341
345
|
const configPath = getShellConfigPath();
|
|
342
346
|
const fileName = configPath.replace(os2.homedir(), "~");
|
|
343
347
|
const sourceCommand = `source ${fileName}`;
|
|
348
|
+
const configContent = fs2.readFileSync(configPath, "utf-8");
|
|
349
|
+
const hasAutoReload = configContent.includes("alias-cli()") && configContent.includes("command alias-cli");
|
|
344
350
|
exit();
|
|
345
|
-
console.log("\n\x1B[
|
|
346
|
-
|
|
347
|
-
|
|
351
|
+
console.log("\n\x1B[32;1m\u2728 Changes saved!\x1B[0m\n");
|
|
352
|
+
if (hasAutoReload) {
|
|
353
|
+
console.log(
|
|
354
|
+
"\x1B[2;32m\u26A1 Auto-reload is set up! Your aliases are now applied.\x1B[0m\n"
|
|
355
|
+
);
|
|
356
|
+
} else {
|
|
357
|
+
console.log("\x1B[2m\u{1F4CB} To apply your aliases, run:\x1B[0m");
|
|
358
|
+
console.log(`\x1B[7;36;1m ${sourceCommand} \x1B[0m
|
|
348
359
|
`);
|
|
349
|
-
try {
|
|
350
|
-
const { execSync } = __require("child_process");
|
|
351
360
|
try {
|
|
352
|
-
|
|
353
|
-
|
|
361
|
+
const { execSync } = __require("child_process");
|
|
362
|
+
try {
|
|
363
|
+
execSync(`echo '${sourceCommand}' | pbcopy`, { stdio: "ignore" });
|
|
364
|
+
console.log("\x1B[2;32m\u2713 Copied to clipboard!\x1B[0m\n");
|
|
365
|
+
} catch {
|
|
366
|
+
execSync(`echo '${sourceCommand}' | xclip -selection clipboard`, {
|
|
367
|
+
stdio: "ignore"
|
|
368
|
+
});
|
|
369
|
+
console.log("\x1B[2;32m\u2713 Copied to clipboard!\x1B[0m\n");
|
|
370
|
+
}
|
|
354
371
|
} catch {
|
|
355
|
-
execSync(`echo '${sourceCommand}' | xclip -selection clipboard`, { stdio: "ignore" });
|
|
356
|
-
console.log("\x1B[2m\u2713 Copied to clipboard! Just paste and run.\x1B[0m\n");
|
|
357
372
|
}
|
|
358
|
-
|
|
373
|
+
console.log(
|
|
374
|
+
"\x1B[2m\u26A1 Want aliases to auto-reload when you quit? Run this once:\x1B[0m\n"
|
|
375
|
+
);
|
|
376
|
+
console.log(
|
|
377
|
+
`\x1B[7;36m echo '\\nalias-cli() { command alias-cli; ${sourceCommand}; }' >> ${fileName} && ${sourceCommand} \x1B[0m
|
|
378
|
+
`
|
|
379
|
+
);
|
|
359
380
|
}
|
|
360
|
-
console.log("\x1B[33m\u26A1 Want auto-reload on quit?\x1B[0m");
|
|
361
|
-
console.log(`\x1B[2m Add this wrapper to your ${fileName}:\x1B[0m`);
|
|
362
|
-
console.log(`\x1B[2m alias-cli-reload() { command alias-cli && [ -f ~/.alias-cli-reload ] && source "$(cat ~/.alias-cli-reload)" && rm ~/.alias-cli-reload; }\x1B[0m`);
|
|
363
|
-
console.log(`\x1B[2m alias alias-cli='alias-cli-reload'\x1B[0m`);
|
|
364
|
-
console.log(`\x1B[2m Then run: source ${fileName}\x1B[0m
|
|
365
|
-
`);
|
|
366
381
|
} else {
|
|
367
382
|
exit();
|
|
368
383
|
}
|
|
@@ -420,7 +435,9 @@ function App() {
|
|
|
420
435
|
if (direction === "up") {
|
|
421
436
|
setSelectedIndex((prev) => Math.max(0, prev - 1));
|
|
422
437
|
} else {
|
|
423
|
-
setSelectedIndex(
|
|
438
|
+
setSelectedIndex(
|
|
439
|
+
(prev) => Math.min(filteredAliases.length - 1, prev + 1)
|
|
440
|
+
);
|
|
424
441
|
}
|
|
425
442
|
},
|
|
426
443
|
[filteredAliases.length]
|
|
@@ -462,7 +479,14 @@ function App() {
|
|
|
462
479
|
}
|
|
463
480
|
);
|
|
464
481
|
case "add":
|
|
465
|
-
return /* @__PURE__ */ React8.createElement(
|
|
482
|
+
return /* @__PURE__ */ React8.createElement(
|
|
483
|
+
AddAliasModal,
|
|
484
|
+
{
|
|
485
|
+
onSave: handleAdd,
|
|
486
|
+
onCancel: handleCancel,
|
|
487
|
+
existingAliases: aliases
|
|
488
|
+
}
|
|
489
|
+
);
|
|
466
490
|
case "edit":
|
|
467
491
|
return filteredAliases[selectedIndex] ? /* @__PURE__ */ React8.createElement(
|
|
468
492
|
EditAliasModal,
|