ai-memory-cli 0.1.8__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.8/src/ai_memory_cli.egg-info → ai_memory_cli-0.1.9}/PKG-INFO +2 -1
- {ai_memory_cli-0.1.8 → ai_memory_cli-0.1.9}/README.md +1 -0
- {ai_memory_cli-0.1.8 → ai_memory_cli-0.1.9}/pyproject.toml +1 -1
- {ai_memory_cli-0.1.8 → ai_memory_cli-0.1.9}/src/ai_memory_cli/__init__.py +1 -1
- {ai_memory_cli-0.1.8 → ai_memory_cli-0.1.9}/src/ai_memory_cli/cli.py +56 -11
- {ai_memory_cli-0.1.8 → ai_memory_cli-0.1.9/src/ai_memory_cli.egg-info}/PKG-INFO +2 -1
- {ai_memory_cli-0.1.8 → ai_memory_cli-0.1.9}/LICENSE +0 -0
- {ai_memory_cli-0.1.8 → ai_memory_cli-0.1.9}/bin/watch.cmd +0 -0
- {ai_memory_cli-0.1.8 → ai_memory_cli-0.1.9}/setup.cfg +0 -0
- {ai_memory_cli-0.1.8 → ai_memory_cli-0.1.9}/src/ai_memory_cli/__main__.py +0 -0
- {ai_memory_cli-0.1.8 → ai_memory_cli-0.1.9}/src/ai_memory_cli.egg-info/SOURCES.txt +0 -0
- {ai_memory_cli-0.1.8 → ai_memory_cli-0.1.9}/src/ai_memory_cli.egg-info/dependency_links.txt +0 -0
- {ai_memory_cli-0.1.8 → ai_memory_cli-0.1.9}/src/ai_memory_cli.egg-info/entry_points.txt +0 -0
- {ai_memory_cli-0.1.8 → 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
|
|
@@ -69,6 +69,7 @@ On Windows, `python -m ai_memory_cli ...` is the safest form because it avoids P
|
|
|
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
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.
|
|
72
73
|
|
|
73
74
|
## Background agent
|
|
74
75
|
|
|
@@ -47,6 +47,7 @@ On Windows, `python -m ai_memory_cli ...` is the safest form because it avoids P
|
|
|
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
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.
|
|
50
51
|
|
|
51
52
|
## Background agent
|
|
52
53
|
|
|
@@ -43,6 +43,7 @@ SHELL_NOT_FOUND_PATTERNS = [
|
|
|
43
43
|
"command not found",
|
|
44
44
|
]
|
|
45
45
|
CLEAR_COMMANDS = {"cls", "clear"}
|
|
46
|
+
INTERACTIVE_SHELLS = {"powershell", "pwsh", "cmd", "bash", "sh", "zsh", "fish"}
|
|
46
47
|
|
|
47
48
|
|
|
48
49
|
def utc_now() -> str:
|
|
@@ -326,6 +327,44 @@ def is_clear_command(command: str) -> bool:
|
|
|
326
327
|
return normalize_command(command).lower() in CLEAR_COMMANDS
|
|
327
328
|
|
|
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
|
+
|
|
329
368
|
def clear_console() -> None:
|
|
330
369
|
command = "cls" if os.name == "nt" else "clear"
|
|
331
370
|
try:
|
|
@@ -341,10 +380,15 @@ def normalize_output(stdout: str, stderr: str) -> str:
|
|
|
341
380
|
|
|
342
381
|
|
|
343
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"
|
|
344
388
|
shell = os.getenv("SHELL") or os.getenv("COMSPEC") or ""
|
|
345
389
|
if shell:
|
|
346
390
|
return Path(shell).name
|
|
347
|
-
return "
|
|
391
|
+
return "sh"
|
|
348
392
|
|
|
349
393
|
|
|
350
394
|
def command_line(parts: list[str]) -> str:
|
|
@@ -649,25 +693,23 @@ def capture_command(home: Path, config: dict[str, Any], command: str, include_ex
|
|
|
649
693
|
clear_console()
|
|
650
694
|
return 0
|
|
651
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
|
+
|
|
652
700
|
require_verified_auth(home, config)
|
|
653
701
|
workspace = Path(str(config.get("workspace_path") or ".")).expanduser()
|
|
654
702
|
cwd = workspace if workspace.exists() else Path.cwd()
|
|
655
703
|
|
|
656
704
|
if is_excluded(command, config) and not include_excluded:
|
|
657
705
|
print(f"ai-memory: running without capture because this command is excluded: {command}", file=sys.stderr)
|
|
658
|
-
return
|
|
706
|
+
return int(run_external_command(command, cwd, capture=False))
|
|
659
707
|
|
|
660
708
|
started_at = utc_now()
|
|
661
709
|
started_monotonic = time.monotonic()
|
|
662
|
-
completed =
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
cwd=str(cwd),
|
|
666
|
-
capture_output=True,
|
|
667
|
-
text=True,
|
|
668
|
-
encoding="utf-8",
|
|
669
|
-
errors="replace",
|
|
670
|
-
)
|
|
710
|
+
completed = run_external_command(command, cwd, capture=True)
|
|
711
|
+
if isinstance(completed, int):
|
|
712
|
+
return completed
|
|
671
713
|
ended_at = utc_now()
|
|
672
714
|
duration_ms = int((time.monotonic() - started_monotonic) * 1000)
|
|
673
715
|
|
|
@@ -888,6 +930,9 @@ def command_watch(args: argparse.Namespace) -> int:
|
|
|
888
930
|
if is_clear_command(command):
|
|
889
931
|
clear_console()
|
|
890
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
|
|
891
936
|
capture_command(home, config, command, args.include_excluded, "watch")
|
|
892
937
|
return 0
|
|
893
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
|
|
@@ -69,6 +69,7 @@ On Windows, `python -m ai_memory_cli ...` is the safest form because it avoids P
|
|
|
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
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.
|
|
72
73
|
|
|
73
74
|
## Background agent
|
|
74
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
|