twd-m4sc0 1.5.4__py3-none-any.whl → 2.0.0__py3-none-any.whl

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.
twd/crud.py ADDED
@@ -0,0 +1,94 @@
1
+ import os
2
+ import json
3
+ import hashlib
4
+ import time
5
+ from .logger import log, error
6
+ from collections import OrderedDict
7
+
8
+
9
+ def create_alias_id():
10
+ data = str(time.time()) + str(os.urandom(16))
11
+ return hashlib.sha256(data.encode()).hexdigest()[:12]
12
+
13
+
14
+ def get_data_file(config):
15
+ return os.path.expanduser(config.get("data_file", "~/.twd/data"))
16
+
17
+
18
+ def ensure_data_file_exists(config):
19
+ data_file = get_data_file(config)
20
+ if not os.path.exists(data_file):
21
+ try:
22
+ with open(data_file, "w") as f:
23
+ json.dump({}, f)
24
+ except OSError as e:
25
+ error(f"Error creating data file: {e}", config)
26
+
27
+
28
+ def load_data(config):
29
+ data_file = get_data_file(config)
30
+ if not os.path.exists(data_file):
31
+ ensure_data_file_exists(config)
32
+ try:
33
+ with open(data_file, "r") as f:
34
+ data = json.load(f)
35
+ return data
36
+ except json.JSONDecodeError as e:
37
+ error(f"Error reading data file: {e}", config)
38
+ return {}
39
+ except OSError as e:
40
+ error(f"Error reading data file: {e}", config)
41
+ return {}
42
+
43
+
44
+ def save_data(config, data):
45
+ data_file = get_data_file(config)
46
+ try:
47
+ sorted_data = OrderedDict(
48
+ sorted(data.items(), key=lambda item: item[1]["alias"])
49
+ )
50
+ with open(data_file, "w") as f:
51
+ json.dump(sorted_data, f, indent=4)
52
+ except OSError as e:
53
+ error(f"Error writing to data file: {e}", config)
54
+
55
+
56
+ def create_entry(config, data, path, alias=None):
57
+ alias_id = create_alias_id()
58
+ data[alias_id] = {
59
+ "path": path,
60
+ "alias": alias if alias else "no_alias",
61
+ "created_at": time.time(),
62
+ }
63
+ save_data(config, data)
64
+ return alias_id
65
+
66
+
67
+ def delete_entry(config, data, entry_id):
68
+ if entry_id in data:
69
+ del data[entry_id]
70
+ save_data(config, data)
71
+ else:
72
+ error(f"Entry ID {entry_id} not found", config)
73
+ raise KeyError(f"Entry ID {entry_id} not found")
74
+
75
+
76
+ def update_entry(config, data, entry_id, entry):
77
+ if entry_id in data:
78
+ data[entry_id] = entry
79
+ save_data(config, data)
80
+ else:
81
+ error(f"Entry ID {entry_id} not found", config)
82
+ raise KeyError(f"Entry ID {entry_id} not found")
83
+
84
+
85
+ def delete_data_file(config):
86
+ data_file = get_data_file(config)
87
+ if os.path.exists(data_file):
88
+ try:
89
+ os.remove(data_file)
90
+ except OSError as e:
91
+ error(f"Error deleting data file: {e}", config)
92
+ raise
93
+ else:
94
+ error("No data file found to delete", config)
twd/screen.py ADDED
@@ -0,0 +1,199 @@
1
+ import curses
2
+ import time
3
+ import os
4
+ from . import crud
5
+ from .logger import error
6
+
7
+ CONFIG = None
8
+ DIRS = None
9
+ filtered_DIRS = None
10
+ search_query = ""
11
+ original_DIRS = None
12
+
13
+
14
+ def draw_hr(stdscr, y, mode=None):
15
+ _, max_cols = stdscr.getmaxyx()
16
+ mode = mode if mode is not None else curses.A_NORMAL
17
+ stdscr.addstr(y, 1, "─" * (max_cols - 2), mode)
18
+
19
+
20
+ def filter_dirs_by_search(query):
21
+ global filtered_DIRS
22
+ filtered_DIRS = (
23
+ {k: v for k, v in DIRS.items() if query.lower() in v["alias"].lower()}
24
+ if query
25
+ else DIRS
26
+ )
27
+
28
+
29
+ def display_select_screen(stdscr):
30
+ global search_query, filtered_DIRS, original_DIRS
31
+ selected_entry = 0
32
+ pre_selected_path = None
33
+ confirm_mode = False
34
+ action = None
35
+ search_mode = False
36
+ post_search_mode = False
37
+
38
+ running = True
39
+
40
+ while running:
41
+ max_items = len(filtered_DIRS)
42
+ stdscr.clear()
43
+
44
+ # Border setup
45
+ height, width = stdscr.getmaxyx()
46
+ stdscr.addstr(0, 0, "╭")
47
+ stdscr.addstr(0, 1, "─" * (width - 2))
48
+ stdscr.addstr(0, width - 1, "╮")
49
+ stdscr.addstr(height - 1, 0, "╰")
50
+ stdscr.addstr(height - 1, 1, "─" * (width - 2))
51
+ stdscr.addstr(height - 2, width - 1, "╯")
52
+ for i in range(1, height - 1):
53
+ stdscr.addstr(i, 0, "│")
54
+ stdscr.addstr(i, width - 1, "│")
55
+
56
+ inner_height = height - 2
57
+ inner_width = width - 2
58
+ stdscr.addstr(1, 1, f"Current directory: {os.getcwd()}")
59
+
60
+ draw_hr(stdscr, 2)
61
+
62
+ # Header
63
+ max_alias_len = max(
64
+ max(len(entry["alias"]) for entry in filtered_DIRS.values()), 5
65
+ )
66
+ max_path_len = max(
67
+ max(len(entry["path"]) for entry in filtered_DIRS.values()), 4
68
+ )
69
+ max_id_len = max(max(len(alias_id) for alias_id in filtered_DIRS.keys()), 2)
70
+
71
+ alias_col = max_alias_len + 2
72
+ id_col = max_id_len + 2
73
+ path_col = max_path_len
74
+
75
+ header_text = f"{'ALIAS'.ljust(alias_col)}{'ID'.ljust(id_col)}{'PATH'.ljust(path_col)} CREATED AT"
76
+ stdscr.addstr(3, 1, header_text[:inner_width])
77
+
78
+ draw_hr(stdscr, 4)
79
+
80
+ # List entries
81
+ line_start = 5
82
+ for entry_id, entry in enumerate(filtered_DIRS.values()):
83
+ if line_start >= inner_height - 5:
84
+ break
85
+ alias = entry["alias"].ljust(max_alias_len)
86
+ path = entry["path"].ljust(max_path_len)
87
+ alias_id = list(filtered_DIRS.keys())[entry_id].ljust(max_id_len)
88
+ created_at = time.strftime(
89
+ "%Y-%m-%d %H:%M:%S", time.localtime(entry["created_at"])
90
+ )
91
+
92
+ line_text = f"{alias} {alias_id} {path} {created_at}"
93
+ if entry_id == selected_entry:
94
+ stdscr.addstr(line_start, 1, line_text[:inner_width], curses.A_REVERSE)
95
+ pre_selected_path = entry["path"]
96
+ else:
97
+ stdscr.addstr(line_start, 1, line_text[:inner_width])
98
+
99
+ line_start += 1
100
+
101
+ # Controls
102
+ controls_y = height - 5
103
+ draw_hr(stdscr, controls_y, curses.A_DIM)
104
+ controls_text = (
105
+ "ctrls: enter=select"
106
+ if search_mode
107
+ else "ctrls: ↑/k=up ↓/j=down enter=select d/backspace=delete q=exit search s=search"
108
+ if post_search_mode
109
+ else "ctrls: ↑/k=up ↓/j=down enter=select d/backspace=delete q=quit s=search"
110
+ )
111
+ stdscr.addstr(controls_y + 1, 1, controls_text, curses.A_DIM)
112
+
113
+ # Action area
114
+ action_area_y = height - 3
115
+ draw_hr(stdscr, action_area_y)
116
+
117
+ if search_mode:
118
+ stdscr.addstr(action_area_y + 1, 1, f"Search: {search_query}")
119
+ elif confirm_mode and action == "delete":
120
+ entry = filtered_DIRS[list(filtered_DIRS.keys())[selected_entry]]
121
+ stdscr.addstr(
122
+ action_area_y + 1,
123
+ 1,
124
+ f"Delete entry '{entry['alias']}' ({entry['path']})? [enter/q]",
125
+ )
126
+ elif pre_selected_path:
127
+ stdscr.addstr(
128
+ action_area_y + 1,
129
+ 1,
130
+ f"Command: cd {os.path.abspath(os.path.expanduser(pre_selected_path))}",
131
+ )
132
+
133
+ stdscr.refresh()
134
+
135
+ # Handle key events
136
+ key = stdscr.getch()
137
+
138
+ if search_mode:
139
+ if key == ord("\n"):
140
+ search_mode = False
141
+ post_search_mode = True
142
+ elif key == curses.KEY_BACKSPACE or key == 127:
143
+ search_query = search_query[:-1]
144
+ filter_dirs_by_search(search_query)
145
+ else:
146
+ search_query += chr(key)
147
+ filter_dirs_by_search(search_query)
148
+ elif post_search_mode:
149
+ if key == ord("q") or key == 27: # 'q' or 'esc'
150
+ filtered_DIRS = original_DIRS
151
+ post_search_mode = False
152
+ elif key == curses.KEY_UP or key == ord("k"):
153
+ selected_entry = max(0, selected_entry - 1)
154
+ elif key == curses.KEY_DOWN or key == ord("j"):
155
+ selected_entry = min(max_items - 1, selected_entry + 1)
156
+ elif key == ord("\n"):
157
+ selected_entry_id = list(filtered_DIRS.keys())[selected_entry]
158
+ return filtered_DIRS[selected_entry_id]
159
+ elif confirm_mode:
160
+ if key == ord("\n") and action == "delete":
161
+ selected_entry_id = list(filtered_DIRS.keys())[selected_entry]
162
+ data = crud.load_data(CONFIG)
163
+ try:
164
+ crud.delete_entry(CONFIG, data, selected_entry_id)
165
+ except KeyError:
166
+ error(f"Entry ID {selected_entry_id} not found", CONFIG)
167
+ del filtered_DIRS[selected_entry_id]
168
+ if selected_entry >= len(filtered_DIRS):
169
+ selected_entry = max(len(filtered_DIRS) - 1, 0)
170
+ confirm_mode = False
171
+ else:
172
+ confirm_mode = False
173
+ else:
174
+ if key == curses.KEY_UP or key == ord("k"):
175
+ selected_entry = (selected_entry - 1) % max_items
176
+ elif key == curses.KEY_DOWN or key == ord("j"):
177
+ selected_entry = (selected_entry + 1) % max_items
178
+ elif key == ord("\n"):
179
+ selected_entry_id = list(filtered_DIRS.keys())[selected_entry]
180
+ return filtered_DIRS[selected_entry_id]
181
+ elif key == ord("q"):
182
+ return None
183
+ elif key == ord("d") or key == curses.KEY_BACKSPACE:
184
+ confirm_mode = True
185
+ action = "delete"
186
+ elif key == ord("s"):
187
+ search_mode = True
188
+ selected_entry = 0
189
+
190
+
191
+ def display_select(config, dirs):
192
+ global CONFIG, DIRS, filtered_DIRS, search_query, original_DIRS
193
+ CONFIG = config
194
+ DIRS = dirs
195
+ filtered_DIRS = DIRS
196
+ original_DIRS = DIRS
197
+ search_query = ""
198
+ return curses.wrapper(display_select_screen)
199
+
twd/twd.py CHANGED
@@ -1,11 +1,12 @@
1
1
  import os
2
2
  import argparse
3
3
  import json
4
- import hashlib
5
4
  import time
6
5
  import re
7
6
  from importlib.metadata import version, PackageNotFoundError
8
7
  from .logger import log, error
8
+ from .screen import display_select
9
+ from . import crud
9
10
 
10
11
  TWD_DIR = os.path.join(os.path.expanduser("~"), ".twd")
11
12
  CONFIG_FILE = os.path.join(TWD_DIR, "config")
@@ -18,16 +19,10 @@ DEFAULT_CONFIG = {
18
19
  "log_format": "[$T]: $M",
19
20
  }
20
21
 
21
- # os.makedirs(TWD_DIR, exist_ok=True)
22
-
23
-
24
- def create_alias_id():
25
- data = str(time.time()) + str(os.urandom(16))
26
- return hashlib.sha256(data.encode()).hexdigest()[:12]
27
-
28
22
 
29
23
  def load_config():
30
24
  if not os.path.exists(CONFIG_FILE):
25
+ os.makedirs(TWD_DIR, exist_ok=True)
31
26
  with open(CONFIG_FILE, "w") as file:
32
27
  json.dump(DEFAULT_CONFIG, file, indent=4)
33
28
  return DEFAULT_CONFIG
@@ -42,20 +37,13 @@ def load_config():
42
37
 
43
38
  CONFIG = load_config()
44
39
 
45
- TWD_FILE = os.path.expanduser(CONFIG.get("data_file", "~/.twd/data"))
46
-
47
-
48
- def ensure_data_file_exists():
49
- if not os.path.exists(TWD_FILE):
50
- try:
51
- with open(TWD_FILE, "w") as f:
52
- json.dump({}, f)
53
- except OSError as e:
54
- error(f"Error creating data file: {e}", CONFIG)
40
+ # Ensure data files exist
41
+ crud.ensure_data_file_exists(CONFIG)
42
+ log_file = os.path.expanduser(CONFIG.get("log_file"))
43
+ error_file = os.path.expanduser(CONFIG.get("error_file"))
55
44
 
56
- log_file = os.path.expanduser(CONFIG.get("log_file"))
57
- error_file = os.path.expanduser(CONFIG.get("error_file"))
58
45
 
46
+ def ensure_log_error_files():
59
47
  if not os.path.exists(log_file):
60
48
  try:
61
49
  with open(log_file, "w+") as f:
@@ -71,7 +59,7 @@ def ensure_data_file_exists():
71
59
  error(f"Error creating error file: {e}", CONFIG)
72
60
 
73
61
 
74
- ensure_data_file_exists()
62
+ ensure_log_error_files()
75
63
 
76
64
 
77
65
  def get_absolute_path(path):
@@ -97,19 +85,18 @@ def output_handler(
97
85
  ):
98
86
  log(f"Type: {message_type}, Msg: {message or path}", CONFIG)
99
87
 
100
- if not output or CONFIG["output_behaviour"] == 0:
101
- return
102
-
103
- if not message and not path:
104
- return
105
-
106
- if message_type == 1:
107
- print(f"1;{message}")
108
- elif message_type == 0:
109
- if simple_output and path:
110
- print(f"0;{path}")
111
- elif not simple_output and message:
112
- print(f"0;{message}")
88
+ if CONFIG["output_behaviour"] == 1 or simple_output:
89
+ if path:
90
+ with open("/tmp/twd_path", "w") as f:
91
+ f.write(path)
92
+ if output:
93
+ print(path)
94
+ elif CONFIG["output_behaviour"] == 2:
95
+ if path:
96
+ with open("/tmp/twd_path", "w") as f:
97
+ f.write(path)
98
+ if output:
99
+ print(message)
113
100
 
114
101
 
115
102
  def save_directory(path=None, alias=None, output=True, simple_output=False):
@@ -121,26 +108,8 @@ def save_directory(path=None, alias=None, output=True, simple_output=False):
121
108
  if alias:
122
109
  alias = validate_alias(alias)
123
110
 
124
- try:
125
- with open(TWD_FILE, "r") as f:
126
- data = json.load(f)
127
- except json.JSONDecodeError as e:
128
- error(f"Error reading TWD file: {e}", CONFIG)
129
- data = {}
130
-
131
- alias_id = create_alias_id()
132
- data[alias_id] = {
133
- "path": path,
134
- "alias": alias if alias else alias_id,
135
- "created_at": time.time(),
136
- }
137
-
138
- try:
139
- with open(TWD_FILE, "w") as f:
140
- json.dump(data, f, indent=4)
141
- except OSError as e:
142
- error(f"Error writing to TWD file: {e}", CONFIG)
143
- raise
111
+ data = crud.load_data(CONFIG)
112
+ alias_id = crud.create_entry(CONFIG, data, path, alias)
144
113
 
145
114
  output_handler(
146
115
  f"Saved TWD to {path} with alias '{alias or alias_id}'",
@@ -151,27 +120,35 @@ def save_directory(path=None, alias=None, output=True, simple_output=False):
151
120
 
152
121
 
153
122
  def load_directory():
154
- if not os.path.exists(TWD_FILE):
155
- return None
156
-
157
- try:
158
- with open(TWD_FILE, "r") as f:
159
- return json.load(f)
160
- except json.JSONDecodeError as e:
161
- error(f"Error loading TWD file: {e}", CONFIG)
162
- return None
123
+ data = crud.load_data(CONFIG)
124
+ return data if data else None
163
125
 
164
126
 
165
- def go_to_directory(alias=None, output=True, simple_output=False):
127
+ def show_main(alias=None, output=True, simple_output=False):
166
128
  dirs = load_directory()
167
129
 
168
- if not dirs:
130
+ if dirs is None:
169
131
  output_handler("No TWD found", None, output, simple_output)
170
132
  return 1
171
133
  else:
172
- for entry_id, entry in dirs.items():
173
- if "alias" in entry and entry["alias"] and entry["alias"] == alias:
174
- TWD = entry["path"]
134
+ # Use alias if provided
135
+ if alias:
136
+ matched_dirs = []
137
+
138
+ for entry_id, entry in dirs.items():
139
+ if (
140
+ "alias" in entry
141
+ and entry["alias"]
142
+ and entry["alias"].startswith(alias)
143
+ ):
144
+ entry["id"] = entry_id
145
+ matched_dirs.append(entry)
146
+ elif entry_id.startswith(alias):
147
+ entry["id"] = entry_id
148
+ matched_dirs.append(entry)
149
+
150
+ if len(matched_dirs) == 1:
151
+ TWD = matched_dirs[0]["path"]
175
152
 
176
153
  if os.path.exists(TWD):
177
154
  output_handler(
@@ -184,9 +161,39 @@ def go_to_directory(alias=None, output=True, simple_output=False):
184
161
  f"Directory does not exist: {TWD}", None, output, simple_output
185
162
  )
186
163
  return 1
187
-
188
- output_handler("No TWD with alias found", None, output, simple_output)
189
- return 1
164
+ elif len(matched_dirs) > 1:
165
+ output_handler(
166
+ f"Multiple TWDs match for '{alias}':", None, output, simple_output
167
+ )
168
+ for match in matched_dirs:
169
+ output_handler(
170
+ f"{match['alias']} {match['id']} {match['path']}",
171
+ None,
172
+ output,
173
+ simple_output,
174
+ )
175
+ return 1
176
+ else:
177
+ output_handler("No TWD with alias found", None, output, simple_output)
178
+ return 1
179
+
180
+ # Display selection using curses if alias is not given
181
+ selected_dir = display_select(CONFIG, dirs)
182
+ if selected_dir is None:
183
+ output_handler("No TWD selected", None, output, simple_output)
184
+ return 0
185
+ else:
186
+ TWD = selected_dir["path"]
187
+
188
+ if os.path.exists(TWD):
189
+ output_handler(f"cd {TWD}", TWD, output, simple_output, message_type=1)
190
+ return 0
191
+ else:
192
+ error(f"Directory does not exist: {TWD}", CONFIG)
193
+ output_handler(
194
+ f"Directory does not exist: {TWD}", None, output, simple_output
195
+ )
196
+ return 1
190
197
 
191
198
 
192
199
  def show_directory(output=True, simple_output=False):
@@ -220,26 +227,22 @@ def show_directory(output=True, simple_output=False):
220
227
 
221
228
 
222
229
  def unset_directory(output=True, simple_output=False, force=False):
223
- if not os.path.exists(TWD_FILE):
224
- output_handler(f"No TWD file found", None, output, simple_output)
225
- else:
226
- if not force:
227
- output_handler(
228
- r"""If you want to execute deleting and therefore unsetting all set TWD's, please use "--force" or "-f" and run again.
229
-
230
+ if not force:
231
+ output_handler(
232
+ r"""If you want to execute deleting and therefore unsetting all set TWD's, please use "--force" or "-f" and run again.
230
233
 
231
234
  This feature is to prevent accidental execution.""",
232
- None,
233
- True,
234
- False,
235
- )
236
- return
237
- try:
238
- os.remove(TWD_FILE)
239
- except OSError as e:
240
- error(f"Error deleting TWD file: {e}", CONFIG)
241
- raise
242
- output_handler(f"TWD File deleted and TWD unset", None, output, simple_output)
235
+ None,
236
+ True,
237
+ False,
238
+ )
239
+ return
240
+ try:
241
+ crud.delete_data_file(CONFIG)
242
+ except OSError as e:
243
+ error(f"Error deleting TWD file: {e}", CONFIG)
244
+ raise
245
+ output_handler("TWD File deleted and TWD unset", None, output, simple_output)
243
246
 
244
247
 
245
248
  def get_package_version():
@@ -251,8 +254,6 @@ def get_package_version():
251
254
 
252
255
 
253
256
  def main():
254
- global TWD_FILE
255
-
256
257
  parser = argparse.ArgumentParser(
257
258
  description="Temporarily save and navigate to working directories."
258
259
  )
@@ -273,7 +274,7 @@ def main():
273
274
  parser.add_argument("-d", "--dir", nargs="?", help="Directory to save")
274
275
  parser.add_argument("-a", "--ali", nargs="?", help="Alias for the saved directory")
275
276
  parser.add_argument(
276
- "-g", "--go", nargs="?", const=None, help="Go to the saved directory"
277
+ "-g", "--go", nargs="?", const=" ", help="Go to the saved directory"
277
278
  )
278
279
  parser.add_argument("-l", "--list", action="store_true", help="Show saved TWD")
279
280
  parser.add_argument(
@@ -283,7 +284,7 @@ def main():
283
284
  "-v",
284
285
  "--version",
285
286
  action="version",
286
- version=f"TWD Version: {get_package_version()}",
287
+ version=f"TWD Version: v{get_package_version()}",
287
288
  help="Show the current version of TWD installed",
288
289
  )
289
290
  parser.add_argument("-f", "--force", action="store_true", help="Force an action")
@@ -305,24 +306,17 @@ def main():
305
306
  output = not args.no_output
306
307
  simple_output = args.simple_output
307
308
 
309
+ # Shell function
308
310
  if args.shell:
309
311
  print(rf"""
310
- function {args.shell}() {{
311
- output=$(python3 -m twd "$@");
312
- while IFS= read -r line; do
313
- if [[ -z "$line" ]]; then
314
- continue;
315
- fi;
316
- type=$(echo "$line" | cut -d';' -f1);
317
- message=$(echo "$line" | cut -d';' -f2-);
318
- if [[ "$type" == "1" ]]; then
319
- eval "$message";
320
- else
321
- echo "$message";
322
- fi;
323
- done <<< "$output";
324
- }}
325
- """)
312
+ function {args.shell}() {{
313
+ python3 -m twd "$@"
314
+ if [[ -f /tmp/twd_path ]]; then
315
+ cd "$(cat /tmp/twd_path)"
316
+ /bin/rm -f /tmp/twd_path
317
+ fi
318
+ }}
319
+ """)
326
320
  return 0
327
321
 
328
322
  directory = args.directory or args.dir
@@ -330,19 +324,24 @@ def main():
330
324
 
331
325
  if args.save:
332
326
  if not directory:
333
- directory = args.directory or os.getcwd()
327
+ directory = os.getcwd()
334
328
 
335
329
  alias = args.alias or args.ali
336
330
 
337
331
  save_directory(directory, alias, output, simple_output)
338
332
  elif args.go:
339
333
  alias = args.go
340
- return go_to_directory(alias, output, simple_output)
334
+ return show_main(alias, output, simple_output)
341
335
  elif args.list:
342
336
  show_directory(output, simple_output)
343
337
  elif args.unset:
344
338
  force = args.force
345
339
  unset_directory(output, simple_output, force)
346
340
  else:
347
- parser.print_help()
341
+ show_main(None, output, simple_output)
348
342
  return 1
343
+
344
+
345
+ if __name__ == "__main__":
346
+ main()
347
+
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: twd_m4sc0
3
- Version: 1.5.4
3
+ Version: 2.0.0
4
4
  Summary: A tool to temporarily save and go to a working directory
5
5
  Home-page: https://github.com/m4sc0/twd
6
6
  Author: m4sc0
@@ -16,6 +16,8 @@ License-File: LICENSE
16
16
 
17
17
  `twd-m4sc0` is a command-line tool that allows you to temporarily save a working directory and easily navigate back to it. It's designed for developers and users who frequently need to switch between directories in the terminal.
18
18
 
19
+ > All Versions `< v1.5` are considered deprecated and should not be used anymore because of the `config` file that was introduced in that version. This file is incompatible with newer versions and might cause issues or break the program.
20
+
19
21
  ## Features
20
22
 
21
23
  - Save the current or specified working directory.
@@ -23,6 +25,7 @@ License-File: LICENSE
23
25
  - List all saved directories with metadata.
24
26
  - Unset and delete saved directories.
25
27
  - Integrates with your shell for seamless directory management.
28
+ - Some options can be configured using the `config` file. For more information please visit the [Config](CONFIG.md) documentation.
26
29
 
27
30
  ## Installation
28
31
 
@@ -0,0 +1,14 @@
1
+ tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
+ tests/test_twd.py,sha256=XrxOo99oqida_7qZ48pA6BCazZekDmG5syw9NvjZWDU,484
3
+ twd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ twd/__main__.py,sha256=gM_Py51pQFH3CXdDIuPzBW6eNlFH1UHeRAmgAPWQyik,61
5
+ twd/crud.py,sha256=pp5QPt1NEC5Hxc-O5foqgjVnQnek7jUohW15bOOoJ98,2561
6
+ twd/logger.py,sha256=Pmhh4sOB-HyJAVBSwiESGRJElJoJ4Anhn-HVF8vxXzY,1595
7
+ twd/screen.py,sha256=X3531u1MysSwyqmmPWZUkkU-0PSOJowz319h6cr9vrA,7005
8
+ twd/twd.py,sha256=NS0-2Lbs1v4keG2itfxb2b5vYhpvEfmt2xPV9CdORE4,10563
9
+ twd_m4sc0-2.0.0.dist-info/LICENSE,sha256=eQSDjcD_fvOwfjmrzxKJhtZsSI39seMawuvsgeD_dfw,1062
10
+ twd_m4sc0-2.0.0.dist-info/METADATA,sha256=D0McQXnvy5PS_hLCRAARbn_8z7mq3YgoZrA9pA5UF3E,3528
11
+ twd_m4sc0-2.0.0.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
12
+ twd_m4sc0-2.0.0.dist-info/entry_points.txt,sha256=QfYDHHjipkVN4oalpACFmIeYHb7GQCJY4SC12bTsiQQ,37
13
+ twd_m4sc0-2.0.0.dist-info/top_level.txt,sha256=PXToru2Yr2Xh3F_F-pHXtuOQVp5x7KKCPFf94P_VI5U,10
14
+ twd_m4sc0-2.0.0.dist-info/RECORD,,
@@ -1,12 +0,0 @@
1
- tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
- tests/test_twd.py,sha256=XrxOo99oqida_7qZ48pA6BCazZekDmG5syw9NvjZWDU,484
3
- twd/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- twd/__main__.py,sha256=gM_Py51pQFH3CXdDIuPzBW6eNlFH1UHeRAmgAPWQyik,61
5
- twd/logger.py,sha256=Pmhh4sOB-HyJAVBSwiESGRJElJoJ4Anhn-HVF8vxXzY,1595
6
- twd/twd.py,sha256=EUsAOweKNl3zV2QnITkEjdG6Nv6UQjaUu3w7RaR__nQ,10344
7
- twd_m4sc0-1.5.4.dist-info/LICENSE,sha256=eQSDjcD_fvOwfjmrzxKJhtZsSI39seMawuvsgeD_dfw,1062
8
- twd_m4sc0-1.5.4.dist-info/METADATA,sha256=Mg_V_ltMMGp2hBInKxtZf8bokIpNJUDS1wBKJgR_F7s,3157
9
- twd_m4sc0-1.5.4.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
10
- twd_m4sc0-1.5.4.dist-info/entry_points.txt,sha256=QfYDHHjipkVN4oalpACFmIeYHb7GQCJY4SC12bTsiQQ,37
11
- twd_m4sc0-1.5.4.dist-info/top_level.txt,sha256=PXToru2Yr2Xh3F_F-pHXtuOQVp5x7KKCPFf94P_VI5U,10
12
- twd_m4sc0-1.5.4.dist-info/RECORD,,