Skip to content

Commit a013227

Browse files
Merge pull request #15 from udithavithanage/feature/saved-folders-remove-update
Add update and remove commands to CLI
2 parents e86a9c5 + abf8893 commit a013227

File tree

10 files changed

+143
-48
lines changed

10 files changed

+143
-48
lines changed

README.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ thyra config api /var/www/company/api
4444

4545
# Open instantly
4646
thyra open blog
47-
4847
# See everything you saved
4948
thyra list
5049

@@ -110,6 +109,34 @@ v1.0.5
110109

111110
This shows the currently installed version of **thyra**.
112111

112+
### Update a saved project
113+
114+
```bash
115+
thyra update <name> <path>
116+
```
117+
118+
Update the stored path for an existing alias. The folder must exist on disk.
119+
120+
**Example**
121+
122+
```bash
123+
thyra update blog ~/projects/personal-blog-v2
124+
```
125+
126+
### Remove a saved project
127+
128+
```bash
129+
thyra remove <name>
130+
```
131+
132+
Remove an alias from your saved mappings. This cannot be undone except by re-running `thyra config`.
133+
134+
**Example**
135+
136+
```bash
137+
thyra remove api
138+
```
139+
113140
### Help
114141

115142
```bash

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "thyra",
3-
"version": "1.1.1",
3+
"version": "1.2.0",
44
"description": "Simple project folder opener CLI",
55
"type": "module",
66
"bin": {

src/cli.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ import { runVersion } from "~/commands/version";
33
import { runOpen } from "~/commands/open";
44
import { runList } from "~/commands/list";
55
import { runHelp } from "~/commands/help";
6+
import { runRemove } from "~/commands/remove";
7+
import { runUpdate as runUpdateCmd } from "~/commands/update";
68

79
import { getConfigFilePath, ConfigStore } from "~/configStore";
810

@@ -32,6 +34,12 @@ import { getConfigFilePath, ConfigStore } from "~/configStore";
3234
case "open":
3335
runOpen(store, rest);
3436
break;
37+
case "remove":
38+
runRemove(store, rest);
39+
break;
40+
case "update":
41+
runUpdateCmd(store, rest);
42+
break;
3543
case "list":
3644
runList(store);
3745
break;

src/commands/config.ts

Lines changed: 1 addition & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,5 @@
1-
import fs from "node:fs";
2-
import os from "node:os";
3-
import path from "node:path";
4-
51
import type { ConfigStore } from "~/configStore";
6-
7-
function resolveFolderPath(inputPath: string): string {
8-
let folderPath = inputPath.replace(/^~(?=$|[\\/])/, os.homedir());
9-
folderPath = path.resolve(folderPath);
10-
return folderPath;
11-
}
12-
13-
function ensureDirectoryExists(folderPath: string): void {
14-
if (!fs.existsSync(folderPath)) {
15-
console.error(`Folder does not exist: ${folderPath}`);
16-
process.exit(1);
17-
}
18-
19-
const stat = fs.statSync(folderPath);
20-
if (!stat.isDirectory()) {
21-
console.error(`Path is not a directory: ${folderPath}`);
22-
process.exit(1);
23-
}
24-
}
2+
import { resolveFolderPath, ensureDirectoryExists } from "~/utils/path";
253

264
export function runConfig(store: ConfigStore, args: string[]): void {
275
const name = args[0];

src/commands/help.ts

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,8 @@ import { colorize, printCommandTable } from "~/color-logs";
55
export function runHelp(exitCode: number) {
66
console.log(
77
`\n${color.bold(color.cyan("thyra"))} ${color.dim(
8-
"- Quick shortcut manager for project folders",
9-
)}\n`,
8+
"- Quick shortcut manager for project folders"
9+
)}\n`
1010
);
1111

1212
const rows = [
@@ -18,6 +18,14 @@ export function runHelp(exitCode: number) {
1818
Command: colorize("thyra open <name>"),
1919
Description: "Open folder in your editor",
2020
},
21+
{
22+
Command: colorize("thyra update <name> <folder_path>"),
23+
Description: "Update an existing saved path",
24+
},
25+
{
26+
Command: colorize("thyra remove <name>"),
27+
Description: "Remove a saved path",
28+
},
2129
{ Command: colorize("thyra list"), Description: "Show all saved paths" },
2230
{ Command: colorize("thyra --version"), Description: "Show CLI version" },
2331
{ Command: colorize("thyra --help"), Description: "Show this help" },
@@ -27,15 +35,25 @@ export function runHelp(exitCode: number) {
2735

2836
console.log(
2937
`\n${color.bold(color.underline("Examples:"))}
30-
${colorize("thyra config <name> <folder_path>")} ${color.dim("# Save a path")}
38+
${colorize("thyra config <name> <folder_path>")} ${color.dim(
39+
"# Save a path"
40+
)}
3141
${colorize("thyra open <name>")} ${color.dim(
32-
"# Open in editor",
33-
)}
42+
"# Open in editor"
43+
)}
44+
${colorize("thyra update <name> <folder_path>")} ${color.dim(
45+
"# Update an existing saved path"
46+
)}
47+
${colorize("thyra remove <name>")} ${color.dim(
48+
"# Remove a saved path"
49+
)}
3450
${colorize("thyra --version")}
3551
3652
${color.bold(color.underline("Environment:"))}
37-
${color.cyan("THYRA_EDITOR")} ${color.dim('Editor command (default: "code")')}
38-
`,
53+
${color.cyan("THYRA_EDITOR")} ${color.dim(
54+
'Editor command (default: "code")'
55+
)}
56+
`
3957
);
4058

4159
if (typeof exitCode === "number") process.exit(exitCode);

src/commands/open.ts

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ function openInEditor(folderPath: string) {
2121

2222
if (error && editorCmd !== "explorer") {
2323
console.error(
24-
`Failed to start editor "${editorCmd}". Is it installed and on your PATH?`,
24+
`Failed to start editor "${editorCmd}". Is it installed and on your PATH?`
2525
);
2626
console.error(error.message);
2727
process.exit(1);
@@ -33,19 +33,7 @@ function openInEditor(folderPath: string) {
3333
}
3434

3535
import type { ConfigStore } from "~/configStore";
36-
37-
function ensureDirectoryExists(folderPath: string): void {
38-
if (!fs.existsSync(folderPath)) {
39-
console.error(`Folder does not exist: ${folderPath}`);
40-
process.exit(1);
41-
}
42-
43-
const stat = fs.statSync(folderPath);
44-
if (!stat.isDirectory()) {
45-
console.error(`Path is not a directory: ${folderPath}`);
46-
process.exit(1);
47-
}
48-
}
36+
import { ensureDirectoryExists } from "~/utils/path";
4937

5038
export function runOpen(store: ConfigStore, args: string[]): void {
5139
const name = args[0];
@@ -57,7 +45,7 @@ export function runOpen(store: ConfigStore, args: string[]): void {
5745

5846
if (!store.has(name)) {
5947
console.error(
60-
`No folder found for name "${name}". Use 'thyra list' to see saved entries.`,
48+
`No folder found for name "${name}". Use 'thyra list' to see saved entries.`
6149
);
6250
process.exit(1);
6351
}

src/commands/remove.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import type { ConfigStore } from "~/configStore";
2+
3+
export function runRemove(store: ConfigStore, args: string[]): void {
4+
const name = args[0];
5+
6+
if (!name) {
7+
console.error("Missing <name> argument for 'remove' command.");
8+
console.log("Usage: thyra remove <name>");
9+
process.exit(1);
10+
}
11+
12+
if (!store.has(name)) {
13+
console.error(
14+
`No folder found for name "${name}". Use 'thyra list' to see saved entries.`
15+
);
16+
process.exit(1);
17+
}
18+
19+
store.delete(name);
20+
console.log(`Removed mapping: "${name}"`);
21+
}

src/commands/update.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import type { ConfigStore } from "~/configStore";
2+
import { resolveFolderPath, ensureDirectoryExists } from "~/utils/path";
3+
4+
export function runUpdate(store: ConfigStore, args: string[]): void {
5+
const name = args[0];
6+
const folderArg = args[1];
7+
8+
if (!name || !folderArg) {
9+
console.error("Missing arguments for 'update' command.");
10+
console.log("Usage: thyra update <name> <folder_path>");
11+
process.exit(1);
12+
}
13+
14+
if (!store.has(name)) {
15+
console.error(
16+
`No folder found for name "${name}". Use 'thyra list' to see saved entries.`
17+
);
18+
process.exit(1);
19+
}
20+
21+
const folderPath = resolveFolderPath(folderArg);
22+
ensureDirectoryExists(folderPath);
23+
24+
store.set(name, folderPath);
25+
console.log(`Updated mapping: "${name}" -> ${folderPath}`);
26+
}

src/configStore.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ export class ConfigStore {
7373
fs.writeFileSync(
7474
this.filePath,
7575
JSON.stringify(this.data, null, 2),
76-
"utf8",
76+
"utf8"
7777
);
7878
} catch (err) {
7979
const error = err as Error;
@@ -95,6 +95,13 @@ export class ConfigStore {
9595
return Object.prototype.hasOwnProperty.call(this.data, key);
9696
}
9797

98+
delete(key: string): void {
99+
if (this.has(key)) {
100+
delete this.data[key];
101+
this.save();
102+
}
103+
}
104+
98105
all(): Record<string, string> {
99106
return { ...this.data };
100107
}

src/utils/path.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import fs from "node:fs";
2+
import os from "node:os";
3+
import path from "node:path";
4+
5+
export function resolveFolderPath(inputPath: string): string {
6+
let folderPath = inputPath.replace(/^~(?=$|[\\/])/, os.homedir());
7+
folderPath = path.resolve(folderPath);
8+
return folderPath;
9+
}
10+
11+
export function ensureDirectoryExists(folderPath: string): void {
12+
if (!fs.existsSync(folderPath)) {
13+
console.error(`Folder does not exist: ${folderPath}`);
14+
process.exit(1);
15+
}
16+
17+
const stat = fs.statSync(folderPath);
18+
if (!stat.isDirectory()) {
19+
console.error(`Path is not a directory: ${folderPath}`);
20+
process.exit(1);
21+
}
22+
}

0 commit comments

Comments
 (0)