ai-memory-cli 0.1.6__tar.gz → 0.1.8__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ai-memory-cli
3
- Version: 0.1.6
3
+ Version: 0.1.8
4
4
  Summary: Python CLI for AI Memory terminal capture and offline sync.
5
5
  Author: AI Memory
6
6
  License-Expression: MIT
@@ -57,6 +57,9 @@ The `auth` command verifies the website-issued token with FastAPI before it is s
57
57
  the CLI stores a SHA-512 user hash for this computer, binds the token to that hash on the server, and starts the
58
58
  background sync agent once on Windows.
59
59
 
60
+ If you run `watch` before auth, it will prompt for the website CLI token and FastAPI URL, then continue into
61
+ terminal capture after verification.
62
+
60
63
  `watch` is a shortcut for `python -m ai_memory_cli watch`. If Windows Device Guard blocks the generated launcher, keep using `python -m ai_memory_cli watch`.
61
64
  On Windows the shortcut is installed as `watch.cmd`; the Python Scripts folder must be on `PATH` for bare `watch` to resolve.
62
65
 
@@ -65,6 +68,7 @@ Use `python -m ai_memory_cli run -- COMMAND` when you only want to record one co
65
68
  On Windows, `python -m ai_memory_cli ...` is the safest form because it avoids PATH issues and Device Guard policies that can block pip's generated `ai-memory.exe` launcher. Also avoid angle bracket placeholders in CMD because they are treated as file redirection.
66
69
 
67
70
  Inside `watch`, type the real command you want to capture, for example `python --version`. Do not type `python -m ai_memory_cli run -- ...` inside `watch`, or you will capture the nested CLI command too.
71
+ Use `cls` on Windows or `clear` on Unix shells to clear the watch screen; those control commands are not stored or synced.
68
72
 
69
73
  ## Background agent
70
74
 
@@ -35,6 +35,9 @@ The `auth` command verifies the website-issued token with FastAPI before it is s
35
35
  the CLI stores a SHA-512 user hash for this computer, binds the token to that hash on the server, and starts the
36
36
  background sync agent once on Windows.
37
37
 
38
+ If you run `watch` before auth, it will prompt for the website CLI token and FastAPI URL, then continue into
39
+ terminal capture after verification.
40
+
38
41
  `watch` is a shortcut for `python -m ai_memory_cli watch`. If Windows Device Guard blocks the generated launcher, keep using `python -m ai_memory_cli watch`.
39
42
  On Windows the shortcut is installed as `watch.cmd`; the Python Scripts folder must be on `PATH` for bare `watch` to resolve.
40
43
 
@@ -43,6 +46,7 @@ Use `python -m ai_memory_cli run -- COMMAND` when you only want to record one co
43
46
  On Windows, `python -m ai_memory_cli ...` is the safest form because it avoids PATH issues and Device Guard policies that can block pip's generated `ai-memory.exe` launcher. Also avoid angle bracket placeholders in CMD because they are treated as file redirection.
44
47
 
45
48
  Inside `watch`, type the real command you want to capture, for example `python --version`. Do not type `python -m ai_memory_cli run -- ...` inside `watch`, or you will capture the nested CLI command too.
49
+ Use `cls` on Windows or `clear` on Unix shells to clear the watch screen; those control commands are not stored or synced.
46
50
 
47
51
  ## Background agent
48
52
 
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ai-memory-cli"
7
- version = "0.1.6"
7
+ version = "0.1.8"
8
8
  description = "Python CLI for AI Memory terminal capture and offline sync."
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.10"
@@ -1,3 +1,3 @@
1
1
  """AI Memory terminal capture CLI."""
2
2
 
3
- __version__ = "0.1.6"
3
+ __version__ = "0.1.8"
@@ -42,6 +42,7 @@ SHELL_NOT_FOUND_PATTERNS = [
42
42
  "no such file or directory",
43
43
  "command not found",
44
44
  ]
45
+ CLEAR_COMMANDS = {"cls", "clear"}
45
46
 
46
47
 
47
48
  def utc_now() -> str:
@@ -174,6 +175,10 @@ def client_identity(home: Path, config: dict[str, Any]) -> dict[str, Any]:
174
175
  }
175
176
 
176
177
 
178
+ def has_verified_auth(config: dict[str, Any]) -> bool:
179
+ return bool(config.get("token") and config.get("auth_verified_at") and config.get("user_hash") and config.get("local_user_hash"))
180
+
181
+
177
182
  def agent_state_path(home: Path) -> Path:
178
183
  return home / "agent.json"
179
184
 
@@ -317,6 +322,18 @@ def clean_watch_command(command: str) -> str:
317
322
  return cleaned
318
323
 
319
324
 
325
+ def is_clear_command(command: str) -> bool:
326
+ return normalize_command(command).lower() in CLEAR_COMMANDS
327
+
328
+
329
+ def clear_console() -> None:
330
+ command = "cls" if os.name == "nt" else "clear"
331
+ try:
332
+ subprocess.call(command, shell=True)
333
+ except Exception:
334
+ print("\033[2J\033[H", end="")
335
+
336
+
320
337
  def normalize_output(stdout: str, stderr: str) -> str:
321
338
  combined = f"stdout:\n{stdout}\nstderr:\n{stderr}"
322
339
  lines = [line.rstrip() for line in combined.replace("\r\n", "\n").replace("\r", "\n").split("\n")]
@@ -425,9 +442,56 @@ def verify_cli_auth(home: Path, config: dict[str, Any], token: str) -> dict[str,
425
442
  return response
426
443
 
427
444
 
445
+ def finish_auth(home: Path, config: dict[str, Any], token: str, start_agent: bool = True) -> dict[str, Any]:
446
+ config["pending_token_hash"] = sha256_text(token)
447
+ response = verify_cli_auth(home, config, token)
448
+ config["authed_at"] = utc_now()
449
+ save_config(home, config)
450
+
451
+ print(f"Saved CLI auth in {config_path(home)}")
452
+ print(f"GitHub account: {response.get('github_user') or config.get('github_user') or '-'}")
453
+ print(f"Local user hash: {str(config.get('user_hash') or '')[:24]}...")
454
+ if response.get("account_storage_dir"):
455
+ print(f"Server account storage: {response['account_storage_dir']}")
456
+
457
+ if start_agent:
458
+ ensure_agent_started_once(home, config)
459
+
460
+ try:
461
+ synced = sync_events(home, config, quiet=True)
462
+ if synced:
463
+ print(f"Synced {synced} queued terminal event(s).")
464
+ except Exception as exc:
465
+ print(f"Auth saved. Sync will retry later: {exc}", file=sys.stderr)
466
+
467
+ return response
468
+
469
+
470
+ def prompt_for_auth(home: Path, config: dict[str, Any]) -> dict[str, Any]:
471
+ print("AI Memory needs website auth before watch can capture and sync.")
472
+ print("Generate a CLI token from the website Integrations page, then paste it here.")
473
+ token = getpass.getpass("Website CLI token: ").strip()
474
+ if not token:
475
+ raise SystemExit("No token entered. Generate a CLI token from the website and run watch again.")
476
+
477
+ current_api_url = api_url(config)
478
+ entered_api_url = input(f"FastAPI URL [{current_api_url}]: ").strip()
479
+ if entered_api_url:
480
+ config["api_url"] = entered_api_url.rstrip("/")
481
+
482
+ try:
483
+ return finish_auth(home, config, token, start_agent=True)
484
+ except urllib.error.HTTPError as exc:
485
+ save_config(home, config)
486
+ raise SystemExit(f"CLI auth failed: {describe_http_error(exc)}") from exc
487
+ except Exception as exc:
488
+ save_config(home, config)
489
+ raise SystemExit(f"CLI auth failed. Keep the local FastAPI server running and generate a fresh website token: {exc}") from exc
490
+
491
+
428
492
  def require_verified_auth(home: Path, config: dict[str, Any]) -> str:
429
493
  token = require_token(config)
430
- if config.get("auth_verified_at") and config.get("user_hash") and config.get("local_user_hash"):
494
+ if has_verified_auth(config):
431
495
  return token
432
496
 
433
497
  try:
@@ -545,7 +609,7 @@ def sync_events(home: Path, config: dict[str, Any], limit: int = 50, quiet: bool
545
609
  if not quiet:
546
610
  print("No CLI token saved. Events remain queued until python -m ai_memory_cli auth is configured.")
547
611
  return 0
548
- if not (config.get("auth_verified_at") and config.get("user_hash")):
612
+ if not has_verified_auth(config):
549
613
  try:
550
614
  verify_cli_auth(home, config, token)
551
615
  save_config(home, config)
@@ -581,6 +645,10 @@ def sync_events(home: Path, config: dict[str, Any], limit: int = 50, quiet: bool
581
645
 
582
646
 
583
647
  def capture_command(home: Path, config: dict[str, Any], command: str, include_excluded: bool, source: str) -> int:
648
+ if is_clear_command(command):
649
+ clear_console()
650
+ return 0
651
+
584
652
  require_verified_auth(home, config)
585
653
  workspace = Path(str(config.get("workspace_path") or ".")).expanduser()
586
654
  cwd = workspace if workspace.exists() else Path.cwd()
@@ -661,31 +729,14 @@ def command_auth(args: argparse.Namespace) -> int:
661
729
  config["api_url"] = args.api_url.rstrip("/")
662
730
 
663
731
  token = args.token.strip()
664
- config["pending_token_hash"] = sha256_text(token)
665
732
  try:
666
- response = verify_cli_auth(home, config, token)
733
+ finish_auth(home, config, token, start_agent=not args.no_agent)
667
734
  except urllib.error.HTTPError as exc:
668
735
  save_config(home, config)
669
736
  raise SystemExit(f"CLI auth failed: {describe_http_error(exc)}") from exc
670
737
  except Exception as exc:
671
738
  save_config(home, config)
672
739
  raise SystemExit(f"CLI auth failed. Keep the local FastAPI server running and generate a fresh website token: {exc}") from exc
673
-
674
- config["authed_at"] = utc_now()
675
- save_config(home, config)
676
- print(f"Saved CLI auth in {config_path(home)}")
677
- print(f"GitHub account: {response.get('github_user') or config.get('github_user') or '-'}")
678
- print(f"Local user hash: {str(config.get('user_hash') or '')[:24]}...")
679
- if response.get("account_storage_dir"):
680
- print(f"Server account storage: {response['account_storage_dir']}")
681
-
682
- if not args.no_agent:
683
- ensure_agent_started_once(home, config)
684
-
685
- try:
686
- sync_events(home, config, quiet=True)
687
- except Exception as exc:
688
- print(f"Auth saved. Sync will retry later: {exc}", file=sys.stderr)
689
740
  return 0
690
741
 
691
742
 
@@ -812,6 +863,10 @@ def command_run(args: argparse.Namespace) -> int:
812
863
  def command_watch(args: argparse.Namespace) -> int:
813
864
  home = cli_home()
814
865
  config = load_config(home)
866
+ if not has_verified_auth(config):
867
+ prompt_for_auth(home, config)
868
+ config = load_config(home)
869
+
815
870
  print("AI Memory watch mode. Type commands to run and capture. Type exit to stop.")
816
871
  while True:
817
872
  try:
@@ -830,6 +885,9 @@ def command_watch(args: argparse.Namespace) -> int:
830
885
  command = cleaned_command
831
886
  if command.lower() in {"exit", "quit"}:
832
887
  break
888
+ if is_clear_command(command):
889
+ clear_console()
890
+ continue
833
891
  capture_command(home, config, command, args.include_excluded, "watch")
834
892
  return 0
835
893
 
@@ -1120,7 +1178,7 @@ def command_status(_: argparse.Namespace) -> int:
1120
1178
  print(f"Project: {config.get('project') or '-'}")
1121
1179
  print(f"Repository: {config.get('repository') or '-'}")
1122
1180
  print(f"Workspace: {config.get('workspace_path') or '.'}")
1123
- print(f"Token: {'verified' if config.get('auth_verified_at') and config.get('user_hash') else 'saved' if config.get('token') else 'missing'}")
1181
+ print(f"Token: {'verified' if has_verified_auth(config) else 'saved' if config.get('token') else 'missing'}")
1124
1182
  print(f"GitHub account: {config.get('github_user') or '-'}")
1125
1183
  print(f"User hash: {str(config.get('user_hash') or '-')[:24]}{'...' if config.get('user_hash') else ''}")
1126
1184
  print(f"Events: {event_count} total, {outbox_count} queued, {sent_count} synced receipts")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ai-memory-cli
3
- Version: 0.1.6
3
+ Version: 0.1.8
4
4
  Summary: Python CLI for AI Memory terminal capture and offline sync.
5
5
  Author: AI Memory
6
6
  License-Expression: MIT
@@ -57,6 +57,9 @@ The `auth` command verifies the website-issued token with FastAPI before it is s
57
57
  the CLI stores a SHA-512 user hash for this computer, binds the token to that hash on the server, and starts the
58
58
  background sync agent once on Windows.
59
59
 
60
+ If you run `watch` before auth, it will prompt for the website CLI token and FastAPI URL, then continue into
61
+ terminal capture after verification.
62
+
60
63
  `watch` is a shortcut for `python -m ai_memory_cli watch`. If Windows Device Guard blocks the generated launcher, keep using `python -m ai_memory_cli watch`.
61
64
  On Windows the shortcut is installed as `watch.cmd`; the Python Scripts folder must be on `PATH` for bare `watch` to resolve.
62
65
 
@@ -65,6 +68,7 @@ Use `python -m ai_memory_cli run -- COMMAND` when you only want to record one co
65
68
  On Windows, `python -m ai_memory_cli ...` is the safest form because it avoids PATH issues and Device Guard policies that can block pip's generated `ai-memory.exe` launcher. Also avoid angle bracket placeholders in CMD because they are treated as file redirection.
66
69
 
67
70
  Inside `watch`, type the real command you want to capture, for example `python --version`. Do not type `python -m ai_memory_cli run -- ...` inside `watch`, or you will capture the nested CLI command too.
71
+ Use `cls` on Windows or `clear` on Unix shells to clear the watch screen; those control commands are not stored or synced.
68
72
 
69
73
  ## Background agent
70
74
 
File without changes
File without changes