aethr 0.1.0__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.
aethr-0.1.0/.gitignore ADDED
@@ -0,0 +1,7 @@
1
+ .venv/
2
+ __pycache__/
3
+ *.py[cod]
4
+ *.egg-info/
5
+ .pytest_cache/
6
+ .DS_Store
7
+ dist/
aethr-0.1.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Archana Pradeep
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.
aethr-0.1.0/PKG-INFO ADDED
@@ -0,0 +1,218 @@
1
+ Metadata-Version: 2.4
2
+ Name: aethr
3
+ Version: 0.1.0
4
+ Summary: A lightweight CLI for explicit, reproducible AI coding workflows.
5
+ Project-URL: Homepage, https://github.com/archthegit/Relay
6
+ Project-URL: Repository, https://github.com/archthegit/Relay
7
+ Project-URL: Issues, https://github.com/archthegit/Relay/issues
8
+ Author: Archana Pradeep
9
+ License-Expression: MIT
10
+ License-File: LICENSE
11
+ Keywords: ai,cli,coding,llm,workflow
12
+ Classifier: Development Status :: 3 - Alpha
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.12
18
+ Classifier: Programming Language :: Python :: 3.13
19
+ Classifier: Topic :: Software Development
20
+ Classifier: Topic :: Utilities
21
+ Requires-Python: >=3.12
22
+ Requires-Dist: litellm>=1.60.0
23
+ Requires-Dist: pydantic>=2.10.0
24
+ Requires-Dist: pyyaml>=6.0.2
25
+ Requires-Dist: rich>=13.9.0
26
+ Requires-Dist: typer>=0.15.0
27
+ Provides-Extra: dev
28
+ Requires-Dist: pytest>=8.0.0; extra == 'dev'
29
+ Description-Content-Type: text/markdown
30
+
31
+ # Aethr
32
+
33
+ A tiny CLI for running explicit AI coding workflows from YAML.
34
+
35
+ ## Core Idea
36
+
37
+ Coding with LLMs is not one-shot generation.
38
+
39
+ Real development is:
40
+
41
+ ```text
42
+ plan -> implement -> review -> iterate
43
+ ```
44
+
45
+ Aethr makes those workflows programmable. A run is just:
46
+
47
+ ```text
48
+ task + workflow + explicit context + model routing
49
+ ```
50
+
51
+ Aethr is stateless. The only project file it creates is `.aethr.yaml`.
52
+
53
+ ## Install
54
+
55
+ ```bash
56
+ pip install aethr
57
+ ```
58
+
59
+ For local development:
60
+
61
+ ```bash
62
+ pip install -e ".[dev]"
63
+ ```
64
+
65
+ ## Quickstart
66
+
67
+ ```bash
68
+ aethr init review-existing-diff
69
+ aethr run "review my current changes before I commit"
70
+ ```
71
+
72
+ Aethr copies a YAML preset into `.aethr.yaml`. Edit it like any other project
73
+ file.
74
+
75
+ ## How Aethr Works
76
+
77
+ - **Task**: the instruction passed on the command line.
78
+ - **Workflow**: the YAML file that defines ordered steps.
79
+ - **Steps**: sequential units of work. Aethr runs them in order.
80
+ - **Roles**: named responsibilities such as `planner`, `reviewer`, or `writer`.
81
+ - **Context**: explicit repo input declared per step.
82
+ - **Model routing**: each role can point at a different LiteLLM model.
83
+
84
+ Each step receives the task, prior step outputs, and its declared context. The
85
+ step result stays in memory and is printed to the terminal.
86
+
87
+ ## Example Workflow Config
88
+
89
+ ```yaml
90
+ workflow: review-existing-diff
91
+
92
+ roles:
93
+ reviewer: Review the provided task context as if it were an existing diff.
94
+
95
+ models:
96
+ reviewer: openai:gpt-5.5
97
+
98
+ steps:
99
+ - id: review
100
+ role: reviewer
101
+ context:
102
+ - git_diff
103
+ ```
104
+
105
+ ## Built-In Workflows
106
+
107
+ - `plan-implement-review`: plan a task, propose an implementation, review it.
108
+ - `review-existing-diff`: review the current working tree diff.
109
+ - `debug-failing-test`: diagnose a failing test, propose a fix, review it.
110
+ - `add-tests`: plan, draft, and review focused test coverage.
111
+ - `docs-sync`: update docs from the current diff and README context.
112
+ - `custom`: a minimal one-step workflow to edit freely.
113
+
114
+ List presets:
115
+
116
+ ```bash
117
+ aethr init --list
118
+ ```
119
+
120
+ Initialize another preset:
121
+
122
+ ```bash
123
+ aethr init docs-sync --force
124
+ ```
125
+
126
+ ## Examples
127
+
128
+ The `examples/` directory contains small workflow files you can copy from:
129
+
130
+ - `examples/review-existing-diff.yaml`
131
+ - `examples/add-tests.yaml`
132
+ - `examples/docs-sync.yaml`
133
+
134
+ ## Explicit Context
135
+
136
+ Aethr uses explicit context instead of automatic retrieval. That keeps runs easy
137
+ to understand: the YAML shows exactly what each step can see.
138
+
139
+ Supported context sources:
140
+
141
+ - `git_diff`: runs `git diff --no-ext-diff`.
142
+ - `file:<path>`: reads one UTF-8 file relative to the project root.
143
+ - `glob:<pattern>`: reads matching UTF-8 files relative to the project root,
144
+ with a small content cap.
145
+
146
+ Example:
147
+
148
+ ```yaml
149
+ steps:
150
+ - id: review-docs
151
+ role: reviewer
152
+ context:
153
+ - git_diff
154
+ - file:README.md
155
+ - glob:docs/**/*.md
156
+ ```
157
+
158
+ Missing files, empty diffs, non-git directories, and unreadable files appear as
159
+ clear placeholder notes in the prompt.
160
+
161
+ ## Prompt Previewing
162
+
163
+ Use `--show-prompt` to see exactly what Aethr would send to each model:
164
+
165
+ ```bash
166
+ aethr run "review my current changes before I commit" --show-prompt
167
+ ```
168
+
169
+ Aethr does not call models in prompt preview mode. For later steps, it uses a
170
+ clear placeholder where real previous step output would appear.
171
+
172
+ ## Mock Mode
173
+
174
+ Aethr works without API keys by returning deterministic mock responses.
175
+
176
+ Use the models configured in `.aethr.yaml`:
177
+
178
+ ```bash
179
+ AETHR_LIVE=1 aethr run "review my current changes"
180
+ ```
181
+
182
+ Override every configured model with one LiteLLM model:
183
+
184
+ ```bash
185
+ AETHR_MODEL=openai:gpt-5.5 aethr run "review my current changes"
186
+ ```
187
+
188
+ ## Philosophy
189
+
190
+ Aethr should feel like:
191
+
192
+ - `git`
193
+ - `pytest`
194
+ - `rg`
195
+ - `cargo`
196
+
197
+ It should not feel like:
198
+
199
+ - an agent framework
200
+ - an autonomous coding platform
201
+ - an AI operating system
202
+
203
+ Aethr intentionally avoids persistence, replay systems, caches, plugins, DAGs,
204
+ async runtimes, vector search, automatic retrieval, memory systems, and agent
205
+ abstractions.
206
+
207
+ ## Architecture
208
+
209
+ ```text
210
+ aethr/
211
+ cli.py
212
+ config.py
213
+ context.py
214
+ executor.py
215
+ llm.py
216
+ prompts.py
217
+ workflow.py
218
+ ```
aethr-0.1.0/README.md ADDED
@@ -0,0 +1,188 @@
1
+ # Aethr
2
+
3
+ A tiny CLI for running explicit AI coding workflows from YAML.
4
+
5
+ ## Core Idea
6
+
7
+ Coding with LLMs is not one-shot generation.
8
+
9
+ Real development is:
10
+
11
+ ```text
12
+ plan -> implement -> review -> iterate
13
+ ```
14
+
15
+ Aethr makes those workflows programmable. A run is just:
16
+
17
+ ```text
18
+ task + workflow + explicit context + model routing
19
+ ```
20
+
21
+ Aethr is stateless. The only project file it creates is `.aethr.yaml`.
22
+
23
+ ## Install
24
+
25
+ ```bash
26
+ pip install aethr
27
+ ```
28
+
29
+ For local development:
30
+
31
+ ```bash
32
+ pip install -e ".[dev]"
33
+ ```
34
+
35
+ ## Quickstart
36
+
37
+ ```bash
38
+ aethr init review-existing-diff
39
+ aethr run "review my current changes before I commit"
40
+ ```
41
+
42
+ Aethr copies a YAML preset into `.aethr.yaml`. Edit it like any other project
43
+ file.
44
+
45
+ ## How Aethr Works
46
+
47
+ - **Task**: the instruction passed on the command line.
48
+ - **Workflow**: the YAML file that defines ordered steps.
49
+ - **Steps**: sequential units of work. Aethr runs them in order.
50
+ - **Roles**: named responsibilities such as `planner`, `reviewer`, or `writer`.
51
+ - **Context**: explicit repo input declared per step.
52
+ - **Model routing**: each role can point at a different LiteLLM model.
53
+
54
+ Each step receives the task, prior step outputs, and its declared context. The
55
+ step result stays in memory and is printed to the terminal.
56
+
57
+ ## Example Workflow Config
58
+
59
+ ```yaml
60
+ workflow: review-existing-diff
61
+
62
+ roles:
63
+ reviewer: Review the provided task context as if it were an existing diff.
64
+
65
+ models:
66
+ reviewer: openai:gpt-5.5
67
+
68
+ steps:
69
+ - id: review
70
+ role: reviewer
71
+ context:
72
+ - git_diff
73
+ ```
74
+
75
+ ## Built-In Workflows
76
+
77
+ - `plan-implement-review`: plan a task, propose an implementation, review it.
78
+ - `review-existing-diff`: review the current working tree diff.
79
+ - `debug-failing-test`: diagnose a failing test, propose a fix, review it.
80
+ - `add-tests`: plan, draft, and review focused test coverage.
81
+ - `docs-sync`: update docs from the current diff and README context.
82
+ - `custom`: a minimal one-step workflow to edit freely.
83
+
84
+ List presets:
85
+
86
+ ```bash
87
+ aethr init --list
88
+ ```
89
+
90
+ Initialize another preset:
91
+
92
+ ```bash
93
+ aethr init docs-sync --force
94
+ ```
95
+
96
+ ## Examples
97
+
98
+ The `examples/` directory contains small workflow files you can copy from:
99
+
100
+ - `examples/review-existing-diff.yaml`
101
+ - `examples/add-tests.yaml`
102
+ - `examples/docs-sync.yaml`
103
+
104
+ ## Explicit Context
105
+
106
+ Aethr uses explicit context instead of automatic retrieval. That keeps runs easy
107
+ to understand: the YAML shows exactly what each step can see.
108
+
109
+ Supported context sources:
110
+
111
+ - `git_diff`: runs `git diff --no-ext-diff`.
112
+ - `file:<path>`: reads one UTF-8 file relative to the project root.
113
+ - `glob:<pattern>`: reads matching UTF-8 files relative to the project root,
114
+ with a small content cap.
115
+
116
+ Example:
117
+
118
+ ```yaml
119
+ steps:
120
+ - id: review-docs
121
+ role: reviewer
122
+ context:
123
+ - git_diff
124
+ - file:README.md
125
+ - glob:docs/**/*.md
126
+ ```
127
+
128
+ Missing files, empty diffs, non-git directories, and unreadable files appear as
129
+ clear placeholder notes in the prompt.
130
+
131
+ ## Prompt Previewing
132
+
133
+ Use `--show-prompt` to see exactly what Aethr would send to each model:
134
+
135
+ ```bash
136
+ aethr run "review my current changes before I commit" --show-prompt
137
+ ```
138
+
139
+ Aethr does not call models in prompt preview mode. For later steps, it uses a
140
+ clear placeholder where real previous step output would appear.
141
+
142
+ ## Mock Mode
143
+
144
+ Aethr works without API keys by returning deterministic mock responses.
145
+
146
+ Use the models configured in `.aethr.yaml`:
147
+
148
+ ```bash
149
+ AETHR_LIVE=1 aethr run "review my current changes"
150
+ ```
151
+
152
+ Override every configured model with one LiteLLM model:
153
+
154
+ ```bash
155
+ AETHR_MODEL=openai:gpt-5.5 aethr run "review my current changes"
156
+ ```
157
+
158
+ ## Philosophy
159
+
160
+ Aethr should feel like:
161
+
162
+ - `git`
163
+ - `pytest`
164
+ - `rg`
165
+ - `cargo`
166
+
167
+ It should not feel like:
168
+
169
+ - an agent framework
170
+ - an autonomous coding platform
171
+ - an AI operating system
172
+
173
+ Aethr intentionally avoids persistence, replay systems, caches, plugins, DAGs,
174
+ async runtimes, vector search, automatic retrieval, memory systems, and agent
175
+ abstractions.
176
+
177
+ ## Architecture
178
+
179
+ ```text
180
+ aethr/
181
+ cli.py
182
+ config.py
183
+ context.py
184
+ executor.py
185
+ llm.py
186
+ prompts.py
187
+ workflow.py
188
+ ```
@@ -0,0 +1,3 @@
1
+ """Aethr: explicit pipelines for AI coding workflows."""
2
+
3
+ __version__ = "0.1.0"
@@ -0,0 +1,132 @@
1
+ """Command-line interface for Aethr."""
2
+
3
+ from typing import Annotated
4
+
5
+ import typer
6
+ from rich.console import Console
7
+ from rich.panel import Panel
8
+
9
+ from aethr import __version__
10
+ from aethr.config import CONFIG_FILE, ConfigError, load_workflow_config
11
+ from aethr.executor import StepPrompt, StepResult, build_workflow_prompts, run_workflow
12
+ from aethr.llm import LLMError
13
+ from aethr.workflow import WorkflowTemplateError, available_workflows, init_workflow
14
+
15
+
16
+ app = typer.Typer(help="Explicit, reproducible AI coding workflows.")
17
+ console = Console()
18
+
19
+
20
+ @app.callback()
21
+ def main() -> None:
22
+ """Aethr command group."""
23
+
24
+
25
+ @app.command()
26
+ def init(
27
+ preset: Annotated[str | None, typer.Argument(help="Workflow preset to initialize.")] = None,
28
+ force: Annotated[bool, typer.Option("--force", "-f", help="Overwrite an existing .aethr.yaml.")] = False,
29
+ list_only: Annotated[bool, typer.Option("--list", "-l", help="List available workflow presets.")] = False,
30
+ ) -> None:
31
+ """Initialize a workflow config from a built-in YAML preset."""
32
+
33
+ workflows = available_workflows()
34
+ if list_only:
35
+ for workflow in workflows:
36
+ console.print(workflow)
37
+ return
38
+
39
+ selected = preset
40
+ if selected is None:
41
+ console.print("Available workflows:")
42
+ for index, workflow in enumerate(workflows, start=1):
43
+ console.print(f" {index}. {workflow}")
44
+ choice = typer.prompt("Select a workflow", default="1")
45
+ selected = _resolve_workflow_choice(choice, workflows)
46
+
47
+ try:
48
+ path = init_workflow(selected, force=force)
49
+ except WorkflowTemplateError as exc:
50
+ raise typer.BadParameter(str(exc)) from exc
51
+
52
+ console.print(f"Initialized [bold]{path}[/bold] from [cyan]{selected}[/cyan]")
53
+
54
+
55
+ @app.command()
56
+ def run(
57
+ task: Annotated[str, typer.Argument(help="Coding task to run through the pipeline.")],
58
+ show_prompt: Annotated[
59
+ bool,
60
+ typer.Option("--show-prompt", help="Print exact step prompts without calling models."),
61
+ ] = False,
62
+ ) -> None:
63
+ """Run the configured sequential workflow."""
64
+
65
+ console.print(Panel.fit(task, title="Aethr Task", border_style="cyan"))
66
+ try:
67
+ config = load_workflow_config()
68
+ except ConfigError as exc:
69
+ raise typer.BadParameter(f"{exc}. Run 'aethr init' to create {CONFIG_FILE}.") from exc
70
+
71
+ console.print(f"[bold]Workflow[/bold] {config.workflow}")
72
+ if show_prompt:
73
+ console.print("[bold]Mode[/bold] prompt preview")
74
+ for planned in build_workflow_prompts(task, config):
75
+ _print_step_prompt(planned)
76
+ console.print("[green]Prompt preview complete[/green]")
77
+ return
78
+
79
+ try:
80
+ results = run_workflow(task, config, on_step_result=_print_step_result)
81
+ except LLMError as exc:
82
+ raise typer.BadParameter(str(exc)) from exc
83
+ console.print(f"[green]Workflow complete[/green] ({len(results)} step results)")
84
+
85
+
86
+ @app.command()
87
+ def version() -> None:
88
+ """Print the Aethr version."""
89
+
90
+ console.print(f"Aethr {__version__}")
91
+
92
+
93
+ def _print_step_result(result: StepResult) -> None:
94
+ """Print one in-memory step result."""
95
+
96
+ title = step_title(result.step_id, result.metadata)
97
+ console.print()
98
+ console.print(Panel(result.content, title=title, border_style="green"))
99
+
100
+
101
+ def _print_step_prompt(planned: StepPrompt) -> None:
102
+ """Print one planned prompt."""
103
+
104
+ title = step_title(planned.step_id, planned.metadata)
105
+ console.print()
106
+ console.print(Panel(planned.prompt, title=title, border_style="yellow"))
107
+
108
+
109
+ def step_title(step_id: str, metadata: dict[str, str]) -> str:
110
+ """Build a compact terminal title for a step."""
111
+
112
+ return (
113
+ f"{step_id} | role={metadata['role']} | "
114
+ f"model={metadata['model']} | context={metadata['context_sources']}"
115
+ )
116
+
117
+
118
+ def _resolve_workflow_choice(choice: str, workflows: list[str]) -> str:
119
+ """Resolve an interactive workflow prompt response."""
120
+
121
+ if choice.isdigit():
122
+ index = int(choice)
123
+ if 1 <= index <= len(workflows):
124
+ return workflows[index - 1]
125
+ if choice in workflows:
126
+ return choice
127
+ choices = ", ".join(workflows)
128
+ raise typer.BadParameter(f"Unknown workflow '{choice}'. Available workflows: {choices}")
129
+
130
+
131
+ if __name__ == "__main__":
132
+ app()
@@ -0,0 +1,81 @@
1
+ """Workflow configuration schema and loader."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from pathlib import Path
6
+
7
+ import yaml
8
+ from pydantic import BaseModel, ConfigDict, Field, ValidationError, model_validator
9
+
10
+
11
+ CONFIG_FILE = ".aethr.yaml"
12
+
13
+
14
+ class ConfigError(Exception):
15
+ """Raised when workflow configuration cannot be loaded."""
16
+
17
+
18
+ class WorkflowStep(BaseModel):
19
+ """One sequential step in an Aethr workflow."""
20
+
21
+ model_config = ConfigDict(extra="forbid")
22
+
23
+ id: str = Field(min_length=1)
24
+ role: str = Field(min_length=1)
25
+ context: list[str] = Field(default_factory=list)
26
+
27
+
28
+ class WorkflowConfig(BaseModel):
29
+ """A YAML-defined Aethr workflow."""
30
+
31
+ model_config = ConfigDict(extra="forbid")
32
+
33
+ workflow: str = Field(min_length=1)
34
+ roles: dict[str, str] = Field(default_factory=dict)
35
+ models: dict[str, str] = Field(default_factory=dict)
36
+ steps: list[WorkflowStep] = Field(min_length=1)
37
+
38
+ @model_validator(mode="after")
39
+ def validate_roles(self) -> "WorkflowConfig":
40
+ """Ensure configured steps and model routes reference known roles."""
41
+
42
+ known_roles = set(self.roles)
43
+ step_roles = {step.role for step in self.steps}
44
+ missing_step_roles = sorted(step_roles - known_roles)
45
+ if missing_step_roles:
46
+ joined = ", ".join(missing_step_roles)
47
+ raise ValueError(f"steps reference undefined roles: {joined}")
48
+
49
+ unknown_model_roles = sorted(set(self.models) - known_roles)
50
+ if unknown_model_roles:
51
+ joined = ", ".join(unknown_model_roles)
52
+ raise ValueError(f"models reference undefined roles: {joined}")
53
+
54
+ return self
55
+
56
+
57
+ def load_workflow_config(path: Path | str = CONFIG_FILE) -> WorkflowConfig:
58
+ """Load and validate an Aethr workflow config file."""
59
+
60
+ config_path = Path(path)
61
+ if not config_path.exists():
62
+ raise ConfigError(f"Workflow config not found: {config_path}")
63
+
64
+ try:
65
+ raw = yaml.safe_load(config_path.read_text(encoding="utf-8"))
66
+ except yaml.YAMLError as exc:
67
+ raise ConfigError(f"Invalid YAML in {config_path}: {exc}") from exc
68
+
69
+ if raw is None:
70
+ raise ConfigError(f"Workflow config is empty: {config_path}")
71
+ if not isinstance(raw, dict):
72
+ raise ConfigError(f"Workflow config must be a YAML mapping: {config_path}")
73
+
74
+ try:
75
+ return WorkflowConfig.model_validate(raw)
76
+ except ValidationError as exc:
77
+ details = "; ".join(
78
+ f"{'.'.join(str(part) for part in error['loc'])}: {error['msg']}"
79
+ for error in exc.errors()
80
+ )
81
+ raise ConfigError(f"Invalid workflow config in {config_path}: {details}") from exc