ayechat 0.1.5__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.
ayechat-0.1.5/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Acrotron
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
ayechat-0.1.5/PKG-INFO ADDED
@@ -0,0 +1,83 @@
1
+ Metadata-Version: 2.4
2
+ Name: ayechat
3
+ Version: 0.1.5
4
+ Summary: Aye: Terminal-first AI Code Generator
5
+ Author-email: "Acrotron, Inc." <info@acrotron.com>
6
+ License: MIT
7
+ Requires-Python: >=3.8
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Requires-Dist: typer[all]>=0.9.0
11
+ Requires-Dist: httpx>=0.27.0
12
+ Requires-Dist: keyring>=24.0.0
13
+ Requires-Dist: prompt-toolkit>=3.0.0
14
+ Provides-Extra: dev
15
+ Requires-Dist: pytest>=7.0; extra == "dev"
16
+ Requires-Dist: ruff>=0.6; extra == "dev"
17
+ Dynamic: license-file
18
+
19
+ # aye
20
+ Aye: AI‑powered coding assistant for the terminal
21
+
22
+ **Aye** is a terminal‑only AI assistant that:
23
+
24
+ * generates code from natural‑language prompts,
25
+ * automatically snapshots the target file before each change,
26
+ * lets you list, view, and revert those snapshots (`aye snap …`),
27
+ * uses a simple token‑based authentication model (no Cognito required).
28
+
29
+ ---
30
+
31
+ ## Quick start
32
+
33
+ ```bash
34
+ # Install from PyPI
35
+ pip install aye-cli
36
+
37
+ # Authenticate (you’ll get a token from your backend)
38
+ aye login
39
+
40
+ # One‑shot generation
41
+ aye generate "create a Python function that parses CSV" -f utils.py
42
+
43
+ # Interactive chat (optional)
44
+ aye chat -f utils.py
45
+
46
+ # Undo / snapshot utilities
47
+ aye snap list utils.py
48
+ aye snap show utils.py 20241012T153045
49
+ aye snap revert utils.py 20241012T153045
50
+
51
+ Development
52
+
53
+ # Clone the repo
54
+ git clone https://github.com/yourorg/aye.git
55
+ cd aye
56
+
57
+ # Create a virtual environment
58
+ python -m venv .venv && source .venv/bin/activate
59
+
60
+ # Install in editable mode with dev deps
61
+ pip install -e .[dev]
62
+
63
+ # Run the test suite
64
+ pytest -q
65
+
66
+ Packaging & distribution
67
+
68
+ # Build a wheel and source distribution
69
+ python -m build
70
+
71
+ # Install the wheel locally for a clean test
72
+ pip install dist/aye_cli-0.1.0-py3-none-any.whl
73
+
74
+ The console script aye will be available on the PATH.
75
+ License
76
+
77
+ MIT – see the LICENSE file (or the license header in pyproject.toml).
78
+ Contributing
79
+
80
+ Feel free to open issues or submit pull requests.
81
+ All contributions are welcome; just follow the usual GitHub workflow.
82
+
83
+
@@ -0,0 +1,65 @@
1
+ # aye
2
+ Aye: AI‑powered coding assistant for the terminal
3
+
4
+ **Aye** is a terminal‑only AI assistant that:
5
+
6
+ * generates code from natural‑language prompts,
7
+ * automatically snapshots the target file before each change,
8
+ * lets you list, view, and revert those snapshots (`aye snap …`),
9
+ * uses a simple token‑based authentication model (no Cognito required).
10
+
11
+ ---
12
+
13
+ ## Quick start
14
+
15
+ ```bash
16
+ # Install from PyPI
17
+ pip install aye-cli
18
+
19
+ # Authenticate (you’ll get a token from your backend)
20
+ aye login
21
+
22
+ # One‑shot generation
23
+ aye generate "create a Python function that parses CSV" -f utils.py
24
+
25
+ # Interactive chat (optional)
26
+ aye chat -f utils.py
27
+
28
+ # Undo / snapshot utilities
29
+ aye snap list utils.py
30
+ aye snap show utils.py 20241012T153045
31
+ aye snap revert utils.py 20241012T153045
32
+
33
+ Development
34
+
35
+ # Clone the repo
36
+ git clone https://github.com/yourorg/aye.git
37
+ cd aye
38
+
39
+ # Create a virtual environment
40
+ python -m venv .venv && source .venv/bin/activate
41
+
42
+ # Install in editable mode with dev deps
43
+ pip install -e .[dev]
44
+
45
+ # Run the test suite
46
+ pytest -q
47
+
48
+ Packaging & distribution
49
+
50
+ # Build a wheel and source distribution
51
+ python -m build
52
+
53
+ # Install the wheel locally for a clean test
54
+ pip install dist/aye_cli-0.1.0-py3-none-any.whl
55
+
56
+ The console script aye will be available on the PATH.
57
+ License
58
+
59
+ MIT – see the LICENSE file (or the license header in pyproject.toml).
60
+ Contributing
61
+
62
+ Feel free to open issues or submit pull requests.
63
+ All contributions are welcome; just follow the usual GitHub workflow.
64
+
65
+
@@ -0,0 +1,40 @@
1
+ [project]
2
+ name = "ayechat" # the name that will appear on PyPI
3
+ version = "0.1.5"
4
+ description = "Aye: Terminal-first AI Code Generator"
5
+ readme = "README.md"
6
+ requires-python = ">=3.8"
7
+ license = {text = "MIT"}
8
+ authors = [{name = "Acrotron, Inc.", email = "info@acrotron.com"}]
9
+
10
+ # Runtime dependencies
11
+ dependencies = [
12
+ "typer[all]>=0.9.0", # CLI framework + rich formatting
13
+ "httpx>=0.27.0", # HTTP client
14
+ "keyring>=24.0.0", # Secure storage for the token
15
+ "prompt-toolkit>=3.0.0", # REPL UI
16
+ ]
17
+
18
+ # Optional development dependencies (tests, linting, etc.)
19
+ [project.optional-dependencies]
20
+ dev = [
21
+ "pytest>=7.0", # test runner
22
+ "ruff>=0.6", # linting/formatter (optional)
23
+ ]
24
+
25
+ # Console script – after installation `aye` will be on the PATH
26
+ [project.scripts]
27
+ aye = "aye.__main__:app"
28
+
29
+ # -------------------------------------------------
30
+ # Build system configuration – tells setuptools where the code lives
31
+ # -------------------------------------------------
32
+ [build-system]
33
+ requires = ["setuptools", "wheel"]
34
+ build-backend = "setuptools.build_meta"
35
+
36
+ [tool.setuptools]
37
+ package-dir = {"" = "src"} # “src/” is the source root
38
+ packages = ["aye"] # the only package we ship
39
+ include-package-data = true
40
+
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
File without changes
@@ -0,0 +1,227 @@
1
+ from pathlib import Path
2
+ import typer
3
+
4
+ from .service import (
5
+ handle_login,
6
+ handle_logout,
7
+ handle_generate_cmd,
8
+ handle_chat,
9
+ handle_history_cmd,
10
+ handle_snap_show_cmd,
11
+ handle_restore_cmd,
12
+ handle_prune_cmd,
13
+ handle_cleanup_cmd,
14
+ handle_config_list,
15
+ handle_config_set,
16
+ handle_config_get,
17
+ handle_config_delete
18
+ )
19
+
20
+ from .config import load_config
21
+
22
+ # Load configuration at startup
23
+ load_config()
24
+
25
+ app = typer.Typer(help="Aye: AI‑powered coding assistant for the terminal")
26
+
27
+ # Create subcommands
28
+ auth_app = typer.Typer(help="Authentication commands")
29
+ snap_app = typer.Typer(help="Snapshot management commands")
30
+
31
+ app.add_typer(auth_app, name="auth")
32
+ app.add_typer(snap_app, name="snap")
33
+
34
+ # ----------------------------------------------------------------------
35
+ # Authentication commands
36
+ # ----------------------------------------------------------------------
37
+ @auth_app.command()
38
+ def login():
39
+ """
40
+ Configure personal access token for authenticating with the aye service.
41
+ """
42
+ handle_login()
43
+
44
+
45
+ @auth_app.command()
46
+ def logout():
47
+ """
48
+ Remove the stored aye credentials.
49
+
50
+ Examples: \n
51
+ aye auth logout
52
+ """
53
+ handle_logout()
54
+
55
+ # ----------------------------------------------------------------------
56
+ # One‑shot generation
57
+ # ----------------------------------------------------------------------
58
+ @app.command()
59
+ def generate(
60
+ prompt: str = typer.Argument(..., help="Prompt for the LLM"),
61
+ mode: str = typer.Option(
62
+ "replace",
63
+ "--mode",
64
+ "-m",
65
+ help="replace | append | insert (default: replace)",
66
+ ),
67
+ ):
68
+ """
69
+ Send a single prompt to the backend.
70
+
71
+ Examples: \n
72
+ aye generate "Create a function that reverses a string" \n
73
+ aye generate "Add type hints to this function" --mode append \n
74
+ """
75
+ handle_generate_cmd(prompt, mode)
76
+
77
+ # ----------------------------------------------------------------------
78
+ # Interactive REPL (chat) command
79
+ # ----------------------------------------------------------------------
80
+ @app.command()
81
+ def chat(
82
+ root: Path = typer.Option(
83
+ None, "--root", "-r", help="Root folder where source files are located."
84
+ ),
85
+ file_mask: str = typer.Option(
86
+ "*.py", "--file-mask", "-m", help="File mask for source files to include into generation. Comma-separated masks are allowed."
87
+ ),
88
+ ):
89
+ """
90
+ Start an interactive REPL. Use /exit or Ctrl‑D to leave.
91
+
92
+ Examples: \n
93
+ aye chat \n
94
+ aye chat --root ./src \n
95
+ aye chat --file-mask "*.js" --root ./frontend \n
96
+ """
97
+ handle_chat(root, file_mask)
98
+
99
+ # ----------------------------------------------------------------------
100
+ # Snapshot commands
101
+ # ----------------------------------------------------------------------
102
+ @snap_app.command("history")
103
+ def history(
104
+ file: Path = typer.Argument(None, help="File to list snapshots for")
105
+ ):
106
+ """
107
+ Show timestamps of saved snapshots for *file* or all snapshots if no file provided.
108
+
109
+ Examples: \n
110
+ aye snap history \n
111
+ aye snap history src/main.py \n
112
+ """
113
+ handle_history_cmd(file)
114
+
115
+
116
+ @snap_app.command("show")
117
+ def show(
118
+ file: Path = typer.Argument(..., help="File whose snapshot to show"),
119
+ ordinal: str = typer.Argument(..., help="Ordinal of the snapshot (e.g., 001)"),
120
+ ):
121
+ """
122
+ Print the contents of a specific snapshot.
123
+
124
+ Examples: \n
125
+ aye snap show src/main.py 001 \n
126
+ """
127
+ handle_snap_show_cmd(file, ordinal)
128
+
129
+
130
+ @snap_app.command("restore")
131
+ def restore(
132
+ ordinal: str = typer.Argument(None, help="Ordinal of the snapshot to restore (e.g., 001, default: latest)"),
133
+ file_name: str = typer.Argument(None, help="Specific file to restore from the snapshot"),
134
+ ):
135
+ """
136
+ Replace all files with the latest snapshot or specified snapshot by ordinal.
137
+ If file_name is provided, only that file is restored.
138
+
139
+ Examples:\n
140
+ aye snap restore \n
141
+ aye snap restore 001 \n
142
+ aye snap restore 001 myfile.py \n
143
+ """
144
+ handle_restore_cmd(ordinal, file_name)
145
+
146
+
147
+ # ----------------------------------------------------------------------
148
+ # Snapshot cleanup/pruning commands
149
+ # ----------------------------------------------------------------------
150
+ @snap_app.command()
151
+ def keep(
152
+ num: int = typer.Option(10, "--num", "-n", help="Number of recent snapshots to keep (default: 10)"),
153
+ ):
154
+ """
155
+ Delete all but the most recent N snapshots.
156
+
157
+ Examples: \n
158
+ aye snap keep \n
159
+ aye snap keep --num 5 \n
160
+ aye snap keep -n 3 \n
161
+ """
162
+ handle_prune_cmd(num)
163
+
164
+
165
+ @snap_app.command()
166
+ def cleanup(
167
+ days: int = typer.Option(30, "--days", "-d", help="Delete snapshots older than N days (default: 30)"),
168
+ ):
169
+ """
170
+ Delete snapshots older than N days.
171
+
172
+ Examples: \n
173
+ aye snap cleanup \n
174
+ aye snap cleanup --days 7 \n
175
+ aye snap cleanup -d 14 \n
176
+ """
177
+ handle_cleanup_cmd(days)
178
+
179
+
180
+ # ----------------------------------------------------------------------
181
+ # Configuration management commands
182
+ # ----------------------------------------------------------------------
183
+ @app.command()
184
+ def config(
185
+ action: str = typer.Argument(..., help="Action to perform: list, get, set, delete"),
186
+ key: str = typer.Argument(None, help="Configuration key"),
187
+ value: str = typer.Argument(None, help="Configuration value (for set action)"),
188
+ ):
189
+ """
190
+ Manage configuration values for file masks, root directories, and other settings.
191
+
192
+ Actions: \n
193
+ - list: Show all configuration values \n
194
+ - get: Retrieve a specific configuration value \n
195
+ - set: Set a configuration value \n
196
+ - delete: Remove a configuration value \n
197
+
198
+ Examples: \n
199
+ aye config list \n
200
+ aye config get file_mask \n
201
+ aye config set file_mask "*.py,*.js" \n
202
+ aye config delete file_mask \n
203
+ """
204
+ if action == "list":
205
+ handle_config_list()
206
+ elif action == "get":
207
+ if not key:
208
+ typer.echo("[red]Error:[/] Key is required for get action.")
209
+ raise typer.Exit(code=1)
210
+ handle_config_get(key)
211
+ elif action == "set":
212
+ if not key or not value:
213
+ typer.echo("[red]Error:[/] Key and value are required for set action.")
214
+ raise typer.Exit(code=1)
215
+ handle_config_set(key, value)
216
+ elif action == "delete":
217
+ if not key:
218
+ typer.echo("[red]Error:[/] Key is required for delete action.")
219
+ raise typer.Exit(code=1)
220
+ handle_config_delete(key)
221
+ else:
222
+ typer.echo(f"[red]Error:[/] Invalid action '{action}'. Use: list, get, set, delete")
223
+ raise typer.Exit(code=1)
224
+
225
+
226
+ if __name__ == "__main__":
227
+ app()
@@ -0,0 +1,49 @@
1
+ import json
2
+ from typing import Any, Dict
3
+
4
+ import httpx
5
+ from .auth import get_token
6
+
7
+ # -------------------------------------------------
8
+ # 👉 EDIT THIS TO POINT TO YOUR SERVICE
9
+ # -------------------------------------------------
10
+ BASE_URL = "https://api.acrotron.com"
11
+ TIMEOUT = 30.0
12
+
13
+
14
+ def _auth_headers() -> Dict[str, str]:
15
+ token = get_token()
16
+ if not token:
17
+ raise RuntimeError("No auth token – run `aye login` first.")
18
+ return {"Authorization": f"Bearer {token}"}
19
+
20
+
21
+ def cli_invoke(user_id="v@acrotron.com", chat_id=-1, message="", source_files={}):
22
+ payload = {"user_id": user_id, "chat_id": chat_id, "message": message, "source_files": source_files}
23
+
24
+ url = f"{BASE_URL}/invoke_cli"
25
+
26
+ with httpx.Client(timeout=TIMEOUT, verify=False) as client:
27
+ resp = client.post(url, json=payload, headers=_auth_headers())
28
+ resp.raise_for_status()
29
+ return resp.json()
30
+
31
+
32
+ def fetch_plugin_manifest():
33
+ """Fetch the plugin manifest from the server."""
34
+ url = f"{BASE_URL}/plugins"
35
+
36
+ with httpx.Client(timeout=TIMEOUT, verify=False) as client:
37
+ resp = client.post(url, headers=_auth_headers())
38
+ resp.raise_for_status()
39
+ return resp.json()
40
+
41
+
42
+ def fetch_server_time() -> int:
43
+ """Fetch the current server timestamp."""
44
+ url = f"{BASE_URL}/time"
45
+
46
+ with httpx.Client(timeout=TIMEOUT, verify=False) as client:
47
+ resp = client.get(url)
48
+ resp.raise_for_status()
49
+ return resp.json()['timestamp']
@@ -0,0 +1,54 @@
1
+ # auth.py
2
+ import os
3
+ import typer
4
+ from pathlib import Path
5
+ from rich import print as rprint
6
+
7
+ SERVICE_NAME = "aye-cli"
8
+ TOKEN_ENV_VAR = "AYE_TOKEN"
9
+ TOKEN_FILE = Path.home() / ".ayecfg"
10
+
11
+
12
+ def store_token(token: str) -> None:
13
+ """Persist the token in ~/.ayecfg (unless AYE_TOKEN is set)."""
14
+ TOKEN_FILE.write_text(token.strip(), encoding="utf-8")
15
+ TOKEN_FILE.chmod(0o600) # POSIX only
16
+
17
+
18
+ def get_token() -> str | None:
19
+ """Return the stored token (env → file)."""
20
+ # 1. Try environment variable first
21
+ env_token = os.getenv(TOKEN_ENV_VAR)
22
+ if env_token:
23
+ return env_token.strip()
24
+
25
+ # 2. Try config file
26
+ if TOKEN_FILE.is_file():
27
+ try:
28
+ return TOKEN_FILE.read_text(encoding="utf-8").strip()
29
+ except Exception:
30
+ pass # Continue if file read fails
31
+
32
+ return None
33
+
34
+
35
+ def delete_token() -> None:
36
+ """Delete the token from file (but not environment)."""
37
+ # Delete the file-based token
38
+ TOKEN_FILE.unlink(missing_ok=True)
39
+
40
+
41
+ def login_flow() -> None:
42
+ """
43
+ Small login flow:
44
+ 1. Prompt user to obtain token at https://aye.acrotron.com
45
+ 2. User enters/pastes the token in terminal (hidden input)
46
+ 3. Save the token to ~/.ayecfg (if AYE_TOKEN not set)
47
+ """
48
+ #typer.echo(
49
+ # "Obtain your personal access token at https://aye.acrotron.com"
50
+ #)
51
+ rprint("[yellow]Obtain your personal access token at https://aye.acrotron.com[/]")
52
+ token = typer.prompt("Paste your token", hide_input=True)
53
+ store_token(token.strip())
54
+ typer.secho("✅ Token saved.", fg=typer.colors.GREEN)