ai-memory-cli 0.1.7__tar.gz → 0.1.9__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.
- {ai_memory_cli-0.1.7/src/ai_memory_cli.egg-info → ai_memory_cli-0.1.9}/PKG-INFO +3 -1
- {ai_memory_cli-0.1.7 → ai_memory_cli-0.1.9}/README.md +2 -0
- {ai_memory_cli-0.1.7 → ai_memory_cli-0.1.9}/pyproject.toml +1 -1
- {ai_memory_cli-0.1.7 → ai_memory_cli-0.1.9}/src/ai_memory_cli/__init__.py +1 -1
- {ai_memory_cli-0.1.7 → ai_memory_cli-0.1.9}/src/ai_memory_cli/cli.py +76 -11
- {ai_memory_cli-0.1.7 → ai_memory_cli-0.1.9/src/ai_memory_cli.egg-info}/PKG-INFO +3 -1
- {ai_memory_cli-0.1.7 → ai_memory_cli-0.1.9}/LICENSE +0 -0
- {ai_memory_cli-0.1.7 → ai_memory_cli-0.1.9}/bin/watch.cmd +0 -0
- {ai_memory_cli-0.1.7 → ai_memory_cli-0.1.9}/setup.cfg +0 -0
- {ai_memory_cli-0.1.7 → ai_memory_cli-0.1.9}/src/ai_memory_cli/__main__.py +0 -0
- {ai_memory_cli-0.1.7 → ai_memory_cli-0.1.9}/src/ai_memory_cli.egg-info/SOURCES.txt +0 -0
- {ai_memory_cli-0.1.7 → ai_memory_cli-0.1.9}/src/ai_memory_cli.egg-info/dependency_links.txt +0 -0
- {ai_memory_cli-0.1.7 → ai_memory_cli-0.1.9}/src/ai_memory_cli.egg-info/entry_points.txt +0 -0
- {ai_memory_cli-0.1.7 → ai_memory_cli-0.1.9}/src/ai_memory_cli.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ai-memory-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: Python CLI for AI Memory terminal capture and offline sync.
|
|
5
5
|
Author: AI Memory
|
|
6
6
|
License-Expression: MIT
|
|
@@ -68,6 +68,8 @@ Use `python -m ai_memory_cli run -- COMMAND` when you only want to record one co
|
|
|
68
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.
|
|
69
69
|
|
|
70
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.
|
|
72
|
+
On Windows, commands run through PowerShell, so type `ls` directly. Do not type `powershell` or `cmd` inside `watch`; nested shell launchers are ignored and not stored.
|
|
71
73
|
|
|
72
74
|
## Background agent
|
|
73
75
|
|
|
@@ -46,6 +46,8 @@ Use `python -m ai_memory_cli run -- COMMAND` when you only want to record one co
|
|
|
46
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.
|
|
47
47
|
|
|
48
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.
|
|
50
|
+
On Windows, commands run through PowerShell, so type `ls` directly. Do not type `powershell` or `cmd` inside `watch`; nested shell launchers are ignored and not stored.
|
|
49
51
|
|
|
50
52
|
## Background agent
|
|
51
53
|
|
|
@@ -42,6 +42,8 @@ SHELL_NOT_FOUND_PATTERNS = [
|
|
|
42
42
|
"no such file or directory",
|
|
43
43
|
"command not found",
|
|
44
44
|
]
|
|
45
|
+
CLEAR_COMMANDS = {"cls", "clear"}
|
|
46
|
+
INTERACTIVE_SHELLS = {"powershell", "pwsh", "cmd", "bash", "sh", "zsh", "fish"}
|
|
45
47
|
|
|
46
48
|
|
|
47
49
|
def utc_now() -> str:
|
|
@@ -321,6 +323,56 @@ def clean_watch_command(command: str) -> str:
|
|
|
321
323
|
return cleaned
|
|
322
324
|
|
|
323
325
|
|
|
326
|
+
def is_clear_command(command: str) -> bool:
|
|
327
|
+
return normalize_command(command).lower() in CLEAR_COMMANDS
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
def is_interactive_shell_command(command: str) -> bool:
|
|
331
|
+
tokens = normalize_command(command).lower().split()
|
|
332
|
+
if not tokens:
|
|
333
|
+
return False
|
|
334
|
+
launcher = tokens[0].removesuffix(".exe")
|
|
335
|
+
if launcher not in INTERACTIVE_SHELLS:
|
|
336
|
+
return False
|
|
337
|
+
if launcher in {"powershell", "pwsh"}:
|
|
338
|
+
return len(tokens) == 1 or "-noexit" in tokens
|
|
339
|
+
if launcher == "cmd":
|
|
340
|
+
return len(tokens) == 1 or "/k" in tokens
|
|
341
|
+
return len(tokens) == 1
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def command_invocation(command: str) -> tuple[str | list[str], bool]:
|
|
345
|
+
if os.name == "nt":
|
|
346
|
+
powershell = shutil.which("powershell") or shutil.which("pwsh")
|
|
347
|
+
if powershell:
|
|
348
|
+
return [powershell, "-NoLogo", "-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", command], False
|
|
349
|
+
return command, True
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def run_external_command(command: str, cwd: Path, capture: bool) -> subprocess.CompletedProcess[str] | int:
|
|
353
|
+
invocation, use_shell = command_invocation(command)
|
|
354
|
+
if capture:
|
|
355
|
+
return subprocess.run(
|
|
356
|
+
invocation,
|
|
357
|
+
shell=use_shell,
|
|
358
|
+
cwd=str(cwd),
|
|
359
|
+
stdin=subprocess.DEVNULL,
|
|
360
|
+
capture_output=True,
|
|
361
|
+
text=True,
|
|
362
|
+
encoding="utf-8",
|
|
363
|
+
errors="replace",
|
|
364
|
+
)
|
|
365
|
+
return subprocess.call(invocation, shell=use_shell, cwd=str(cwd), stdin=subprocess.DEVNULL)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
def clear_console() -> None:
|
|
369
|
+
command = "cls" if os.name == "nt" else "clear"
|
|
370
|
+
try:
|
|
371
|
+
subprocess.call(command, shell=True)
|
|
372
|
+
except Exception:
|
|
373
|
+
print("\033[2J\033[H", end="")
|
|
374
|
+
|
|
375
|
+
|
|
324
376
|
def normalize_output(stdout: str, stderr: str) -> str:
|
|
325
377
|
combined = f"stdout:\n{stdout}\nstderr:\n{stderr}"
|
|
326
378
|
lines = [line.rstrip() for line in combined.replace("\r\n", "\n").replace("\r", "\n").split("\n")]
|
|
@@ -328,10 +380,15 @@ def normalize_output(stdout: str, stderr: str) -> str:
|
|
|
328
380
|
|
|
329
381
|
|
|
330
382
|
def selected_shell() -> str:
|
|
383
|
+
if os.name == "nt":
|
|
384
|
+
if shutil.which("powershell"):
|
|
385
|
+
return "powershell"
|
|
386
|
+
if shutil.which("pwsh"):
|
|
387
|
+
return "pwsh"
|
|
331
388
|
shell = os.getenv("SHELL") or os.getenv("COMSPEC") or ""
|
|
332
389
|
if shell:
|
|
333
390
|
return Path(shell).name
|
|
334
|
-
return "
|
|
391
|
+
return "sh"
|
|
335
392
|
|
|
336
393
|
|
|
337
394
|
def command_line(parts: list[str]) -> str:
|
|
@@ -632,25 +689,27 @@ def sync_events(home: Path, config: dict[str, Any], limit: int = 50, quiet: bool
|
|
|
632
689
|
|
|
633
690
|
|
|
634
691
|
def capture_command(home: Path, config: dict[str, Any], command: str, include_excluded: bool, source: str) -> int:
|
|
692
|
+
if is_clear_command(command):
|
|
693
|
+
clear_console()
|
|
694
|
+
return 0
|
|
695
|
+
|
|
696
|
+
if is_interactive_shell_command(command):
|
|
697
|
+
print("ai-memory: watch already runs commands through a shell. Type the command directly, for example: ls")
|
|
698
|
+
return 0
|
|
699
|
+
|
|
635
700
|
require_verified_auth(home, config)
|
|
636
701
|
workspace = Path(str(config.get("workspace_path") or ".")).expanduser()
|
|
637
702
|
cwd = workspace if workspace.exists() else Path.cwd()
|
|
638
703
|
|
|
639
704
|
if is_excluded(command, config) and not include_excluded:
|
|
640
705
|
print(f"ai-memory: running without capture because this command is excluded: {command}", file=sys.stderr)
|
|
641
|
-
return
|
|
706
|
+
return int(run_external_command(command, cwd, capture=False))
|
|
642
707
|
|
|
643
708
|
started_at = utc_now()
|
|
644
709
|
started_monotonic = time.monotonic()
|
|
645
|
-
completed =
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
cwd=str(cwd),
|
|
649
|
-
capture_output=True,
|
|
650
|
-
text=True,
|
|
651
|
-
encoding="utf-8",
|
|
652
|
-
errors="replace",
|
|
653
|
-
)
|
|
710
|
+
completed = run_external_command(command, cwd, capture=True)
|
|
711
|
+
if isinstance(completed, int):
|
|
712
|
+
return completed
|
|
654
713
|
ended_at = utc_now()
|
|
655
714
|
duration_ms = int((time.monotonic() - started_monotonic) * 1000)
|
|
656
715
|
|
|
@@ -868,6 +927,12 @@ def command_watch(args: argparse.Namespace) -> int:
|
|
|
868
927
|
command = cleaned_command
|
|
869
928
|
if command.lower() in {"exit", "quit"}:
|
|
870
929
|
break
|
|
930
|
+
if is_clear_command(command):
|
|
931
|
+
clear_console()
|
|
932
|
+
continue
|
|
933
|
+
if is_interactive_shell_command(command):
|
|
934
|
+
print("ai-memory: do not start a nested shell here. Type commands directly, for example: ls")
|
|
935
|
+
continue
|
|
871
936
|
capture_command(home, config, command, args.include_excluded, "watch")
|
|
872
937
|
return 0
|
|
873
938
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ai-memory-cli
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.9
|
|
4
4
|
Summary: Python CLI for AI Memory terminal capture and offline sync.
|
|
5
5
|
Author: AI Memory
|
|
6
6
|
License-Expression: MIT
|
|
@@ -68,6 +68,8 @@ Use `python -m ai_memory_cli run -- COMMAND` when you only want to record one co
|
|
|
68
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.
|
|
69
69
|
|
|
70
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.
|
|
72
|
+
On Windows, commands run through PowerShell, so type `ls` directly. Do not type `powershell` or `cmd` inside `watch`; nested shell launchers are ignored and not stored.
|
|
71
73
|
|
|
72
74
|
## Background agent
|
|
73
75
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|