taskai-cli 1.0.0__tar.gz → 1.0.2__tar.gz

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 (23) hide show
  1. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/.gitignore +4 -1
  2. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/PKG-INFO +1 -1
  3. taskai_cli-1.0.2/migrations/_001_removing_lists.py +50 -0
  4. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/pyproject.toml +1 -1
  5. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/taskai/cli.py +78 -12
  6. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/taskai/services/user_setup.py +4 -4
  7. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/test/test_cli.py +20 -2
  8. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/.github/workflows/publish-to-pypi.yml +0 -0
  9. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/DEVLOG.md +0 -0
  10. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/README.md +0 -0
  11. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/docs/index.md +0 -0
  12. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/migrations/convert_title_to_name.py +0 -0
  13. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/mkdocs.yml +0 -0
  14. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/taskai/config.py +0 -0
  15. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/taskai/help_menu.py +0 -0
  16. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/taskai/json_dir_database.py +0 -0
  17. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/taskai/models.py +0 -0
  18. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/taskai/services/ai.py +0 -0
  19. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/taskai/services/repair_database.py +0 -0
  20. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/taskai/views.py +0 -0
  21. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/test/test_execution.py +0 -0
  22. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/test/test_json_dir_database.py +0 -0
  23. {taskai_cli-1.0.0 → taskai_cli-1.0.2}/test/test_view.py +0 -0
@@ -12,4 +12,7 @@ PROMPT.md
12
12
  _tmp_database_dir/tmp/
13
13
  tmp*/
14
14
  .taskai
15
- *venv
15
+ *venv
16
+ _tmp_database_dir/
17
+ .dev
18
+ .prod
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: taskai-cli
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Author-email: Alex Paskal <alexcpaskal@gmail.com>
5
5
  Requires-Python: >=3.12
6
6
  Requires-Dist: google-genai>=2.8
@@ -0,0 +1,50 @@
1
+ import argparse
2
+ import orjson as json
3
+ from taskai.models import TodoItem
4
+
5
+ def migrate(data):
6
+ if "TodoList" in data:
7
+ for k, record in data["TodoList"].items():
8
+
9
+ record["child_ids"] = record.pop("item_ids")
10
+ TodoItem(**record)
11
+ print(f"Converting {k} to Item")
12
+ data["TodoItem"][k] = record
13
+ data.pop("TodoList")
14
+
15
+ for k, record in data["TodoItem"].items():
16
+
17
+ if "title" in record:
18
+ print(f"converting title->name for {k}")
19
+ record["title"] = record["name"]
20
+ record.pop("title")
21
+
22
+ if "parent" in record:
23
+ print(f"converting parent->parent_id for {k}")
24
+ record["parent_id"] = record["parent"]
25
+ record.pop("parent")
26
+
27
+ if "child_ids" not in record:
28
+ print(f"Adding Child IDs {k}")
29
+ record["child_ids"] = []
30
+
31
+ try:
32
+ data["TodoItem"][k] = TodoItem(**record).model_dump()
33
+ except:
34
+ print("validation for {} failed".format(k))
35
+ import sys
36
+ sys.exit()
37
+
38
+
39
+ arg_parser = argparse.ArgumentParser()
40
+ arg_parser.add_argument("path", help="path to taskai root")
41
+ args = arg_parser.parse_args()
42
+
43
+ with open(args.path, "rb") as f:
44
+ data = json.loads(f.read())
45
+
46
+ migrate(data)
47
+
48
+
49
+ with open(args.path, "wb") as f:
50
+ f.write(json.dumps(data))
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "taskai-cli"
3
- version = "1.0.0"
3
+ version = "1.0.2"
4
4
  description = ""
5
5
  authors = [{name = "Alex Paskal", email = "alexcpaskal@gmail.com"}]
6
6
  readme = "README.md"
@@ -208,13 +208,45 @@ class Controller:
208
208
  db.commit()
209
209
 
210
210
  # utilities
211
+ def _parse_arg_string(arg_string: str) -> list[str]:
212
+ """parses a string properly before dispatching it to the argument parser"""
213
+
214
+ # scan for quote characters
215
+
216
+ currently_enclosed = False
217
+ current_quote_char = None
218
+ quote_chars = ('"',"'")
219
+ buffer = ""
220
+ arg_parts = []
221
+ for c in arg_string:
222
+ if c == " ":
223
+ if currently_enclosed:
224
+ buffer += c
225
+ else:
226
+ arg_parts.append(buffer)
227
+ buffer = ""
228
+ elif c == current_quote_char:
229
+ currently_enclosed = False
230
+ current_quote_char = None
231
+
232
+ elif c in quote_chars and not currently_enclosed:
233
+ currently_enclosed = True
234
+ current_quote_char = c
235
+ else:
236
+ buffer += c
237
+ if buffer:
238
+ arg_parts.append(buffer)
239
+ return arg_parts
240
+
211
241
  def _parse_remaining(remaining_args: list[str]) -> tuple[list, dict]:
212
242
 
213
- for i, _arg in enumerate(remaining_args):
214
- remaining_args[i] = _arg.replace(" ", "+-*/")
215
- remaining_args = " ".join(remaining_args).replace("="," ").split(" ")
216
- for i, _arg in enumerate(remaining_args):
217
- remaining_args[i] = _arg.replace("+-*/", " ")
243
+ # parse flags
244
+
245
+ #for i, _arg in enumerate(remaining_args):
246
+ # remaining_args[i] = _arg.replace(" ", "+-*/")
247
+ #remaining_args = " ".join(remaining_args).replace("="," ").split(" ")
248
+ #for i, _arg in enumerate(remaining_args):
249
+ # remaining_args[i] = _arg.replace("+-*/", " ")
218
250
 
219
251
  # outputs
220
252
  args = []
@@ -223,11 +255,16 @@ def _parse_remaining(remaining_args: list[str]) -> tuple[list, dict]:
223
255
  while remaining_args:
224
256
  next_arg = remaining_args.pop(0)
225
257
  if next_arg.startswith("--"):
226
- assert remaining_args, "kwarg specified with no value provided"
227
- kwargs[next_arg[2:]] = remaining_args.pop(0)
258
+ if "=" in next_arg:
259
+ key, value = next_arg.split("=")
260
+ kwargs[key] = value
261
+ else:
262
+ assert remaining_args, "kwarg specified with no value provided"
263
+ kwargs[next_arg[2:]] = remaining_args.pop(0)
228
264
  else:
229
265
  args.append(next_arg)
230
-
266
+
267
+
231
268
  return args, kwargs
232
269
 
233
270
  def _is_int(val: any) -> bool:
@@ -349,22 +386,51 @@ def interactive_program():
349
386
  # builtins.print = console.print
350
387
 
351
388
  response = ""
389
+ last_show_command = [("show", "all"), {}]
390
+ args = None
391
+ kwargs = None
392
+
352
393
  _clear_screen()
353
394
  while True:
354
395
 
355
396
  try:
397
+
398
+ # render last show command
399
+ if last_show_command is not None:
400
+ try:
401
+ execute_commands(*last_show_command[0], **last_show_command[1])
402
+ except Exception as e:
403
+ print(e)
404
+
405
+ Console().rule()
406
+
407
+ # prompt user input
356
408
  response = Prompt.ask("Type your commands:", default=response)
357
- _clear_screen()
358
- args, kwargs = _parse_remaining(response.split(" "))
409
+
410
+ # parse commands
411
+ args_remaining = _parse_arg_string(response)
412
+ args, kwargs = _parse_remaining(args_remaining)
413
+ print("arg_string:", args_remaining)
414
+ print("args:", args)
415
+ print("kwargs:", kwargs)
359
416
  if args[0] == "task":
360
417
  args = args[1:]
361
- return_code = execute_commands(*args, **kwargs)
418
+
419
+ # defer show
420
+ if args[0] == "show":
421
+ last_show_command = (args, kwargs)
422
+ return_code = 1
423
+ else:
424
+ return_code = execute_commands(*args, **kwargs)
425
+
426
+ _clear_screen()
362
427
  if return_code == 0:
363
428
  break
364
- Console().rule()
365
429
 
366
430
  except KeyboardInterrupt:
367
431
  break
432
+ except Exception as e:
433
+ print(e)
368
434
 
369
435
  _clear_screen()
370
436
  sys.exit(1)
@@ -14,20 +14,20 @@ def user_setup_service(
14
14
  db: JsonDirectoryDatabase
15
15
  ):
16
16
 
17
- config = db.config
17
+ config = db.get_config()
18
18
  # setup gemini model
19
19
  print("Beginning setup")
20
20
  if "GEMINI_API_KEY" not in config:
21
21
  api_key = _get_gemini_api_key()
22
22
  if api_key:
23
- config["GEMINI_API_KEY"] = api_key
23
+ db.update_config(GEMINI_API_KEY=api_key)
24
24
  else:
25
25
  print("gemini key already specified")
26
26
 
27
27
  if "GEMINI_MODEL" not in config:
28
28
  model = _select_gemini_model()
29
29
  if model:
30
- config["GEMINI_MODEL"] = model
30
+ db.update_config(GEMINI_MODEL=model)
31
31
  else:
32
32
  print("gemini model already specified")
33
33
  print("Setup complete! Use 'task config set|get|list' to interact with your configuration options")
@@ -60,4 +60,4 @@ if __name__ == "__main__":
60
60
  ".taskai/task_db", user=os.getenv("USER")
61
61
  )
62
62
  db.connect()
63
- user_setup_service(db)
63
+ user_setup_service(db)
@@ -1,4 +1,5 @@
1
1
  from taskai.json_dir_database import JsonDirectoryDatabase
2
+ from taskai.cli import _parse_arg_string
2
3
  import os
3
4
  import shutil
4
5
 
@@ -53,6 +54,23 @@ def test_run_commands():
53
54
  finally:
54
55
  _cleanup_db()
55
56
 
57
+ def test_args_parser():
58
+
59
+ def _assert_list_equal(l1, l2):
60
+ assert len(l1) == len(l2), f"{l1} length not equal to {l2} length"
61
+ assert all([thing1==thing2 for thing1, thing2 in zip(l1, l2)]), f"{l1} not equal to {l2}"
62
+
63
+ dataset = [
64
+ ("show thing 1", ["show", "thing", "1"]),
65
+ ("show 'this is a string' 2", ["show", "this is a string", "2"]),
66
+ ("show https://url.com/thing?arg=1 hello there", ["show", "https://url.com/thing?arg=1", "hello", "there"])
67
+ ]
68
+
69
+ for input, label in dataset:
70
+ output = _parse_arg_string(input)
71
+ _assert_list_equal(label, output)
72
+
73
+
56
74
 
57
75
  def _cleanup_db():
58
76
  os.chdir(CWD)
@@ -60,5 +78,5 @@ def _cleanup_db():
60
78
  shutil.rmtree(TESTING_DIR)
61
79
 
62
80
  if __name__ == "__main__":
63
-
64
- test_run_commands()
81
+ test_args_parser()
82
+ # test_run_commands()
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes