redo-cli 0.1.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.
modules/ui.py ADDED
@@ -0,0 +1,433 @@
1
+ from rich import box
2
+ from rich.console import Console, Group
3
+ from rich.panel import Panel
4
+ from rich.syntax import Syntax
5
+ from rich.table import Table
6
+ from rich.text import Text
7
+ from rich.theme import Theme
8
+
9
+
10
+ ASCII_BANNER = r"""
11
+ /$$$$$$$ /$$
12
+ | $$__ $$ | $$
13
+ | $$ \ $$ /$$$$$$ /$$$$$$$ /$$$$$$
14
+ | $$$$$$$/ /$$__ $$ /$$__ $$ /$$__ $$
15
+ | $$__ $$| $$$$$$$$| $$ | $$| $$ \ $$
16
+ | $$ \ $$| $$_____/| $$ | $$| $$ | $$
17
+ | $$ | $$| $$$$$$$| $$$$$$$| $$$$$$/
18
+ |__/ |__/ \_______/ \_______/ \______/
19
+ """.strip("\n")
20
+
21
+ theme = Theme(
22
+ {
23
+ "brand": "bold steel_blue",
24
+ "muted": "grey58",
25
+ "success": "green4",
26
+ "warning": "dark_orange3",
27
+ "error": "red3",
28
+ "number": "grey82",
29
+ "command": "bright_white",
30
+ }
31
+ )
32
+ console = Console(theme=theme, highlight=False)
33
+
34
+ BRAND = "bold steel_blue"
35
+ MUTED = "grey58"
36
+ SUCCESS = "green4"
37
+ WARNING = "dark_orange3"
38
+ ERROR = "red3"
39
+ NUMBER = "grey82"
40
+ PANEL_BORDER = "grey50"
41
+ TABLE_BORDER = "grey46"
42
+ BANNER_START = "#6f86a8"
43
+ BANNER_END = "#b39ddb"
44
+
45
+
46
+ def _hex_to_rgb(value):
47
+ value = value.lstrip("#")
48
+ return tuple(int(value[index : index + 2], 16) for index in (0, 2, 4))
49
+
50
+
51
+ def _rgb_to_hex(rgb):
52
+ return "#{:02x}{:02x}{:02x}".format(*rgb)
53
+
54
+
55
+ def _blend(start, end, ratio):
56
+ return tuple(round(start[index] + (end[index] - start[index]) * ratio) for index in range(3))
57
+
58
+
59
+ def _gradient_text(text, start_color=BANNER_START, end_color=BANNER_END):
60
+ start = _hex_to_rgb(start_color)
61
+ end = _hex_to_rgb(end_color)
62
+ lines = text.splitlines()
63
+ total = max(len(lines) - 1, 1)
64
+ output = Text()
65
+
66
+ for index, line in enumerate(lines):
67
+ color = _rgb_to_hex(_blend(start, end, index / total))
68
+ output.append(line, style=f"bold {color}")
69
+ if index != len(lines) - 1:
70
+ output.append("\n")
71
+
72
+ return output
73
+
74
+
75
+ def _status_line(label, message, style):
76
+ text = Text()
77
+ text.append(f"[{label}] ", style=style)
78
+ text.append(message)
79
+ console.print(text)
80
+
81
+
82
+ def _plural(count, singular, plural=None):
83
+ if count == 1:
84
+ return f"{count} {singular}"
85
+ return f"{count} {plural or singular + 's'}"
86
+
87
+
88
+ def _format_seconds(seconds):
89
+ seconds = int(seconds)
90
+ minutes, remaining_seconds = divmod(seconds, 60)
91
+ hours, remaining_minutes = divmod(minutes, 60)
92
+
93
+ parts = []
94
+ if hours:
95
+ parts.append(f"{hours}h")
96
+ if remaining_minutes:
97
+ parts.append(f"{remaining_minutes}m")
98
+ if remaining_seconds or not parts:
99
+ parts.append(f"{remaining_seconds}s")
100
+
101
+ return " ".join(parts)
102
+
103
+
104
+ def _metadata_table(rows):
105
+ table = Table.grid(padding=(0, 2))
106
+ table.add_column(style=MUTED, no_wrap=True)
107
+ table.add_column()
108
+
109
+ for label, value in rows:
110
+ table.add_row(label, str(value))
111
+
112
+ return table
113
+
114
+
115
+ def print_success(message):
116
+ _status_line("SUCCESS", message, SUCCESS)
117
+
118
+
119
+ def print_error(message):
120
+ _status_line("ERROR", message, ERROR)
121
+
122
+
123
+ def print_warning(message):
124
+ _status_line("WARNING", message, WARNING)
125
+
126
+
127
+ def show_banner():
128
+ console.print(_gradient_text(ASCII_BANNER))
129
+ console.print(Text("Bookmarks for terminal workflows.", style=MUTED))
130
+ console.print(Text("Run redo --help for commands or redo --info for project details.", style=MUTED))
131
+
132
+
133
+ def show_info(version, credit):
134
+ metadata = _metadata_table(
135
+ [
136
+ ("Version", version),
137
+ ("Credit", credit),
138
+ ("Storage", "redo path"),
139
+ ("Guide", "redo guide"),
140
+ ]
141
+ )
142
+
143
+ console.print(_gradient_text(ASCII_BANNER))
144
+ console.print(
145
+ Panel(
146
+ metadata,
147
+ title="Redo info",
148
+ border_style=PANEL_BORDER,
149
+ box=box.ROUNDED,
150
+ )
151
+ )
152
+
153
+
154
+ def show_help_menu(version):
155
+ console.print(_gradient_text(ASCII_BANNER))
156
+ console.print(Text("Bookmarks for terminal workflows.", style=MUTED))
157
+
158
+ daily = Table(
159
+ title="Daily workflow",
160
+ box=box.ROUNDED,
161
+ border_style=TABLE_BORDER,
162
+ header_style=BRAND,
163
+ )
164
+ daily.add_column("Command", no_wrap=True)
165
+ daily.add_column("Purpose")
166
+ daily.add_row("redo new <name>", "Save a reusable workflow")
167
+ daily.add_row("redo run <name>", "Run a saved workflow")
168
+ daily.add_row("redo run <name> --dry", "Preview commands without executing")
169
+ daily.add_row("redo list", "See every saved workflow")
170
+ daily.add_row("redo show <name>", "Inspect commands and run count")
171
+
172
+ utilities = Table(
173
+ title="Utilities",
174
+ box=box.ROUNDED,
175
+ border_style=TABLE_BORDER,
176
+ header_style=BRAND,
177
+ )
178
+ utilities.add_column("Command", no_wrap=True)
179
+ utilities.add_column("Purpose")
180
+ utilities.add_row("redo guide", "Open the quick-start guide")
181
+ utilities.add_row("redo search <query>", "Find workflows by name, description, or command")
182
+ utilities.add_row("redo copy <source> <target>", "Duplicate a workflow")
183
+ utilities.add_row("redo rename <old> <new>", "Rename a workflow")
184
+ utilities.add_row("redo delete <name>", "Delete one workflow")
185
+ utilities.add_row("redo clearhistory", "Clear all saved workflows")
186
+
187
+ maintenance = Table(
188
+ title="Storage and maintenance",
189
+ box=box.ROUNDED,
190
+ border_style=TABLE_BORDER,
191
+ header_style=BRAND,
192
+ )
193
+ maintenance.add_column("Command", no_wrap=True)
194
+ maintenance.add_column("Purpose")
195
+ maintenance.add_row("redo path", "Show workflow storage location")
196
+ maintenance.add_row("redo doctor", "Check storage and risky saved commands")
197
+ maintenance.add_row("redo autofix", "Repair common storage problems")
198
+ maintenance.add_row("redo export <file>", "Back up workflows")
199
+ maintenance.add_row("redo import <file>", "Import workflows")
200
+ maintenance.add_row("redo --info", f"Show version {version} and credits")
201
+
202
+ console.print(
203
+ Panel(
204
+ Text("Redo command center", style=BRAND),
205
+ border_style=PANEL_BORDER,
206
+ box=box.ROUNDED,
207
+ )
208
+ )
209
+ console.print(daily)
210
+ console.print(utilities)
211
+ console.print(maintenance)
212
+ console.print(Text("Tip: placeholders look like {message} and are filled when you run a workflow.", style=MUTED))
213
+
214
+
215
+ def show_guide():
216
+ intro = Text.assemble(
217
+ ("Redo guide\n", BRAND),
218
+ ("Save repeated terminal workflows once. Run them again with one command.", MUTED),
219
+ )
220
+
221
+ basics = Table.grid(padding=(0, 2))
222
+ basics.add_column(style=MUTED, no_wrap=True)
223
+ basics.add_column()
224
+ basics.add_row("Create", "redo new ship")
225
+ basics.add_row("Run", "redo run ship")
226
+ basics.add_row("Preview", "redo run ship --dry")
227
+ basics.add_row("Inspect", "redo list | redo show ship")
228
+
229
+ example = Syntax(
230
+ 'Description: Commit and push code\n'
231
+ 'Command: git add .\n'
232
+ 'Command: git commit -m "{message}"\n'
233
+ 'Command: git push\n'
234
+ 'Command: :done',
235
+ "text",
236
+ theme="ansi_dark",
237
+ word_wrap=True,
238
+ )
239
+
240
+ placeholders = Table(
241
+ title="Placeholders",
242
+ box=box.ROUNDED,
243
+ border_style=TABLE_BORDER,
244
+ header_style=BRAND,
245
+ )
246
+ placeholders.add_column("Pattern", no_wrap=True)
247
+ placeholders.add_column("What happens")
248
+ placeholders.add_row("{message}", "Redo asks once, then inserts the value everywhere.")
249
+ placeholders.add_row("{project_name}", "Names must use letters, numbers, and underscores.")
250
+
251
+ warnings = Table(
252
+ title="Warnings",
253
+ box=box.ROUNDED,
254
+ border_style=TABLE_BORDER,
255
+ header_style=BRAND,
256
+ )
257
+ warnings.add_column("Tip", no_wrap=True)
258
+ warnings.add_column("Why it matters")
259
+ warnings.add_row("One command per prompt", "Do not separate commands with commas.")
260
+ warnings.add_row('Use git commit -m "{message}"', "Git needs -m for commit messages.")
261
+ warnings.add_row("Use --dry first", "Preview before running risky workflows.")
262
+
263
+ console.print(Panel(intro, border_style=PANEL_BORDER, box=box.ROUNDED))
264
+ console.print(Panel(basics, title="Core commands", border_style=PANEL_BORDER, box=box.ROUNDED))
265
+ console.print(Panel(example, title="Example workflow", border_style=PANEL_BORDER, box=box.ROUNDED))
266
+ console.print(placeholders)
267
+ console.print(warnings)
268
+
269
+
270
+ def show_workflows_table(workflows):
271
+ if not workflows:
272
+ console.print(
273
+ Panel(
274
+ Text("No workflows saved yet.", style=MUTED),
275
+ title="Redo workflows",
276
+ border_style=PANEL_BORDER,
277
+ box=box.ROUNDED,
278
+ )
279
+ )
280
+ return
281
+
282
+ table = Table(
283
+ title="Redo workflows",
284
+ box=box.ROUNDED,
285
+ border_style=TABLE_BORDER,
286
+ header_style=BRAND,
287
+ show_lines=False,
288
+ )
289
+ table.add_column("Name", style="bold", no_wrap=True)
290
+ table.add_column("Description", overflow="fold")
291
+ table.add_column("Commands", justify="right", no_wrap=True)
292
+ table.add_column("Runs", justify="right", style=NUMBER, no_wrap=True)
293
+
294
+ for name, workflow in sorted(workflows.items()):
295
+ command_count = len(workflow.get("commands", []))
296
+ table.add_row(
297
+ name,
298
+ workflow.get("description", "") or "-",
299
+ _plural(command_count, "command"),
300
+ str(workflow.get("runs", 0)),
301
+ )
302
+
303
+ console.print(table)
304
+
305
+
306
+ def show_workflow_details(name, workflow):
307
+ commands = workflow.get("commands", [])
308
+ metadata = _metadata_table(
309
+ [
310
+ ("Description", workflow.get("description", "") or "-"),
311
+ ("Commands", _plural(len(commands), "command")),
312
+ ("Runs", workflow.get("runs", 0)),
313
+ ]
314
+ )
315
+
316
+ console.print(
317
+ Panel(
318
+ metadata,
319
+ title=f"Workflow: {name}",
320
+ border_style=PANEL_BORDER,
321
+ box=box.ROUNDED,
322
+ expand=False,
323
+ )
324
+ )
325
+ show_commands(commands)
326
+
327
+
328
+ def show_commands(commands):
329
+ if not commands:
330
+ console.print(
331
+ Panel(
332
+ Text("No commands in this workflow.", style=MUTED),
333
+ title="Commands",
334
+ border_style=PANEL_BORDER,
335
+ box=box.ROUNDED,
336
+ )
337
+ )
338
+ return
339
+
340
+ table = Table(
341
+ title="Commands",
342
+ box=box.SIMPLE_HEAVY,
343
+ header_style=BRAND,
344
+ show_edge=False,
345
+ )
346
+ table.add_column("#", justify="right", style=MUTED, no_wrap=True)
347
+ table.add_column("Command")
348
+
349
+ for index, command in enumerate(commands, start=1):
350
+ syntax = Syntax(command, "bash", theme="ansi_dark", word_wrap=True)
351
+ table.add_row(str(index), syntax)
352
+
353
+ console.print(table)
354
+
355
+
356
+ def show_stats(workflows):
357
+ total_workflows = len(workflows)
358
+ total_runs = sum(int(workflow.get("runs", 0)) for workflow in workflows.values())
359
+ total_commands = sum(len(workflow.get("commands", [])) for workflow in workflows.values())
360
+ total_commands_run = sum(
361
+ len(workflow.get("commands", [])) * int(workflow.get("runs", 0))
362
+ for workflow in workflows.values()
363
+ )
364
+ estimated_seconds_saved = total_commands_run * 5
365
+ most_used = "-"
366
+
367
+ if workflows:
368
+ most_used_name, most_used_workflow = max(
369
+ workflows.items(),
370
+ key=lambda item: int(item[1].get("runs", 0)),
371
+ )
372
+ most_used = f"{most_used_name} ({_plural(int(most_used_workflow.get('runs', 0)), 'run')})"
373
+
374
+ summary = _metadata_table(
375
+ [
376
+ ("Total workflows", total_workflows),
377
+ ("Saved commands", total_commands),
378
+ ("Total runs", total_runs),
379
+ ("Most used workflow", most_used),
380
+ ("Estimated time saved", _format_seconds(estimated_seconds_saved)),
381
+ ]
382
+ )
383
+ console.print(
384
+ Panel(
385
+ summary,
386
+ title="Redo stats",
387
+ border_style=PANEL_BORDER,
388
+ box=box.ROUNDED,
389
+ )
390
+ )
391
+
392
+
393
+ def show_doctor_report(report):
394
+ has_warnings = report["dangerous_count"] > 0 or not report["exists"]
395
+ health_text = "Needs attention" if has_warnings else "Ready"
396
+ health_style = WARNING if has_warnings else SUCCESS
397
+
398
+ headline = Text.assemble(
399
+ ("Status: ", MUTED),
400
+ (health_text, health_style),
401
+ )
402
+ checks = _metadata_table(
403
+ [
404
+ ("Workflow file", report["path"]),
405
+ ("File exists", "yes" if report["exists"] else "no"),
406
+ ("Total workflows", report["total_workflows"]),
407
+ ("Total commands", report["total_commands"]),
408
+ ("Placeholder fields", report["placeholder_count"]),
409
+ ("Dangerous commands", report["dangerous_count"]),
410
+ ]
411
+ )
412
+
413
+ console.print(
414
+ Panel(
415
+ Group(headline, "", checks),
416
+ title="Health check",
417
+ border_style=health_style,
418
+ box=box.ROUNDED,
419
+ )
420
+ )
421
+
422
+ if report["dangerous_commands"]:
423
+ table = Table(
424
+ title="Dangerous commands",
425
+ box=box.ROUNDED,
426
+ border_style=WARNING,
427
+ header_style=WARNING,
428
+ )
429
+ table.add_column("Workflow", style="bold", no_wrap=True)
430
+ table.add_column("Command")
431
+ for name, command in report["dangerous_commands"]:
432
+ table.add_row(name, command)
433
+ console.print(table)
@@ -0,0 +1,231 @@
1
+ Metadata-Version: 2.4
2
+ Name: redo-cli
3
+ Version: 0.1.0
4
+ Summary: Bookmarks for terminal workflows.
5
+ Project-URL: Homepage, https://github.com/VibeSlayer-code/Redo
6
+ Project-URL: Repository, https://github.com/VibeSlayer-code/Redo
7
+ Project-URL: Issues, https://github.com/VibeSlayer-code/Redo/issues
8
+ Keywords: cli,terminal,workflow,automation,developer-tools
9
+ Classifier: Development Status :: 4 - Beta
10
+ Classifier: Environment :: Console
11
+ Classifier: Intended Audience :: Developers
12
+ Classifier: Operating System :: OS Independent
13
+ Classifier: Programming Language :: Python :: 3
14
+ Classifier: Programming Language :: Python :: 3.10
15
+ Classifier: Programming Language :: Python :: 3.11
16
+ Classifier: Programming Language :: Python :: 3.12
17
+ Classifier: Programming Language :: Python :: 3.13
18
+ Classifier: Topic :: Software Development
19
+ Classifier: Topic :: System :: Shells
20
+ Classifier: Topic :: Utilities
21
+ Requires-Python: >=3.10
22
+ Description-Content-Type: text/markdown
23
+ Requires-Dist: typer>=0.12
24
+ Requires-Dist: rich>=13
25
+ Provides-Extra: dev
26
+ Requires-Dist: build; extra == "dev"
27
+ Requires-Dist: pytest; extra == "dev"
28
+ Requires-Dist: twine; extra == "dev"
29
+
30
+ # Redo
31
+
32
+ Redo is a CLI tool that saves repeated terminal workflows and runs them again with one command. It is built for developers who are tired of retyping the same setup, build, deploy, and cleanup commands.
33
+
34
+ Think of it as bookmarks for terminal workflows.
35
+
36
+ ## Why Redo Exists
37
+
38
+ Developers repeat the same command chains constantly: starting projects, running test suites, cleaning folders, pushing code, building apps, and following long README setup steps.
39
+
40
+ Redo lets you define those workflows once, then replay them whenever you need them. It supports smart placeholders, previews, safety checks for dangerous commands, and simple usage stats.
41
+
42
+ ## Installation
43
+
44
+ Install from PyPI:
45
+
46
+ ```bash
47
+ pip install redo-cli
48
+ ```
49
+
50
+ For local development, clone the project, create a virtual environment, and install dependencies:
51
+
52
+ ```bash
53
+ python -m venv .venv
54
+ .venv\Scripts\activate
55
+ pip install -r requirements.txt
56
+ pip install -e .
57
+ ```
58
+
59
+ You can run Redo locally with either form:
60
+
61
+ ```bash
62
+ python main.py --help
63
+ redo --help
64
+ redo --info
65
+ ```
66
+
67
+ Running `redo` with no command shows the Redo ASCII banner. Running `redo --info` shows the banner, version, storage path, and credit.
68
+
69
+ Redo stores its workflow data in:
70
+
71
+ ```txt
72
+ %APPDATA%/Redo/workflows.json on Windows, or ~/.redo/workflows.json when APPDATA is unavailable
73
+ ```
74
+
75
+ Set `REDO_DATA_DIR` to override the storage directory. Run `redo path` to print the exact file Redo is using.
76
+
77
+ Run `redo init` to create the folder and file explicitly, or let Redo create them the first time it needs storage.
78
+
79
+ The first time you run `redo new`, Redo offers to show a quick guide. You can open that guide anytime with:
80
+
81
+ ```bash
82
+ redo guide
83
+ ```
84
+
85
+ ## Usage
86
+
87
+ Create a workflow:
88
+
89
+ ```bash
90
+ redo new ship
91
+ ```
92
+
93
+ Enter commands one by one:
94
+
95
+ ```txt
96
+ Description: Commit and push code
97
+ Command: git add .
98
+ Command: git commit -m "{message}"
99
+ Command: git push
100
+ Command: :done
101
+ ```
102
+
103
+ Run it later:
104
+
105
+ ```bash
106
+ redo run ship
107
+ ```
108
+
109
+ Redo shows a live status table while commands run. Successful command output stays quiet by default; if a command fails, Redo stops the workflow and shows a focused error panel with the captured output.
110
+
111
+ Preview it without executing commands:
112
+
113
+ ```bash
114
+ redo run ship --dry
115
+ ```
116
+
117
+ ## Commands
118
+
119
+ ```bash
120
+ redo init
121
+ redo new <name>
122
+ redo list
123
+ redo show <name>
124
+ redo run <name>
125
+ redo run <name> --dry
126
+ redo delete <name>
127
+ redo clearhistory
128
+ redo stats
129
+ ```
130
+
131
+ Developer QoL commands:
132
+
133
+ ```bash
134
+ redo search <query>
135
+ redo copy <source> <target>
136
+ redo rename <old-name> <new-name>
137
+ redo path
138
+ redo export workflows-backup.json
139
+ redo import workflows-backup.json
140
+ redo import workflows-backup.json --replace
141
+ redo doctor
142
+ redo autofix
143
+ redo guide
144
+ ```
145
+
146
+ `redo doctor` checks the workflow file, counts saved commands/placeholders, and flags risky commands before they surprise you later.
147
+
148
+ `redo autofix` repairs common storage problems: missing files, blank files, malformed JSON, and workflow entries with missing fields. If JSON is malformed, Redo saves a non-overwriting `workflows.broken.json` backup next to the main file before resetting it.
149
+
150
+ `redo clearhistory` clears every saved workflow from the file shown by `redo path`. Use `redo clearhistory --yes` to skip the confirmation prompt.
151
+
152
+ ## Placeholders
153
+
154
+ Use placeholders when part of a command changes each run:
155
+
156
+ ```bash
157
+ git commit -m "{message}"
158
+ npm create vite@latest {project_name}
159
+ cd {project_name}
160
+ ```
161
+
162
+ Redo asks once for each unique placeholder, then replaces every occurrence across the workflow.
163
+
164
+ Valid placeholder names use letters, numbers, and underscores, and cannot start with a number:
165
+
166
+ ```txt
167
+ {message}
168
+ {project_name}
169
+ {ticket_123}
170
+ ```
171
+
172
+ Placeholder values are quoted before execution so prompt input is treated as one literal value instead of shell syntax. This prevents command separators, variable expansion, and globs from silently changing the command shape.
173
+
174
+ Workflow names cannot be blank or reuse Redo command names such as `run`, `new`, `delete`, or `stats`.
175
+
176
+ ## Demo Workflow
177
+
178
+ ```bash
179
+ redo new ship
180
+ redo list
181
+ redo show ship
182
+ redo run ship --dry
183
+ redo run ship
184
+ redo stats
185
+ ```
186
+
187
+ Example workflow data:
188
+
189
+ ```json
190
+ {
191
+ "ship": {
192
+ "description": "Commit and push code",
193
+ "commands": [
194
+ "git add .",
195
+ "git commit -m \"{message}\"",
196
+ "git push"
197
+ ],
198
+ "runs": 0
199
+ }
200
+ }
201
+ ```
202
+
203
+ ## Safety
204
+
205
+ Redo detects risky commands before running them and asks for confirmation. Examples include:
206
+
207
+ ```bash
208
+ rm -rf
209
+ del /s
210
+ format
211
+ sudo
212
+ git reset --hard
213
+ ```
214
+
215
+ ## Git Push Tip
216
+
217
+ If Git says the current branch has no upstream branch, run the command Git suggests once:
218
+
219
+ ```bash
220
+ git push --set-upstream origin master
221
+ ```
222
+
223
+ After that, a workflow containing `git push` can push normally.
224
+
225
+ ## Roadmap
226
+
227
+ - Project-local and global workflow stores
228
+ - Tags and search
229
+ - Shell completion
230
+ - Workflow sharing through repository templates
231
+ - More detailed time-saved analytics
@@ -0,0 +1,11 @@
1
+ main.py,sha256=NVRu7YDsoqbRUGfNomRBSLdnjIrz7LH2nW4dp26iLss,9266
2
+ modules/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
3
+ modules/placeholders.py,sha256=m0Yvo89cb5IKIxT1O1qPnAdyyo9ynuB5fsUKFed2nkQ,3606
4
+ modules/runner.py,sha256=wihfKp4VMvJbn4t_B0J3bUvVO6Xu8gm3U_ZTiHP6Gag,7967
5
+ modules/storage.py,sha256=bg7fGNVsgIehOXo1i-bEl5ZGwkpG5AXZIbumnl5m0Yk,15132
6
+ modules/ui.py,sha256=tQi0JKDz8VD8BCpXw69zhtlW28I7LrVTSjFF9TwK9VQ,13221
7
+ redo_cli-0.1.0.dist-info/METADATA,sha256=EBk_oFqtG6HbtRsMs-kASsJkowbCG_SSM0P908lpvAQ,6186
8
+ redo_cli-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
9
+ redo_cli-0.1.0.dist-info/entry_points.txt,sha256=g5fzs2F79fisWEEssiMcn6s-exrkvPme9CiYBPu7rqU,34
10
+ redo_cli-0.1.0.dist-info/top_level.txt,sha256=AEStq8Ul9X2lAyef8AHIv8n5McZyoG48rZkemhotqrg,13
11
+ redo_cli-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ redo = main:app
@@ -0,0 +1,2 @@
1
+ main
2
+ modules