cli-router 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.
Files changed (33) hide show
  1. cli_router-0.1.0/.github/workflows/publish.yml +68 -0
  2. cli_router-0.1.0/.github/workflows/test.yml +24 -0
  3. cli_router-0.1.0/.gitignore +224 -0
  4. cli_router-0.1.0/AGENTS.md +204 -0
  5. cli_router-0.1.0/CHANGELOG.md +5 -0
  6. cli_router-0.1.0/LICENSE +201 -0
  7. cli_router-0.1.0/PKG-INFO +172 -0
  8. cli_router-0.1.0/PRD.md +424 -0
  9. cli_router-0.1.0/README.md +145 -0
  10. cli_router-0.1.0/cli_router/__init__.py +3 -0
  11. cli_router-0.1.0/cli_router/artifacts.py +50 -0
  12. cli_router-0.1.0/cli_router/cli.py +96 -0
  13. cli_router-0.1.0/cli_router/config.py +139 -0
  14. cli_router-0.1.0/cli_router/extractors.py +39 -0
  15. cli_router-0.1.0/cli_router/failures.py +52 -0
  16. cli_router-0.1.0/cli_router/presets/__init__.py +1 -0
  17. cli_router-0.1.0/cli_router/presets/claude.yaml +14 -0
  18. cli_router-0.1.0/cli_router/presets/codex.yaml +9 -0
  19. cli_router-0.1.0/cli_router/presets/generic.yaml +60 -0
  20. cli_router-0.1.0/cli_router/presets/hermes.yaml +9 -0
  21. cli_router-0.1.0/cli_router/runner.py +75 -0
  22. cli_router-0.1.0/cli_router/tools.py +39 -0
  23. cli_router-0.1.0/cli_router/workflows.py +176 -0
  24. cli_router-0.1.0/examples/claude_codex.yaml +73 -0
  25. cli_router-0.1.0/examples/cli-router.yaml +45 -0
  26. cli_router-0.1.0/examples/local_models.yaml +43 -0
  27. cli_router-0.1.0/pyproject.toml +52 -0
  28. cli_router-0.1.0/tests/test_cli.py +78 -0
  29. cli_router-0.1.0/tests/test_config.py +49 -0
  30. cli_router-0.1.0/tests/test_extractors.py +18 -0
  31. cli_router-0.1.0/tests/test_failures.py +38 -0
  32. cli_router-0.1.0/tests/test_runner.py +42 -0
  33. cli_router-0.1.0/tests/test_workflows.py +155 -0
@@ -0,0 +1,68 @@
1
+ name: Publish to PyPI
2
+
3
+ on:
4
+ release:
5
+ types: [published]
6
+ workflow_dispatch:
7
+
8
+ permissions:
9
+ contents: read
10
+
11
+ jobs:
12
+ test:
13
+ name: Test package
14
+ runs-on: ubuntu-latest
15
+ strategy:
16
+ matrix:
17
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
18
+ steps:
19
+ - uses: actions/checkout@v4
20
+ - uses: actions/setup-python@v5
21
+ with:
22
+ python-version: ${{ matrix.python-version }}
23
+ - name: Install test dependencies
24
+ run: python -m pip install --upgrade pip pytest
25
+ - name: Install package
26
+ run: python -m pip install -e .
27
+ - name: Run tests
28
+ run: python -m pytest tests -q
29
+
30
+ build:
31
+ name: Build distributions
32
+ runs-on: ubuntu-latest
33
+ needs: test
34
+ steps:
35
+ - uses: actions/checkout@v4
36
+ - uses: actions/setup-python@v5
37
+ with:
38
+ python-version: "3.12"
39
+ - name: Install build tools
40
+ run: python -m pip install --upgrade pip build twine
41
+ - name: Build package
42
+ run: python -m build
43
+ - name: Check distributions
44
+ run: python -m twine check dist/*
45
+ - name: Upload distributions
46
+ uses: actions/upload-artifact@v4
47
+ with:
48
+ name: dist
49
+ path: dist/
50
+ if-no-files-found: error
51
+
52
+ pypi-publish:
53
+ name: Publish CLI-Router to PyPI
54
+ runs-on: ubuntu-latest
55
+ needs: build
56
+ environment: pypi
57
+ permissions:
58
+ id-token: write
59
+ contents: read
60
+
61
+ steps:
62
+ - name: Download distributions
63
+ uses: actions/download-artifact@v4
64
+ with:
65
+ name: dist
66
+ path: dist/
67
+ - name: Publish package
68
+ uses: pypa/gh-action-pypi-publish@release/v1
@@ -0,0 +1,24 @@
1
+ name: Tests
2
+
3
+ on:
4
+ push:
5
+ pull_request:
6
+
7
+ jobs:
8
+ test:
9
+ runs-on: ubuntu-latest
10
+ strategy:
11
+ matrix:
12
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
13
+
14
+ steps:
15
+ - uses: actions/checkout@v4
16
+ - uses: actions/setup-python@v5
17
+ with:
18
+ python-version: ${{ matrix.python-version }}
19
+ - name: Install test dependencies
20
+ run: python -m pip install --upgrade pip pytest
21
+ - name: Install package
22
+ run: python -m pip install -e .
23
+ - name: Test
24
+ run: python -m pytest tests -q
@@ -0,0 +1,224 @@
1
+ # Byte-compiled / optimized / DLL files
2
+ __pycache__/
3
+ *.py[codz]
4
+ *$py.class
5
+
6
+ # C extensions
7
+ *.so
8
+
9
+ # Distribution / packaging
10
+ .Python
11
+ build/
12
+ develop-eggs/
13
+ dist/
14
+ downloads/
15
+ eggs/
16
+ .eggs/
17
+ lib/
18
+ lib64/
19
+ parts/
20
+ sdist/
21
+ var/
22
+ wheels/
23
+ share/python-wheels/
24
+ *.egg-info/
25
+ .installed.cfg
26
+ *.egg
27
+ MANIFEST
28
+
29
+ # PyInstaller
30
+ # Usually these files are written by a python script from a template
31
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
+ *.manifest
33
+ *.spec
34
+
35
+ # Installer logs
36
+ pip-log.txt
37
+ pip-delete-this-directory.txt
38
+
39
+ # Unit test / coverage reports
40
+ htmlcov/
41
+ .tox/
42
+ .nox/
43
+ .coverage
44
+ .coverage.*
45
+ .cache
46
+ nosetests.xml
47
+ coverage.xml
48
+ *.cover
49
+ *.py.cover
50
+ .hypothesis/
51
+ .pytest_cache/
52
+ cover/
53
+
54
+ # Translations
55
+ *.mo
56
+ *.pot
57
+
58
+ # Django stuff:
59
+ *.log
60
+ local_settings.py
61
+ db.sqlite3
62
+ db.sqlite3-journal
63
+
64
+ # Flask stuff:
65
+ instance/
66
+ .webassets-cache
67
+
68
+ # Scrapy stuff:
69
+ .scrapy
70
+
71
+ # Sphinx documentation
72
+ docs/_build/
73
+
74
+ # PyBuilder
75
+ .pybuilder/
76
+ target/
77
+
78
+ # Jupyter Notebook
79
+ .ipynb_checkpoints
80
+
81
+ # IPython
82
+ profile_default/
83
+ ipython_config.py
84
+
85
+ # pyenv
86
+ # For a library or package, you might want to ignore these files since the code is
87
+ # intended to run in multiple environments; otherwise, check them in:
88
+ # .python-version
89
+
90
+ # pipenv
91
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
+ # install all needed dependencies.
95
+ # Pipfile.lock
96
+
97
+ # UV
98
+ # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
100
+ # commonly ignored for libraries.
101
+ # uv.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ # poetry.lock
109
+ # poetry.toml
110
+
111
+ # pdm
112
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
+ # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
+ # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
+ # pdm.lock
116
+ # pdm.toml
117
+ .pdm-python
118
+ .pdm-build/
119
+
120
+ # pixi
121
+ # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
+ # pixi.lock
123
+ # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
+ # in the .venv directory. It is recommended not to include this directory in version control.
125
+ .pixi
126
+
127
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
+ __pypackages__/
129
+
130
+ # Celery stuff
131
+ celerybeat-schedule
132
+ celerybeat.pid
133
+
134
+ # Redis
135
+ *.rdb
136
+ *.aof
137
+ *.pid
138
+
139
+ # RabbitMQ
140
+ mnesia/
141
+ rabbitmq/
142
+ rabbitmq-data/
143
+
144
+ # ActiveMQ
145
+ activemq-data/
146
+
147
+ # SageMath parsed files
148
+ *.sage.py
149
+
150
+ # Environments
151
+ .env
152
+ .envrc
153
+ .venv
154
+ env/
155
+ venv/
156
+ ENV/
157
+ env.bak/
158
+ venv.bak/
159
+
160
+ # Spyder project settings
161
+ .spyderproject
162
+ .spyproject
163
+
164
+ # Rope project settings
165
+ .ropeproject
166
+
167
+ # mkdocs documentation
168
+ /site
169
+
170
+ # mypy
171
+ .mypy_cache/
172
+ .dmypy.json
173
+ dmypy.json
174
+
175
+ # Pyre type checker
176
+ .pyre/
177
+
178
+ # pytype static type analyzer
179
+ .pytype/
180
+
181
+ # Cython debug symbols
182
+ cython_debug/
183
+
184
+ # PyCharm
185
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
186
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
187
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
188
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
189
+ # .idea/
190
+
191
+ # Abstra
192
+ # Abstra is an AI-powered process automation framework.
193
+ # Ignore directories containing user credentials, local state, and settings.
194
+ # Learn more at https://abstra.io/docs
195
+ .abstra/
196
+
197
+ # Visual Studio Code
198
+ # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
199
+ # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
200
+ # and can be added to the global gitignore or merged into this file. However, if you prefer,
201
+ # you could uncomment the following to ignore the entire vscode folder
202
+ # .vscode/
203
+ # Temporary file for partial code execution
204
+ tempCodeRunnerFile.py
205
+
206
+ # Ruff stuff:
207
+ .ruff_cache/
208
+
209
+ # PyPI configuration file
210
+ .pypirc
211
+
212
+ # CLI-Router local config and run artifacts
213
+ /cli-router.yaml
214
+ /.cli-router.yaml
215
+ /PLAN.md
216
+ /.cli-router/
217
+
218
+ # Marimo
219
+ marimo/_static/
220
+ marimo/_lsp/
221
+ __marimo__/
222
+
223
+ # Streamlit
224
+ .streamlit/secrets.toml
@@ -0,0 +1,204 @@
1
+ # AGENTS.md
2
+
3
+ Guidance for AI agents working in this repository.
4
+
5
+ ## Project Purpose
6
+
7
+ CLI-Router is a Python command-line orchestrator for external AI coding CLIs. It is intentionally programmatic and non-intelligent:
8
+
9
+ - It does not inspect repositories on its own unless a configured external tool does so.
10
+ - It does not semantically choose models or providers.
11
+ - It loads YAML config, renders prompt templates, runs configured commands, captures stdout/stderr, extracts configured output, writes artifacts, classifies failures, and applies configured fallback behavior.
12
+
13
+ Keep that boundary intact. New behavior should make orchestration more reliable or configurable, not turn this package into an agent.
14
+
15
+ ## Source Layout
16
+
17
+ - `cli_router/cli.py`: argparse CLI entry point and command dispatch.
18
+ - `cli_router/config.py`: config discovery, merge, and validation.
19
+ - `cli_router/runner.py`: subprocess execution, placeholder rendering, command timeout handling.
20
+ - `cli_router/workflows.py`: `plan`, `implement`, and full `run` workflow execution.
21
+ - `cli_router/extractors.py`: text and JSON output extraction.
22
+ - `cli_router/failures.py`: external-tool failure classification and user-facing messages.
23
+ - `cli_router/artifacts.py`: run directory and artifact persistence.
24
+ - `cli_router/tools.py`: `tools list` and `tools test`.
25
+ - `cli_router/presets/`: packaged YAML defaults/presets.
26
+ - `examples/`: user-facing example configs.
27
+ - `tests/`: pytest coverage using fake subprocess CLIs, not real Claude/Codex calls.
28
+
29
+ ## Development Commands
30
+
31
+ Use these from the repository root:
32
+
33
+ ```bash
34
+ python -m pytest tests -q
35
+ python -m cli_router.cli --help
36
+ python -m cli_router.cli check
37
+ ```
38
+
39
+ Package verification:
40
+
41
+ ```bash
42
+ python -m build
43
+ python -m twine check dist/*
44
+ ```
45
+
46
+ Editable install may fail on system Python environments protected by PEP 668. Use a virtual environment:
47
+
48
+ ```bash
49
+ python -m venv /tmp/cli-router-venv
50
+ /tmp/cli-router-venv/bin/python -m pip install -e .
51
+ /tmp/cli-router-venv/bin/cli-router --help
52
+ ```
53
+
54
+ `python -m build` and package install may need network access to fetch build dependencies.
55
+
56
+ ## Configuration Semantics
57
+
58
+ Config lookup order is:
59
+
60
+ 1. `./cli-router.yaml`
61
+ 2. `./.cli-router.yaml`
62
+ 3. `~/.config/cli-router/config.yaml`
63
+ 4. Built-in defaults from `cli_router/presets/generic.yaml`
64
+
65
+ Config version is currently `version: 1`.
66
+
67
+ Supported placeholders are literal token replacements:
68
+
69
+ - `{prompt}`
70
+ - `{user_prompt}`
71
+ - `{plan_path}`
72
+
73
+ Do not switch to Python `str.format` semantics without a strong reason. Current literal replacement intentionally allows command snippets and prompts to contain unrelated braces.
74
+
75
+ Workflow stages use:
76
+
77
+ - `tool`: primary tool name.
78
+ - `fallback_tools`: optional ordered list of tool names to try after failed attempts.
79
+ - `input_template`: rendered prompt sent as `{prompt}` to the tool command.
80
+ - `output_file`: planner output path, usually `PLAN.md`.
81
+
82
+ Tool config supports:
83
+
84
+ - `command`: list-form command preferred; string-form is parsed with `shlex.split`.
85
+ - `output.format`: `text` or `json`.
86
+ - `output.extract`: top-level or dotted JSON field path.
87
+ - `timeout_seconds`: optional positive number; timeout returns exit code `124`.
88
+
89
+ ## Runtime Artifacts
90
+
91
+ Runs write to `.cli-router/runs/<timestamp>/`.
92
+
93
+ Primary stage attempt artifacts use the stage id:
94
+
95
+ - `planner.stdout`
96
+ - `planner.stderr`
97
+ - `planner.extracted.md`
98
+
99
+ Fallback attempts include the tool name:
100
+
101
+ - `planner.codex-planner.stdout`
102
+ - `planner.codex-planner.stderr`
103
+ - `planner.codex-planner.extracted.md`
104
+
105
+ Every run writes `run.yaml` containing stage summaries, commands, return codes, extracted output, and `failure_kind`.
106
+
107
+ `.cli-router/runs/` is ignored by git. Do not commit generated run artifacts.
108
+
109
+ ## Failure Behavior
110
+
111
+ Failure classification lives in `cli_router/failures.py`.
112
+
113
+ Current `failure_kind` values include:
114
+
115
+ - `usage_limit`
116
+ - `unsupported_model`
117
+ - `timeout`
118
+ - `command_not_found`
119
+ - `command_failed`
120
+ - `extraction_failed`
121
+
122
+ Known real-world messages are covered by tests, including:
123
+
124
+ - Claude: `You've hit your session limit ...`
125
+ - Claude/rate/quota variants containing usage limit, session limit, rate limit, quota, 429, or too many requests.
126
+ - Codex/OpenAI unsupported model messages such as `model is not supported`.
127
+
128
+ When adding a new failure classifier, add a focused test in `tests/test_failures.py` using the real observed stdout/stderr text when possible.
129
+
130
+ ## Testing Rules
131
+
132
+ Prefer tests that use temporary configs and fake commands such as:
133
+
134
+ ```python
135
+ [sys.executable, "-c", "import json; print(json.dumps({'result': 'plan'}))"]
136
+ ```
137
+
138
+ Do not require real Claude, Codex, Hermes, OpenAI, network access, or credentials in the normal test suite.
139
+
140
+ Use `tmp_path` and `monkeypatch.chdir(tmp_path)` for workflow tests that write `PLAN.md` or `.cli-router/runs/`.
141
+
142
+ When changing workflow behavior, cover:
143
+
144
+ - plan-only execution
145
+ - full planner-to-coder execution
146
+ - missing `PLAN.md` for `implement`
147
+ - nonzero command failures
148
+ - fallback ordering and artifact names
149
+ - failure classification and exit codes
150
+
151
+ When changing packaging, verify:
152
+
153
+ ```bash
154
+ python -m pytest tests -q
155
+ python -m build
156
+ python -m twine check dist/*
157
+ ```
158
+
159
+ ## Real External CLI Trials
160
+
161
+ Real Claude/Codex trials are useful but should be explicit and isolated.
162
+
163
+ Use a temporary directory such as `/tmp/cli-router-real-test`; do not run real provider trials in the repo root unless the user explicitly wants repo files changed.
164
+
165
+ Recommended safety controls:
166
+
167
+ - Add `timeout_seconds` to every real tool.
168
+ - For Codex trials, prefer `codex --ask-for-approval never exec --skip-git-repo-check --ephemeral --sandbox read-only ...`.
169
+ - Use harmless prompts and include "Do not edit files" in `input_template`.
170
+ - Expect provider/model availability to vary by account; classify and report unsupported model failures instead of treating them as code defects.
171
+
172
+ Known observed Codex behavior in this environment:
173
+
174
+ - Local Codex config default model was `gpt-5.5`.
175
+ - `gpt-5` and `gpt-5-codex` returned unsupported-model errors for the ChatGPT-backed account.
176
+ - `gpt-5.5` succeeded for both planner and coder in the real fallback trial.
177
+
178
+ These observations are environment-specific. Do not hardcode them into package defaults without a product decision.
179
+
180
+ ## Design Constraints
181
+
182
+ - Keep the CLI stable: `cli-router run`, `plan`, `implement`, `check`, `config show`, `tools list`, `tools test <name>`.
183
+ - Keep dependencies minimal. Current runtime dependencies are `pyyaml` and `rich`; the CLI uses `argparse`.
184
+ - Prefer list-form subprocess commands in examples and tests.
185
+ - Preserve stdout/stderr exactly in artifacts. Do not redact or transform raw logs unless a user explicitly requests a privacy feature.
186
+ - Avoid hidden behavior. If CLI-Router retries or falls back, make that visible through summaries and `run.yaml`.
187
+ - Do not make built-in presets depend on provider credentials or paid services for basic `cli-router check`.
188
+ - Do not mutate user configuration unless implementing an explicit config-editing command.
189
+
190
+ ## Git Hygiene
191
+
192
+ The worktree may contain user or generated changes. Do not revert files you did not intentionally change.
193
+
194
+ Generated files that should normally remain untracked:
195
+
196
+ - `.cli-router/runs/`
197
+ - `dist/`
198
+ - `build/`
199
+ - `*.egg-info/`
200
+ - `__pycache__/`
201
+ - `.pytest_cache/`
202
+
203
+ Before finalizing substantive changes, run `git status --short` and report any verification commands run.
204
+
@@ -0,0 +1,5 @@
1
+ # Changelog
2
+
3
+ ## 0.1.0
4
+
5
+ - Initial PyPI-ready CLI-Router MVP.